You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jh...@apache.org on 2018/08/31 06:04:25 UTC
[3/3] calcite git commit: [CALCITE-2470] In RelBuilder,
project method should combine expressions if the underlying node is a
Project
[CALCITE-2470] In RelBuilder, project method should combine expressions if the underlying node is a Project
Add RelBuilder.shouldMergeProject() to allow sub-classes to disable
merging.
Improve the message given by CompositeMatcher when match fails.
When RelStructuredTypeFlattener rewrites a RexInputRef be sure to use
the field's new type. (It might have strengthened from say INTEGER to
INTEGER NOT NULL.)
Add a test case inspired by Drill (it passes in Calcite, but I gather
it fails in Drill).
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/370e95ab
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/370e95ab
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/370e95ab
Branch: refs/heads/master
Commit: 370e95ab8557946023ce209e975e1c321765559e
Parents: d0e3089
Author: Julian Hyde <jh...@apache.org>
Authored: Thu Aug 16 01:26:13 2018 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Aug 30 23:03:22 2018 -0700
----------------------------------------------------------------------
.../calcite/rel/logical/LogicalFilter.java | 2 +
.../java/org/apache/calcite/runtime/Hook.java | 4 +-
.../sql2rel/RelStructuredTypeFlattener.java | 73 +-
.../org/apache/calcite/tools/RelBuilder.java | 166 +++--
.../calcite/test/JdbcFrontLinqBackTest.java | 4 +-
.../java/org/apache/calcite/test/Matchers.java | 5 +
.../calcite/test/MaterializationTest.java | 2 +-
.../org/apache/calcite/test/RelBuilderTest.java | 82 ++-
.../calcite/test/SqlToRelConverterTest.java | 4 +-
.../org/apache/calcite/test/StreamTest.java | 11 +-
.../org/apache/calcite/tools/PlannerTest.java | 7 +-
.../org/apache/calcite/test/HepPlannerTest.xml | 5 +-
.../org/apache/calcite/test/RelOptRulesTest.xml | 576 +++++++---------
.../calcite/test/SqlToRelConverterTest.xml | 664 ++++++++-----------
core/src/test/resources/sql/agg.iq | 6 +-
core/src/test/resources/sql/misc.iq | 19 +-
core/src/test/resources/sql/sub-query.iq | 44 +-
core/src/test/resources/sql/winagg.iq | 4 +-
.../adapter/mongodb/MongoAdapterTest.java | 10 +-
.../org/apache/calcite/test/ServerTest.java | 2 +-
server/src/test/resources/sql/table.iq | 2 +-
site/_docs/algebra.md | 1 +
22 files changed, 838 insertions(+), 855 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
index 3b5febf..fd98d66 100644
--- a/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
+++ b/core/src/main/java/org/apache/calcite/rel/logical/LogicalFilter.java
@@ -31,6 +31,7 @@ import org.apache.calcite.rel.metadata.RelMdCollation;
import org.apache.calcite.rel.metadata.RelMdDistribution;
import org.apache.calcite.rel.metadata.RelMetadataQuery;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.util.Litmus;
import com.google.common.collect.ImmutableSet;
@@ -66,6 +67,7 @@ public final class LogicalFilter extends Filter {
ImmutableSet<CorrelationId> variablesSet) {
super(cluster, traitSet, child, condition);
this.variablesSet = Objects.requireNonNull(variablesSet);
+ assert isValid(Litmus.THROW, null);
}
@Deprecated // to be removed before 2.0
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/main/java/org/apache/calcite/runtime/Hook.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/runtime/Hook.java b/core/src/main/java/org/apache/calcite/runtime/Hook.java
index 10d4586..a02bb0f 100644
--- a/core/src/main/java/org/apache/calcite/runtime/Hook.java
+++ b/core/src/main/java/org/apache/calcite/runtime/Hook.java
@@ -108,13 +108,13 @@ public enum Hook {
* }</pre>
* </blockquote>
*/
- public <T, R> Closeable add(final Consumer<T> handler) {
+ public <T> Closeable add(final Consumer<T> handler) {
//noinspection unchecked
handlers.add((Consumer<Object>) handler);
return () -> remove(handler);
}
- /** @deprecated Use {@link #addThread(Consumer)}. */
+ /** @deprecated Use {@link #add(Consumer)}. */
@SuppressWarnings("Guava")
@Deprecated // to be removed in 2.0
public <T, R> Closeable add(final Function<T, R> handler) {
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
index ff41b16..98a0aa8 100644
--- a/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
+++ b/core/src/main/java/org/apache/calcite/sql2rel/RelStructuredTypeFlattener.java
@@ -16,6 +16,7 @@
*/
package org.apache.calcite.sql2rel;
+import org.apache.calcite.linq4j.Ord;
import org.apache.calcite.plan.RelOptCluster;
import org.apache.calcite.plan.RelOptTable;
import org.apache.calcite.rel.RelCollation;
@@ -285,34 +286,50 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
/**
* Maps the ordinal of a field pre-flattening to the ordinal of the
- * corresponding field post-flattening, and optionally returns its type.
+ * corresponding field post-flattening.
*
* @param oldOrdinal Pre-flattening ordinal
* @return Post-flattening ordinal
*/
protected int getNewForOldInput(int oldOrdinal) {
+ return getNewFieldForOldInput(oldOrdinal).i;
+ }
+
+ /**
+ * Maps the ordinal of a field pre-flattening to the ordinal of the
+ * corresponding field post-flattening, and also returns its type.
+ *
+ * @param oldOrdinal Pre-flattening ordinal
+ * @return Post-flattening ordinal and type
+ */
+ protected Ord<RelDataType> getNewFieldForOldInput(int oldOrdinal) {
assert currentRel != null;
int newOrdinal = 0;
// determine which input rel oldOrdinal references, and adjust
// oldOrdinal to be relative to that input rel
RelNode oldInput = null;
+ RelNode newInput = null;
for (RelNode oldInput1 : currentRel.getInputs()) {
+ newInput = getNewForOldRel(oldInput1);
RelDataType oldInputType = oldInput1.getRowType();
int n = oldInputType.getFieldCount();
if (oldOrdinal < n) {
oldInput = oldInput1;
break;
}
- RelNode newInput = getNewForOldRel(oldInput1);
newOrdinal += newInput.getRowType().getFieldCount();
oldOrdinal -= n;
}
assert oldInput != null;
+ assert newInput != null;
RelDataType oldInputType = oldInput.getRowType();
- newOrdinal += calculateFlattenedOffset(oldInputType, oldOrdinal);
- return newOrdinal;
+ final int newOffset = calculateFlattenedOffset(oldInputType, oldOrdinal);
+ newOrdinal += newOffset;
+ final RelDataTypeField field =
+ newInput.getRowType().getFieldList().get(newOffset);
+ return Ord.of(newOrdinal, field.getType());
}
/**
@@ -523,10 +540,9 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
// Translate the condition.
final RexLocalRef conditionRef = program.getCondition();
if (conditionRef != null) {
- programBuilder.addCondition(
- new RexLocalRef(
- getNewForOldInput(conditionRef.getIndex()),
- conditionRef.getType()));
+ final Ord<RelDataType> newField =
+ getNewFieldForOldInput(conditionRef.getIndex());
+ programBuilder.addCondition(new RexLocalRef(newField.i, newField.e));
}
RexProgram newProgram = programBuilder.getProgram();
@@ -576,7 +592,6 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
if (exp.getType().isStruct()) {
if (exp instanceof RexInputRef) {
RexInputRef inputRef = (RexInputRef) exp;
- int newOffset = getNewForOldInput(inputRef.getIndex());
// expand to range
RelDataType flattenedType =
@@ -587,10 +602,10 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
List<RelDataTypeField> fieldList = flattenedType.getFieldList();
int n = fieldList.size();
for (int j = 0; j < n; ++j) {
- RelDataTypeField field = fieldList.get(j);
+ final Ord<RelDataType> newField =
+ getNewFieldForOldInput(inputRef.getIndex());
flattenedExps.add(
- Pair.of(
- new RexInputRef(newOffset + j, field.getType()),
+ Pair.of(new RexInputRef(newField.i + j, newField.e),
fieldName));
}
} else if (isConstructor(exp) || exp.isA(SqlKind.CAST)) {
@@ -627,11 +642,13 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
RexNode newExp = exp;
List<RexNode> oldOperands = ((RexCall) exp).getOperands();
if (oldOperands.get(0) instanceof RexInputRef) {
- RexInputRef inputRef = (RexInputRef) oldOperands.get(0);
- int newOffset = getNewForOldInput(inputRef.getIndex());
- newExp = rexBuilder.makeCall(exp.getType(), ((RexCall) exp).getOperator(),
- ImmutableList.of(
- rexBuilder.makeInputRef(inputRef.getType(), newOffset), oldOperands.get(1)));
+ final RexInputRef inputRef = (RexInputRef) oldOperands.get(0);
+ final Ord<RelDataType> newField =
+ getNewFieldForOldInput(inputRef.getIndex());
+ newExp = rexBuilder.makeCall(exp.getType(),
+ ((RexCall) exp).getOperator(),
+ ImmutableList.of(rexBuilder.makeInputRef(newField.e, newField.i),
+ oldOperands.get(1)));
}
for (RelDataTypeField field : newExp.getType().getFieldList()) {
flattenedExps.add(
@@ -759,15 +776,12 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
private class RewriteRexShuttle extends RexShuttle {
@Override public RexNode visitInputRef(RexInputRef input) {
final int oldIndex = input.getIndex();
- final int newIndex = getNewForOldInput(oldIndex);
-
- // FIXME: jhyde, 2005/12/3: Once indicator fields have been
- // introduced, the new field type may be very different to the
- // old field type. We should look at the actual flattened types,
- // rather than trying to deduce the type from the current type.
- RelDataType fieldType = removeDistinct(input.getType());
- RexInputRef newInput = new RexInputRef(newIndex, fieldType);
- return newInput;
+ final Ord<RelDataType> field = getNewFieldForOldInput(oldIndex);
+
+ // Use the actual flattened type, which may be different from the current
+ // type.
+ RelDataType fieldType = removeDistinct(field.e);
+ return new RexInputRef(field.i, fieldType);
}
private RelDataType removeDistinct(RelDataType type) {
@@ -781,7 +795,6 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
// walk down the field access path expression, calculating
// the desired input number
int iInput = 0;
- RelDataType fieldType = removeDistinct(fieldAccess.getType());
for (;;) {
RexNode refExp = fieldAccess.getReferenceExpr();
@@ -792,8 +805,10 @@ public class RelStructuredTypeFlattener implements ReflectiveVisitor {
ordinal);
if (refExp instanceof RexInputRef) {
RexInputRef inputRef = (RexInputRef) refExp;
- iInput += getNewForOldInput(inputRef.getIndex());
- return new RexInputRef(iInput, fieldType);
+ final Ord<RelDataType> newField =
+ getNewFieldForOldInput(inputRef.getIndex());
+ iInput += newField.i;
+ return new RexInputRef(iInput, removeDistinct(newField.e));
} else if (refExp instanceof RexCorrelVariable) {
RelDataType refType =
SqlTypeUtil.flattenRecordType(
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
----------------------------------------------------------------------
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 491be00..64ae6cb 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -17,6 +17,7 @@
package org.apache.calcite.tools;
import org.apache.calcite.linq4j.Ord;
+import org.apache.calcite.linq4j.function.Experimental;
import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.Contexts;
import org.apache.calcite.plan.RelOptCluster;
@@ -95,7 +96,6 @@ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
@@ -941,6 +941,12 @@ public class RelBuilder {
return this;
}
+ /** Creates a {@link Project} of the given
+ * expressions. */
+ public RelBuilder project(RexNode... nodes) {
+ return project(ImmutableList.copyOf(nodes));
+ }
+
/** Creates a {@link Project} of the given list
* of expressions.
*
@@ -964,6 +970,19 @@ public class RelBuilder {
return project(nodes, fieldNames, false);
}
+ /** Creates a {@link Project} of all original fields, plus the given
+ * expressions. */
+ public RelBuilder projectPlus(RexNode... nodes) {
+ return projectPlus(ImmutableList.copyOf(nodes));
+ }
+
+ /** Creates a {@link Project} of all original fields, plus the given list of
+ * expressions. */
+ public RelBuilder projectPlus(Iterable<RexNode> nodes) {
+ final ImmutableList.Builder<RexNode> builder = ImmutableList.builder();
+ return project(builder.addAll(fields()).addAll(nodes).build());
+ }
+
/** Creates a {@link Project} of the given list
* of expressions, using the given names.
*
@@ -991,27 +1010,86 @@ public class RelBuilder {
Iterable<? extends RexNode> nodes,
Iterable<String> fieldNames,
boolean force) {
- final List<String> names = new ArrayList<>();
- final List<RexNode> exprList = new ArrayList<>();
- final Iterator<String> nameIterator = fieldNames.iterator();
- for (RexNode node : nodes) {
- if (simplify) {
- node = simplifier.simplifyPreservingType(node);
+ final Frame frame = stack.peek();
+ final RelDataType inputRowType = frame.rel.getRowType();
+ final List<RexNode> nodeList = Lists.newArrayList(nodes);
+
+ // Perform a quick check for identity. We'll do a deeper check
+ // later when we've derived column names.
+ if (!force && Iterables.isEmpty(fieldNames)
+ && RexUtil.isIdentity(nodeList, inputRowType)) {
+ return this;
+ }
+
+ final List<String> fieldNameList = Lists.newArrayList(fieldNames);
+ while (fieldNameList.size() < nodeList.size()) {
+ fieldNameList.add(null);
+ }
+
+ if (frame.rel instanceof Project
+ && shouldMergeProject()) {
+ final Project project = (Project) frame.rel;
+ // Populate field names. If the upper expression is an input ref and does
+ // not have a recommended name, use the name of the underlying field.
+ for (int i = 0; i < fieldNameList.size(); i++) {
+ if (fieldNameList.get(i) == null) {
+ final RexNode node = nodeList.get(i);
+ if (node instanceof RexInputRef) {
+ final RexInputRef ref = (RexInputRef) node;
+ fieldNameList.set(i,
+ project.getRowType().getFieldNames().get(ref.getIndex()));
+ }
+ }
+ }
+ final List<RexNode> newNodes =
+ RelOptUtil.pushPastProject(nodeList, project);
+
+ // Carefully build a list of fields, so that table aliases from the input
+ // can be seen for fields that are based on a RexInputRef.
+ final Frame frame1 = stack.pop();
+ final List<Field> fields = new ArrayList<>();
+ for (RelDataTypeField f
+ : project.getInput().getRowType().getFieldList()) {
+ fields.add(new Field(ImmutableSet.of(), f));
+ }
+ for (Pair<RexNode, Field> pair
+ : Pair.zip(project.getProjects(), frame1.fields)) {
+ switch (pair.left.getKind()) {
+ case INPUT_REF:
+ final int i = ((RexInputRef) pair.left).getIndex();
+ final Field field = fields.get(i);
+ final ImmutableSet<String> aliases = pair.right.left;
+ fields.set(i, new Field(aliases, field.right));
+ break;
+ }
}
- exprList.add(node);
- String name = nameIterator.hasNext() ? nameIterator.next() : null;
- names.add(name != null ? name : inferAlias(exprList, node));
+ stack.push(new Frame(project.getInput(), ImmutableList.copyOf(fields)));
+ return project(newNodes, fieldNameList, force);
}
- final Frame frame = stack.peek();
+
+ // Simplify expressions.
+ if (simplify) {
+ for (int i = 0; i < nodeList.size(); i++) {
+ nodeList.set(i, simplifier.simplifyPreservingType(nodeList.get(i)));
+ }
+ }
+
+ // Replace null names with generated aliases.
+ for (int i = 0; i < fieldNameList.size(); i++) {
+ if (fieldNameList.get(i) == null) {
+ fieldNameList.set(i, inferAlias(nodeList, nodeList.get(i), i));
+ }
+ }
+
final ImmutableList.Builder<Field> fields = ImmutableList.builder();
final Set<String> uniqueNameList =
getTypeFactory().getTypeSystem().isSchemaCaseSensitive()
- ? new HashSet<String>()
+ ? new HashSet<>()
: new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
// calculate final names and build field list
- for (int i = 0; i < names.size(); ++i) {
- RexNode node = exprList.get(i);
- String name = names.get(i);
+ for (int i = 0; i < fieldNameList.size(); ++i) {
+ final RexNode node = nodeList.get(i);
+ String name = fieldNameList.get(i);
Field field;
if (name == null || uniqueNameList.contains(name)) {
int j = 0;
@@ -1021,7 +1099,7 @@ public class RelBuilder {
do {
name = SqlValidatorUtil.F_SUGGESTER.apply(name, j, j++);
} while (uniqueNameList.contains(name));
- names.set(i, name);
+ fieldNameList.set(i, name);
}
RelDataTypeField fieldType =
new RelDataTypeFieldImpl(name, i, node.getType());
@@ -1038,9 +1116,8 @@ public class RelBuilder {
uniqueNameList.add(name);
fields.add(field);
}
- final RelDataType inputRowType = peek().getRowType();
- if (!force && RexUtil.isIdentity(exprList, inputRowType)) {
- if (names.equals(inputRowType.getFieldNames())) {
+ if (!force && RexUtil.isIdentity(nodeList, inputRowType)) {
+ if (fieldNameList.equals(inputRowType.getFieldNames())) {
// Do not create an identity project if it does not rename any fields
return this;
} else {
@@ -1051,17 +1128,20 @@ public class RelBuilder {
}
}
final RelNode project =
- projectFactory.createProject(frame.rel, ImmutableList.copyOf(exprList),
- names);
+ projectFactory.createProject(frame.rel, ImmutableList.copyOf(nodeList),
+ fieldNameList);
stack.pop();
stack.push(new Frame(project, fields.build()));
return this;
}
- /** Creates a {@link Project} of the given
- * expressions. */
- public RelBuilder project(RexNode... nodes) {
- return project(ImmutableList.copyOf(nodes));
+ /** Whether to attempt to merge consecutive {@link Project} operators.
+ *
+ * <p>The default implementation returns {@code true};
+ * sub-classes may disable merge by overriding to return {@code false}. */
+ @Experimental
+ protected boolean shouldMergeProject() {
+ return true;
}
/** Creates a {@link Project} of the given
@@ -1146,18 +1226,7 @@ public class RelBuilder {
return values(v.tuples, b.build());
}
- project(fields(), newFieldNames, true);
-
- // If, after de-duplication, the field names are unchanged, discard the
- // identity project we just created.
- if (peek().getRowType().getFieldNames().equals(oldFieldNames)) {
- final RelNode r = peek();
- if (r instanceof Project) {
- stack.pop();
- push(((Project) r).getInput());
- }
- }
- return this;
+ return project(fields(), newFieldNames, true);
}
/** Infers the alias of an expression.
@@ -1165,20 +1234,16 @@ public class RelBuilder {
* <p>If the expression was created by {@link #alias}, replaces the expression
* in the project list.
*/
- private String inferAlias(List<RexNode> exprList, RexNode expr) {
+ private String inferAlias(List<RexNode> exprList, RexNode expr, int i) {
switch (expr.getKind()) {
case INPUT_REF:
final RexInputRef ref = (RexInputRef) expr;
return stack.peek().fields.get(ref.getIndex()).getValue().getName();
case CAST:
- return inferAlias(exprList, ((RexCall) expr).getOperands().get(0));
+ return inferAlias(exprList, ((RexCall) expr).getOperands().get(0), -1);
case AS:
final RexCall call = (RexCall) expr;
- for (;;) {
- final int i = exprList.indexOf(expr);
- if (i < 0) {
- break;
- }
+ if (i >= 0) {
exprList.set(i, call.getOperands().get(0));
}
return ((NlsString) ((RexLiteral) call.getOperands().get(1)).getValue())
@@ -1526,9 +1591,7 @@ public class RelBuilder {
public RelBuilder as(final String alias) {
final Frame pair = stack.pop();
List<Field> newFields =
- Lists.transform(pair.fields, field ->
- new Field(ImmutableSet.<String>builder().addAll(field.left)
- .add(alias).build(), field.right));
+ Util.transform(pair.fields, field -> field.addAlias(alias));
stack.push(new Frame(pair.rel, ImmutableList.copyOf(newFields)));
return this;
}
@@ -2090,6 +2153,15 @@ public class RelBuilder {
Field(ImmutableSet<String> left, RelDataTypeField right) {
super(left, right);
}
+
+ Field addAlias(String alias) {
+ if (left.contains(alias)) {
+ return this;
+ }
+ final ImmutableSet<String> aliasList =
+ ImmutableSet.<String>builder().addAll(left).add(alias).build();
+ return new Field(aliasList, right);
+ }
}
/** Shuttle that shifts a predicate's inputs to the left, replacing early
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java b/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
index a8c86f8..2c5d603 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcFrontLinqBackTest.java
@@ -104,8 +104,8 @@ public class JdbcFrontLinqBackTest {
+ "from \"hr\".\"emps\" as e\n"
+ "order by \"deptno\", \"name\" desc")
.explainContains(""
- + "EnumerableCalc(expr#0..4=[{inputs}], expr#5=[UPPER($t2)], UN=[$t5], deptno=[$t1], name=[$t2])\n"
- + " EnumerableSort(sort0=[$1], sort1=[$2], dir0=[ASC], dir1=[DESC])\n"
+ + "EnumerableSort(sort0=[$1], sort1=[$2], dir0=[ASC], dir1=[DESC])\n"
+ + " EnumerableCalc(expr#0..4=[{inputs}], expr#5=[UPPER($t2)], UN=[$t5], deptno=[$t1], name=[$t2])\n"
+ " EnumerableTableScan(table=[[hr, emps]])")
.returns("UN=THEODORE; deptno=10\n"
+ "UN=SEBASTIAN; deptno=10\n"
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/test/Matchers.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/Matchers.java b/core/src/test/java/org/apache/calcite/test/Matchers.java
index 0b5e6bd..98bb820 100644
--- a/core/src/test/java/org/apache/calcite/test/Matchers.java
+++ b/core/src/test/java/org/apache/calcite/test/Matchers.java
@@ -255,6 +255,11 @@ public class Matchers {
public void describeTo(Description description) {
matcher.describeTo(description);
}
+
+ @Override protected void describeMismatchSafely(F item,
+ Description mismatchDescription) {
+ mismatchDescription.appendText("was ").appendValue(f.apply(item));
+ }
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
index b32ec74..b8a929d 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -1743,7 +1743,7 @@ public class MaterializationTest {
+ "join \"dependents\" on (\"depts\".\"name\" = \"dependents\".\"name\")",
HR_FKUK_MODEL,
CalciteAssert.checkResultContains(
- "EnumerableCalc(expr#0..2=[{inputs}], empid0=[$t1])\n"
+ "EnumerableCalc(expr#0..2=[{inputs}], empid=[$t1])\n"
+ " EnumerableJoin(condition=[=($0, $2)], joinType=[inner])\n"
+ " EnumerableCalc(expr#0=[{inputs}], expr#1=[CAST($t0):VARCHAR CHARACTER SET \"ISO-8859-1\" "
+ "COLLATE \"ISO-8859-1$en_US$primary\"], name=[$t1])\n"
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
----------------------------------------------------------------------
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 b6ae845..5c18173 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -48,6 +48,7 @@ import org.apache.calcite.tools.RelRunner;
import org.apache.calcite.tools.RelRunners;
import org.apache.calcite.util.Holder;
import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Util;
import org.apache.calcite.util.mapping.Mappings;
import com.google.common.collect.ImmutableList;
@@ -808,9 +809,8 @@ public class RelBuilderTest {
.build();
final String expected = ""
+ "LogicalAggregate(group=[{0}])\n"
- + " LogicalProject(departmentNo=[$0])\n"
- + " LogicalProject(DEPTNO=[$7])\n"
- + " LogicalTableScan(table=[[scott, EMP]])\n";
+ + " LogicalProject(departmentNo=[$7])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
assertThat(root, hasTree(expected));
}
@@ -828,10 +828,8 @@ public class RelBuilderTest {
.build();
final String expected = ""
+ "LogicalAggregate(group=[{1}])\n"
- + " LogicalProject(DEPTNO=[$0], d3=[$1])\n"
- + " LogicalProject(DEPTNO=[$0], $f1=[+($0, 3)])\n"
- + " LogicalProject(DEPTNO=[$7])\n"
- + " LogicalTableScan(table=[[scott, EMP]])\n";
+ + " LogicalProject(DEPTNO=[$7], d3=[+($7, 3)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
assertThat(root, hasTree(expected));
}
@@ -1397,8 +1395,53 @@ public class RelBuilderTest {
.project(builder.field("EMP_alias", "DEPTNO"))
.build();
final String expected = ""
- + "LogicalProject(DEPTNO=[$0])\n"
- + " LogicalProject(DEPTNO=[$7], $f1=[20])\n"
+ + "LogicalProject(DEPTNO=[$7])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(root, hasTree(expected));
+ }
+
+ /** Tests that table aliases are propagated even when there is a project on
+ * top of a project. (Aliases tend to get lost when projects are merged). */
+ @Test public void testAliasProjectProject() {
+ final RelBuilder builder = RelBuilder.create(config().build());
+ RelNode root =
+ builder.scan("EMP")
+ .as("EMP_alias")
+ .project(builder.field("DEPTNO"),
+ builder.literal(20))
+ .project(builder.field(1),
+ builder.literal(10),
+ builder.field(0))
+ .project(builder.alias(builder.field(1), "sum"),
+ builder.field("EMP_alias", "DEPTNO"))
+ .build();
+ final String expected = ""
+ + "LogicalProject(sum=[10], DEPTNO=[$7])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(root, hasTree(expected));
+ }
+
+ /** Tests that table aliases are propagated and are available to a filter,
+ * even when there is a project on top of a project. (Aliases tend to get lost
+ * when projects are merged). */
+ @Test public void testAliasFilter() {
+ final RelBuilder builder = RelBuilder.create(config().build());
+ RelNode root =
+ builder.scan("EMP")
+ .as("EMP_alias")
+ .project(builder.field("DEPTNO"),
+ builder.literal(20))
+ .project(builder.field(1), // literal 20
+ builder.literal(10),
+ builder.field(0)) // DEPTNO
+ .filter(
+ builder.call(SqlStdOperatorTable.GREATER_THAN,
+ builder.field(1),
+ builder.field("EMP_alias", "DEPTNO")))
+ .build();
+ final String expected = ""
+ + "LogicalFilter(condition=[>($1, $2)])\n"
+ + " LogicalProject($f1=[20], $f12=[10], DEPTNO=[$7])\n"
+ " LogicalTableScan(table=[[scott, EMP]])\n";
assertThat(root, hasTree(expected));
}
@@ -1449,6 +1492,27 @@ public class RelBuilderTest {
assertThat(root, hasTree(expected));
}
+ /** Tests that a projection after a projection. */
+ @Test public void testProjectProject() {
+ final RelBuilder builder = RelBuilder.create(config().build());
+ RelNode root =
+ builder.scan("EMP")
+ .as("e")
+ .projectPlus(
+ builder.alias(
+ builder.call(SqlStdOperatorTable.PLUS, builder.field(0),
+ builder.field(3)), "x"))
+ .project(builder.field("e", "DEPTNO"),
+ builder.field(0),
+ builder.field("e", "MGR"),
+ Util.last(builder.fields()))
+ .build();
+ final String expected = ""
+ + "LogicalProject(DEPTNO=[$7], EMPNO=[$0], MGR=[$3], x=[+($0, $3)])\n"
+ + " LogicalTableScan(table=[[scott, EMP]])\n";
+ assertThat(root, hasTree(expected));
+ }
+
@Test public void testMultiLevelAlias() {
final RelBuilder builder = RelBuilder.create(config().build());
RelNode root =
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
index aba2274..cafe24a 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlToRelConverterTest.java
@@ -2507,7 +2507,7 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
}
@Test public void testStarDynamicSchemaUnnest() {
- final String sql3 = "select * \n"
+ final String sql3 = "select *\n"
+ "from SALES.CUSTOMER as t1,\n"
+ "lateral (select t2.\"$unnest\" as fake_col3\n"
+ " from unnest(t1.fake_col) as t2) as t3";
@@ -2515,7 +2515,7 @@ public class SqlToRelConverterTest extends SqlToRelTestBase {
}
@Test public void testStarDynamicSchemaUnnest2() {
- final String sql3 = "select * \n"
+ final String sql3 = "select *\n"
+ "from SALES.CUSTOMER as t1,\n"
+ "unnest(t1.fake_col) as t2";
sql(sql3).with(getTesterWithDynamicTable()).ok();
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/test/StreamTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/StreamTest.java b/core/src/test/java/org/apache/calcite/test/StreamTest.java
index b25da6d..502fd61 100644
--- a/core/src/test/java/org/apache/calcite/test/StreamTest.java
+++ b/core/src/test/java/org/apache/calcite/test/StreamTest.java
@@ -277,12 +277,11 @@ public class StreamTest {
+ "orders.rowtime as rowtime, orders.id as orderId, products.supplier as supplierId "
+ "from orders join products on orders.product = products.id")
.convertContains("LogicalDelta\n"
- + " LogicalProject(ROWTIME=[$0], ORDERID=[$1], SUPPLIERID=[$5])\n"
- + " LogicalProject(ROWTIME=[$0], ID=[$1], PRODUCT=[$2], UNITS=[$3], ID0=[$5], SUPPLIER=[$6])\n"
- + " LogicalJoin(condition=[=($4, $5)], joinType=[inner])\n"
- + " LogicalProject(ROWTIME=[$0], ID=[$1], PRODUCT=[$2], UNITS=[$3], PRODUCT0=[CAST($2):VARCHAR(32) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL])\n"
- + " LogicalTableScan(table=[[STREAM_JOINS, ORDERS]])\n"
- + " LogicalTableScan(table=[[STREAM_JOINS, PRODUCTS]])\n")
+ + " LogicalProject(ROWTIME=[$0], ORDERID=[$1], SUPPLIERID=[$6])\n"
+ + " LogicalJoin(condition=[=($4, $5)], joinType=[inner])\n"
+ + " LogicalProject(ROWTIME=[$0], ID=[$1], PRODUCT=[$2], UNITS=[$3], PRODUCT0=[CAST($2):VARCHAR(32) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL])\n"
+ + " LogicalTableScan(table=[[STREAM_JOINS, ORDERS]])\n"
+ + " LogicalTableScan(table=[[STREAM_JOINS, PRODUCTS]])\n")
.explainContains(""
+ "EnumerableCalc(expr#0..6=[{inputs}], proj#0..1=[{exprs}], SUPPLIERID=[$t6])\n"
+ " EnumerableJoin(condition=[=($4, $5)], joinType=[inner])\n"
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
index da54ee8..04c3746 100644
--- a/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
+++ b/core/src/test/java/org/apache/calcite/tools/PlannerTest.java
@@ -1117,10 +1117,9 @@ public class PlannerTest {
assertThat(plan,
equalTo("LogicalSort(sort0=[$0], dir0=[ASC])\n"
+ " LogicalProject(psPartkey=[$0])\n"
- + " LogicalProject(psPartkey=[$0])\n"
- + " LogicalSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC])\n"
- + " LogicalProject(psPartkey=[$0], psSupplyCost=[$1])\n"
- + " EnumerableTableScan(table=[[tpch, partsupp]])\n"));
+ + " LogicalSort(sort0=[$0], sort1=[$1], dir0=[ASC], dir1=[ASC])\n"
+ + " LogicalProject(psPartkey=[$0], psSupplyCost=[$1])\n"
+ + " EnumerableTableScan(table=[[tpch, partsupp]])\n"));
}
/** Test case for
http://git-wip-us.apache.org/repos/asf/calcite/blob/370e95ab/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml
----------------------------------------------------------------------
diff --git a/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml b/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml
index 217eb36..1e56768 100644
--- a/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml
+++ b/core/src/test/resources/org/apache/calcite/test/HepPlannerTest.xml
@@ -167,9 +167,8 @@ LogicalIntersect(all=[false])
</Resource>
<Resource name="planBefore">
<![CDATA[
-LogicalProject(EXPR$0=[UPPER($0)])
- LogicalProject(ENAME=[LOWER($1)])
- LogicalTableScan(table=[[CATALOG, SALES, EMP]])
+LogicalProject(EXPR$0=[UPPER(LOWER($1))])
+ LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
<Resource name="planAfter">