This is an automated email from the ASF dual-hosted git repository. jhyde pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/calcite.git
commit aabcb2e5d1df0201fc8b5f3d99f9317bde6d49ae Author: Julian Hyde <[email protected]> AuthorDate: Mon Apr 29 17:12:49 2019 -0700 [CALCITE-3096] In RelBuilder, make alias method idempotent --- .../java/org/apache/calcite/tools/RelBuilder.java | 18 ++++++++++- .../org/apache/calcite/test/RelBuilderTest.java | 36 +++++++++++++++++++++- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index f73646a..a33b807 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -653,10 +653,26 @@ public class RelBuilder { /** * Returns an expression wrapped in an alias. * + * <p>This method is idempotent: If the expression is already wrapped in the + * correct alias, does nothing; if wrapped in an incorrect alias, removes + * the incorrect alias and applies the correct alias. + * * @see #project */ public RexNode alias(RexNode expr, String alias) { - return call(SqlStdOperatorTable.AS, expr, literal(alias)); + final RexNode aliasLiteral = literal(alias); + switch (expr.getKind()) { + case AS: + final RexCall call = (RexCall) expr; + if (call.operands.get(1).equals(aliasLiteral)) { + // current alias is correct + return expr; + } + expr = call.operands.get(0); + // strip current (incorrect) alias, and fall through + default: + return call(SqlStdOperatorTable.AS, expr, aliasLiteral); + } } /** Converts a sort expression to descending. */ diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java index dc0240f..a9f7341 100644 --- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java @@ -112,7 +112,6 @@ import static org.junit.Assert.fail; * {@link RelBuilder#alias(RexNode, String)} is removed if not a top-level * project</li> * <li>{@link RelBuilder#aggregate} with grouping sets</li> - * <li>{@link RelBuilder#aggregateCall} with filter</li> * <li>Add call to create {@link TableFunctionScan}</li> * <li>Add call to create {@link Window}</li> * <li>Add call to create {@link TableModify}</li> @@ -1677,6 +1676,41 @@ public class RelBuilderTest { assertThat(root, hasTree(expected)); } + /** Tests that the {@link RelBuilder#alias(RexNode, String)} function is + * idempotent. */ + @Test public void testScanAlias() { + final RelBuilder builder = RelBuilder.create(config().build()); + builder.scan("EMP"); + + // Simplify "emp.deptno as d as d" to "emp.deptno as d". + final RexNode e0 = + builder.alias(builder.alias(builder.field("DEPTNO"), "D"), "D"); + assertThat(e0.toString(), is("AS($7, 'D')")); + + // It would be nice if RelBuilder could simplify + // "emp.deptno as deptno" to "emp.deptno", but there is not + // enough information in RexInputRef. + final RexNode e1 = builder.alias(builder.field("DEPTNO"), "DEPTNO"); + assertThat(e1.toString(), is("AS($7, 'DEPTNO')")); + + // The intervening alias 'DEPTNO' is removed + final RexNode e2 = + builder.alias(builder.alias(builder.field("DEPTNO"), "DEPTNO"), "D1"); + assertThat(e2.toString(), is("AS($7, 'D1')")); + + // Simplify "emp.deptno as d2 as d3" to "emp.deptno as d3" + // because "d3" alias overrides "d2". + final RexNode e3 = + builder.alias(builder.alias(builder.field("DEPTNO"), "D2"), "D3"); + assertThat(e3.toString(), is("AS($7, 'D3')")); + + final RelNode root = builder.project(e0, e1, e2, e3).build(); + final String expected = "" + + "LogicalProject(D=[$7], DEPTNO=[$7], D1=[$7], D3=[$7])\n" + + " LogicalTableScan(table=[[scott, EMP]])\n"; + assertThat(root, hasTree(expected)); + } + /** * Tests that project field name aliases are suggested incrementally. */
