You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by jc...@apache.org on 2017/04/26 19:18:39 UTC
[05/11] calcite git commit: [CALCITE-1682] New metadata providers for
expression column origin and all predicates in plan
[CALCITE-1682] New metadata providers for expression column origin and all predicates in plan
Includes:
* RelNode type metadata provider
* Ranges containment-based simplification in conjunctive predicates
Project: http://git-wip-us.apache.org/repos/asf/calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/41b05d78
Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/41b05d78
Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/41b05d78
Branch: refs/heads/master
Commit: 41b05d784fd2e0ae81b09d013ef8a746036ca446
Parents: 478de56
Author: Jesus Camacho Rodriguez <jc...@apache.org>
Authored: Fri Mar 10 12:53:51 2017 +0000
Committer: Jesus Camacho Rodriguez <jc...@apache.org>
Committed: Wed Apr 26 19:56:37 2017 +0100
----------------------------------------------------------------------
.../org/apache/calcite/plan/RelOptCluster.java | 1 +
.../org/apache/calcite/plan/RelOptUtil.java | 26 +-
.../calcite/rel/metadata/BuiltInMetadata.java | 57 +-
.../metadata/DefaultRelMetadataProvider.java | 3 +
.../rel/metadata/RelMdAllPredicates.java | 249 +++++++
.../rel/metadata/RelMdExpressionLineage.java | 450 +++++++++++++
.../calcite/rel/metadata/RelMdNodeTypes.java | 154 +++++
.../apache/calcite/rel/metadata/RelMdUtil.java | 1 +
.../calcite/rel/metadata/RelMetadataQuery.java | 60 ++
.../calcite/rel/metadata/RelTableRef.java | 62 ++
.../org/apache/calcite/rex/LogicVisitor.java | 4 +
.../org/apache/calcite/rex/RexBiVisitor.java | 2 +
.../java/org/apache/calcite/rex/RexShuttle.java | 4 +
.../org/apache/calcite/rex/RexSimplify.java | 274 +++++++-
.../apache/calcite/rex/RexTableInputRef.java | 82 +++
.../java/org/apache/calcite/rex/RexUtil.java | 82 +++
.../java/org/apache/calcite/rex/RexVisitor.java | 2 +
.../org/apache/calcite/rex/RexVisitorImpl.java | 4 +
.../java/org/apache/calcite/sql/SqlKind.java | 7 +
.../org/apache/calcite/util/BuiltInMethod.java | 6 +
.../apache/calcite/test/RelMetadataTest.java | 647 +++++++++++++++++++
.../org/apache/calcite/test/RelOptRulesTest.xml | 2 +-
22 files changed, 2151 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
index f88e232..034d978 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptCluster.java
@@ -37,6 +37,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* optimization of a query.
*/
public class RelOptCluster {
+
//~ Instance fields --------------------------------------------------------
private final RelDataTypeFactory typeFactory;
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
index 9af225e..662d316 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -118,10 +118,12 @@ import java.io.StringWriter;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.BitSet;
+import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
@@ -224,20 +226,32 @@ public abstract class RelOptUtil {
* Returns a list of all tables used by this expression or its children
*/
public static List<RelOptTable> findAllTables(RelNode rel) {
+ final Multimap<Class<? extends RelNode>, RelNode> nodes =
+ RelMetadataQuery.instance().getNodeTypes(rel);
final List<RelOptTable> usedTables = new ArrayList<>();
- new RelVisitor() {
- @Override public void visit(RelNode node, int ordinal, RelNode parent) {
- if (node instanceof TableScan) {
+ for (Entry<Class<? extends RelNode>, Collection<RelNode>> e : nodes.asMap().entrySet()) {
+ if (TableScan.class.isAssignableFrom(e.getKey())) {
+ for (RelNode node : e.getValue()) {
usedTables.add(node.getTable());
}
- super.visit(node, ordinal, parent);
}
- // CHECKSTYLE: IGNORE 1
- }.go(rel);
+ }
return usedTables;
}
/**
+ * Returns a list of all tables used by this expression or its children
+ */
+ public static List<String> findAllTableQualifiedNames(RelNode rel) {
+ return Lists.transform(findAllTables(rel),
+ new Function<RelOptTable, String>() {
+ @Override public String apply(RelOptTable arg0) {
+ return arg0.getQualifiedName().toString();
+ }
+ });
+ }
+
+ /**
* Returns a list of variables set by a relational expression or its
* descendants.
*/
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
index eb1ff79..0e0cbca 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/BuiltInMetadata.java
@@ -27,6 +27,7 @@ import org.apache.calcite.util.BuiltInMethod;
import org.apache.calcite.util.ImmutableBitSet;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
import java.util.List;
import java.util.Set;
@@ -160,6 +161,22 @@ public abstract class BuiltInMetadata {
}
}
+ /** Metadata about the node types and count in a relational expression. */
+ public interface NodeTypes extends Metadata {
+ MetadataDef<NodeTypes> DEF = MetadataDef.of(NodeTypes.class,
+ NodeTypes.Handler.class, BuiltInMethod.NODE_TYPES.method);
+
+ /**
+ *
+ */
+ Multimap<Class<? extends RelNode>, RelNode> getNodeTypes();
+
+ /** Handler API. */
+ interface Handler extends MetadataHandler<NodeTypes> {
+ Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(RelNode r, RelMetadataQuery mq);
+ }
+ }
+
/** Metadata about the number of rows returned by a relational expression. */
public interface RowCount extends Metadata {
MetadataDef<RowCount> DEF = MetadataDef.of(RowCount.class,
@@ -370,6 +387,23 @@ public abstract class BuiltInMetadata {
}
}
+ /** Metadata about the origins of expressions. */
+ public interface ExpressionLineage extends Metadata {
+ MetadataDef<ExpressionLineage> DEF = MetadataDef.of(ExpressionLineage.class,
+ ExpressionLineage.Handler.class, BuiltInMethod.EXPRESSION_LINEAGE.method);
+
+ /**
+ *
+ */
+ Set<RexNode> getExpressionLineage(RexNode expression);
+
+ /** Handler API. */
+ interface Handler extends MetadataHandler<ExpressionLineage> {
+ Set<RexNode> getExpressionLineage(RelNode r, RelMetadataQuery mq,
+ RexNode expression);
+ }
+ }
+
/** Metadata about the cost of evaluating a relational expression, including
* all of its inputs. */
public interface CumulativeCost extends Metadata {
@@ -461,6 +495,26 @@ public abstract class BuiltInMetadata {
}
}
+ /** Metadata about the predicates that hold in the rows emitted from a
+ * relational expression. */
+ public interface AllPredicates extends Metadata {
+ MetadataDef<AllPredicates> DEF = MetadataDef.of(AllPredicates.class,
+ AllPredicates.Handler.class, BuiltInMethod.ALL_PREDICATES.method);
+
+ /**
+ * Derives the predicates that hold on rows emitted from a relational
+ * expression.
+ *
+ * @return Predicate list
+ */
+ RelOptPredicateList getAllPredicates();
+
+ /** Handler API. */
+ interface Handler extends MetadataHandler<AllPredicates> {
+ RelOptPredicateList getAllPredicates(RelNode r, RelMetadataQuery mq);
+ }
+ }
+
/** Metadata about the degree of parallelism of a relational expression, and
* how its operators are assigned to processes with independent resource
* pools. */
@@ -547,7 +601,8 @@ public abstract class BuiltInMetadata {
/** The built-in forms of metadata. */
interface All extends Selectivity, UniqueKeys, RowCount, DistinctRowCount,
PercentageOriginalRows, ColumnUniqueness, ColumnOrigin, Predicates,
- Collation, Distribution, Size, Parallelism, Memory {
+ Collation, Distribution, Size, Parallelism, Memory, AllPredicates,
+ ExpressionLineage, NodeTypes {
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java b/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
index bc04c4e..cb86698 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/DefaultRelMetadataProvider.java
@@ -43,6 +43,8 @@ public class DefaultRelMetadataProvider extends ChainedRelMetadataProvider {
ImmutableList.of(
RelMdPercentageOriginalRows.SOURCE,
RelMdColumnOrigins.SOURCE,
+ RelMdExpressionLineage.SOURCE,
+ RelMdNodeTypes.SOURCE,
RelMdRowCount.SOURCE,
RelMdMaxRowCount.SOURCE,
RelMdMinRowCount.SOURCE,
@@ -57,6 +59,7 @@ public class DefaultRelMetadataProvider extends ChainedRelMetadataProvider {
RelMdSelectivity.SOURCE,
RelMdExplainVisibility.SOURCE,
RelMdPredicates.SOURCE,
+ RelMdAllPredicates.SOURCE,
RelMdCollation.SOURCE));
}
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
new file mode 100644
index 0000000..5f87a05
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdAllPredicates.java
@@ -0,0 +1,249 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.plan.RelOptPredicateList;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.hep.HepRelVertex;
+import org.apache.calcite.plan.volcano.RelSubset;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Exchange;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexTableInputRef;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Util;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility to extract Predicates that are present in the (sub)plan
+ * starting at this node.
+ *
+ * This should be used to infer whether same filters are applied on
+ * a given plan by materialized view rewriting rules.
+ *
+ * The output predicates might contain references to columns produced
+ * by TableScan operators ({@link RexTableInputRef}). In turn, each TableScan
+ * operator is identified uniquely by its qualified name and an identifier.
+ *
+ * If the provider cannot infer the lineage for any of the expressions
+ * contain in any of the predicates, it will return null. Observe that
+ * this is different from the empty list of predicates, which means that
+ * there are not predicates in the (sub)plan.
+ *
+ */
+public class RelMdAllPredicates
+ implements MetadataHandler<BuiltInMetadata.AllPredicates> {
+ public static final RelMetadataProvider SOURCE = ReflectiveRelMetadataProvider
+ .reflectiveSource(BuiltInMethod.ALL_PREDICATES.method, new RelMdAllPredicates());
+
+ public MetadataDef<BuiltInMetadata.AllPredicates> getDef() {
+ return BuiltInMetadata.AllPredicates.DEF;
+ }
+
+ /** Catch-all implementation for
+ * {@link BuiltInMetadata.AllPredicates#getAllPredicates()},
+ * invoked using reflection.
+ *
+ * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getAllPredicates(RelNode)
+ */
+ public RelOptPredicateList getAllPredicates(RelNode rel, RelMetadataQuery mq) {
+ return null;
+ }
+
+ public RelOptPredicateList getAllPredicates(HepRelVertex rel, RelMetadataQuery mq) {
+ return mq.getAllPredicates(rel.getCurrentRel());
+ }
+
+ public RelOptPredicateList getAllPredicates(RelSubset rel,
+ RelMetadataQuery mq) {
+ return mq.getAllPredicates(Util.first(rel.getBest(), rel.getOriginal()));
+ }
+
+ /**
+ * Extract predicates for a table scan.
+ */
+ public RelOptPredicateList getAllPredicates(TableScan table, RelMetadataQuery mq) {
+ return RelOptPredicateList.EMPTY;
+ }
+
+ /**
+ * Extract predicates for a project.
+ */
+ public RelOptPredicateList getAllPredicates(Project project, RelMetadataQuery mq) {
+ return mq.getAllPredicates(project.getInput());
+ }
+
+ /**
+ * Add the Filter condition to the list obtained from the input.
+ */
+ public RelOptPredicateList getAllPredicates(Filter filter, RelMetadataQuery mq) {
+ final RelNode input = filter.getInput();
+ final RexBuilder rexBuilder = filter.getCluster().getRexBuilder();
+ final RexNode pred = filter.getCondition();
+
+ final RelOptPredicateList predsBelow = mq.getAllPredicates(input);
+ if (predsBelow == null) {
+ // Safety check
+ return null;
+ }
+
+ // Extract input fields referenced by Filter condition
+ final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
+ final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
+ pred.accept(inputFinder);
+ final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+
+ // Infer column origin expressions for given references
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (int idx : inputFieldsUsed) {
+ final RexInputRef ref = RexInputRef.of(idx, filter.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(filter, ref);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ mapping.put(ref, originalExprs);
+ }
+
+ // Replace with new expressions and return union of predicates
+ return predsBelow.union(rexBuilder,
+ RelOptPredicateList.of(rexBuilder,
+ RelMdExpressionLineage.createAllPossibleExpressions(rexBuilder, pred, mapping)));
+ }
+
+ /**
+ * Add the Join condition to the list obtained from the input.
+ */
+ public RelOptPredicateList getAllPredicates(Join join, RelMetadataQuery mq) {
+ if (join.getJoinType() != JoinRelType.INNER) {
+ // We cannot map origin of this expression.
+ return null;
+ }
+
+ final RexBuilder rexBuilder = join.getCluster().getRexBuilder();
+ final RexNode pred = join.getCondition();
+ final RelNode leftInput = join.getLeft();
+ final RelNode rightInput = join.getRight();
+ final int nLeftColumns = leftInput.getRowType().getFieldList().size();
+
+ RelOptPredicateList newPreds = RelOptPredicateList.EMPTY;
+ for (RelNode input : join.getInputs()) {
+ final RelOptPredicateList inputPreds = mq.getAllPredicates(input);
+ if (inputPreds == null) {
+ // Bail out
+ return null;
+ }
+ newPreds = newPreds.union(rexBuilder, inputPreds);
+ }
+
+ // Extract input fields referenced by Join condition
+ final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
+ final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
+ pred.accept(inputFinder);
+ final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+
+ // Infer column origin expressions for given references
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (int idx : inputFieldsUsed) {
+ if (idx < nLeftColumns) {
+ final RexInputRef inputRef = RexInputRef.of(idx, leftInput.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(leftInput, inputRef);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ final RexInputRef ref = RexInputRef.of(idx, join.getRowType().getFieldList());
+ mapping.put(ref, originalExprs);
+ } else {
+ // Right input.
+ final RexInputRef inputRef = RexInputRef.of(idx - nLeftColumns,
+ rightInput.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(rightInput, inputRef);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ final RexInputRef ref = RexInputRef.of(idx, join.getRowType().getFieldList());
+ mapping.put(ref, originalExprs);
+ }
+ }
+
+ // Replace with new expressions and return union of predicates
+ return newPreds.union(rexBuilder,
+ RelOptPredicateList.of(rexBuilder,
+ RelMdExpressionLineage.createAllPossibleExpressions(rexBuilder, pred, mapping)));
+ }
+
+ /**
+ * Extract predicates for an Aggregate.
+ */
+ public RelOptPredicateList getAllPredicates(Aggregate agg, RelMetadataQuery mq) {
+ return mq.getAllPredicates(agg.getInput());
+ }
+
+ /**
+ * Extract predicates for a Union.
+ */
+ public RelOptPredicateList getAllPredicates(Union union, RelMetadataQuery mq) {
+ final RexBuilder rexBuilder = union.getCluster().getRexBuilder();
+
+ RelOptPredicateList newPreds = RelOptPredicateList.EMPTY;
+ for (RelNode input : union.getInputs()) {
+ final RelOptPredicateList inputPreds = mq.getAllPredicates(input);
+ if (inputPreds == null) {
+ // Bail out
+ return null;
+ }
+ newPreds = newPreds.union(rexBuilder, inputPreds);
+ }
+ return newPreds;
+ }
+
+ /**
+ * Extract predicates for a Sort.
+ */
+ public RelOptPredicateList getAllPredicates(Sort sort, RelMetadataQuery mq) {
+ return mq.getAllPredicates(sort.getInput());
+ }
+
+ /**
+ * Extract predicates for an Exchange.
+ */
+ public RelOptPredicateList getAllPredicates(Exchange exchange,
+ RelMetadataQuery mq) {
+ return mq.getAllPredicates(exchange.getInput());
+ }
+
+}
+
+// End RelMdAllPredicates.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
new file mode 100644
index 0000000..5f9e5ba
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdExpressionLineage.java
@@ -0,0 +1,450 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.hep.HepRelVertex;
+import org.apache.calcite.plan.volcano.RelSubset;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Exchange;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
+import org.apache.calcite.rex.RexTableInputRef;
+import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.calcite.util.Util;
+
+import com.google.common.base.Function;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * RelMdExpressionLineage supplies a default implementation of
+ * {@link RelMetadataQuery#getExpressionLineage} for the standard logical algebra.
+ *
+ * The goal of this provider is to infer the lineage for the given expression.
+ *
+ * The output expressions might contain references to columns produced by TableScan
+ * operators ({@link RexTableInputRef}). In turn, each TableScan operator is identified
+ * uniquely by its qualified name and an identifier contained in .
+ *
+ * If the lineage cannot be inferred, we return null.
+ */
+public class RelMdExpressionLineage
+ implements MetadataHandler<BuiltInMetadata.ExpressionLineage> {
+ public static final RelMetadataProvider SOURCE =
+ ReflectiveRelMetadataProvider.reflectiveSource(
+ BuiltInMethod.EXPRESSION_LINEAGE.method, new RelMdExpressionLineage());
+
+ //~ Constructors -----------------------------------------------------------
+
+ private RelMdExpressionLineage() {}
+
+ //~ Methods ----------------------------------------------------------------
+
+ public MetadataDef<BuiltInMetadata.ExpressionLineage> getDef() {
+ return BuiltInMetadata.ExpressionLineage.DEF;
+ }
+
+ // Catch-all rule when none of the others apply.
+ public Set<RexNode> getExpressionLineage(RelNode rel,
+ RelMetadataQuery mq, RexNode outputExpression) {
+ return null;
+ }
+
+ public Set<RexNode> getExpressionLineage(HepRelVertex rel, RelMetadataQuery mq,
+ RexNode outputExpression) {
+ return mq.getExpressionLineage(rel.getCurrentRel(), outputExpression);
+ }
+
+ public Set<RexNode> getExpressionLineage(RelSubset rel,
+ RelMetadataQuery mq, RexNode outputExpression) {
+ return mq.getExpressionLineage(Util.first(rel.getBest(), rel.getOriginal()),
+ outputExpression);
+ }
+
+ /**
+ * Expression lineage from TableScan.
+ *
+ * We extract the fields referenced by the expression and we express them
+ * using {@link RexTableInputRef}.
+ */
+ public Set<RexNode> getExpressionLineage(TableScan rel,
+ RelMetadataQuery mq, RexNode outputExpression) {
+ final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+
+ // Extract input fields referenced by expression
+ final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
+ final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
+ outputExpression.accept(inputFinder);
+ final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+
+ // Infer column origin expressions for given references
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (int idx : inputFieldsUsed) {
+ final RexNode inputRef = RexTableInputRef.of(
+ new RelTableRef(rel.getTable().getQualifiedName().toString(), 0),
+ RexInputRef.of(idx, rel.getRowType().getFieldList()));
+ final Set<RexNode> originalExprs = Sets.newHashSet(inputRef);
+ final RexInputRef ref = RexInputRef.of(idx, rel.getRowType().getFieldList());
+ mapping.put(ref, originalExprs);
+ }
+
+ // Return result
+ return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
+ }
+
+ /**
+ * Expression lineage from Aggregate.
+ *
+ * If the expression references grouping sets or aggregation function results,
+ * we cannot extract the lineage and we return null.
+ */
+ public Set<RexNode> getExpressionLineage(Aggregate rel,
+ RelMetadataQuery mq, RexNode outputExpression) {
+ final RelNode input = rel.getInput();
+ final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+
+ // Extract input fields referenced by expression
+ final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
+ final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
+ outputExpression.accept(inputFinder);
+ final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+
+ for (int idx : inputFieldsUsed) {
+ if (idx >= rel.getGroupCount()) {
+ // We cannot map origin of this expression.
+ return null;
+ }
+ }
+
+ // Infer column origin expressions for given references
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (int idx : inputFieldsUsed) {
+ final RexInputRef inputRef = RexInputRef.of(rel.getGroupSet().nth(idx),
+ input.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(input, inputRef);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ final RexInputRef ref = RexInputRef.of(idx, rel.getRowType().getFieldList());
+ mapping.put(ref, originalExprs);
+ }
+
+ // Return result
+ return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
+ }
+
+ /**
+ * Expression lineage from Join.
+ *
+ * We only extract the lineage for INNER joins.
+ */
+ public Set<RexNode> getExpressionLineage(Join rel, RelMetadataQuery mq,
+ RexNode outputExpression) {
+ if (rel.getJoinType() != JoinRelType.INNER) {
+ // We cannot map origin of this expression.
+ return null;
+ }
+
+ final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+ final RelNode leftInput = rel.getLeft();
+ final RelNode rightInput = rel.getRight();
+ final int nLeftColumns = leftInput.getRowType().getFieldList().size();
+
+ // Infer column origin expressions for given references
+ final Multimap<String, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
+ final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (int idx = 0; idx < rel.getRowType().getFieldList().size(); idx++) {
+ if (idx < nLeftColumns) {
+ final RexInputRef inputRef = RexInputRef.of(idx, leftInput.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(leftInput, inputRef);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ // Gather table references, left input references remain unchanged
+ final Set<RelTableRef> tableRefs =
+ RexUtil.gatherTableReferences(Lists.newArrayList(originalExprs));
+ for (RelTableRef leftRef : tableRefs) {
+ qualifiedNamesToRefs.put(leftRef.getQualifiedName(), leftRef);
+ }
+ mapping.put(RexInputRef.of(idx, rel.getRowType().getFieldList()), originalExprs);
+ } else {
+ // Right input.
+ final RexInputRef inputRef = RexInputRef.of(idx - nLeftColumns,
+ rightInput.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(rightInput, inputRef);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ // Gather table references, right input references might need to be
+ // updated if there are table names clashes with left input
+ final Set<RelTableRef> tableRefs =
+ RexUtil.gatherTableReferences(Lists.newArrayList(originalExprs));
+ for (RelTableRef rightRef : tableRefs) {
+ int shift = 0;
+ Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
+ rightRef.getQualifiedName());
+ if (lRefs != null) {
+ shift = lRefs.size();
+ }
+ currentTablesMapping.put(rightRef,
+ new RelTableRef(rightRef.getQualifiedName(), shift + rightRef.getIdentifier()));
+ }
+ final Set<RexNode> updatedExprs = Sets.newHashSet(
+ Iterables.transform(
+ originalExprs,
+ new Function<RexNode, RexNode>() {
+ @Override public RexNode apply(RexNode e) {
+ return RexUtil.swapTableReferences(rexBuilder, e, currentTablesMapping);
+ }
+ }
+ ));
+ mapping.put(RexInputRef.of(idx, rel.getRowType().getFieldList()), updatedExprs);
+ }
+ }
+
+ // Return result
+ return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
+ }
+
+ /**
+ * Expression lineage from Union.
+ *
+ * For Union operator, we might be able to extract multiple origins for the
+ * references in the given expression.
+ */
+ public Set<RexNode> getExpressionLineage(Union rel, RelMetadataQuery mq,
+ RexNode outputExpression) {
+ final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+
+ // Infer column origin expressions for given references
+ final Multimap<String, RelTableRef> qualifiedNamesToRefs = HashMultimap.create();
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (RelNode input : rel.getInputs()) {
+ final Map<RelTableRef, RelTableRef> currentTablesMapping = new HashMap<>();
+ for (int idx = 0; idx < input.getRowType().getFieldList().size(); idx++) {
+ final RexInputRef inputRef = RexInputRef.of(idx, input.getRowType().getFieldList());
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(input, inputRef);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+
+ final RexInputRef ref = RexInputRef.of(idx, rel.getRowType().getFieldList());
+ // Gather table references, references might need to be
+ // updated
+ final Set<RelTableRef> tableRefs =
+ RexUtil.gatherTableReferences(Lists.newArrayList(originalExprs));
+ for (RelTableRef tableRef : tableRefs) {
+ int shift = 0;
+ Collection<RelTableRef> lRefs = qualifiedNamesToRefs.get(
+ tableRef.getQualifiedName());
+ if (lRefs != null) {
+ shift = lRefs.size();
+ }
+ currentTablesMapping.put(tableRef,
+ new RelTableRef(tableRef.getQualifiedName(), shift + tableRef.getIdentifier()));
+ }
+ final Set<RexNode> updatedExprs = Sets.newHashSet(
+ Iterables.transform(
+ originalExprs,
+ new Function<RexNode, RexNode>() {
+ @Override public RexNode apply(RexNode e) {
+ return RexUtil.swapTableReferences(rexBuilder, e, currentTablesMapping);
+ }
+ }
+ ));
+ final Set<RexNode> set = mapping.get(ref);
+ if (set != null) {
+ set.addAll(updatedExprs);
+ } else {
+ mapping.put(ref, updatedExprs);
+ }
+ }
+ // Add to existing qualified names
+ for (RelTableRef newRef : currentTablesMapping.values()) {
+ qualifiedNamesToRefs.put(newRef.getQualifiedName(), newRef);
+ }
+ }
+
+ // Return result
+ return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
+ }
+
+ /**
+ * Expression lineage from Project.
+ */
+ public Set<RexNode> getExpressionLineage(Project rel,
+ final RelMetadataQuery mq, RexNode outputExpression) {
+ final RelNode input = rel.getInput();
+ final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
+
+ // Extract input fields referenced by expression
+ final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
+ final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
+ outputExpression.accept(inputFinder);
+ final ImmutableBitSet inputFieldsUsed = inputFinder.inputBitSet.build();
+
+ // Infer column origin expressions for given references
+ final Map<RexInputRef, Set<RexNode>> mapping = new LinkedHashMap<>();
+ for (int idx : inputFieldsUsed) {
+ final RexNode inputExpr = rel.getChildExps().get(idx);
+ final Set<RexNode> originalExprs = mq.getExpressionLineage(input, inputExpr);
+ if (originalExprs == null) {
+ // Bail out
+ return null;
+ }
+ final RexInputRef ref = RexInputRef.of(idx, rel.getRowType().getFieldList());
+ mapping.put(ref, originalExprs);
+ }
+
+ // Return result
+ return createAllPossibleExpressions(rexBuilder, outputExpression, mapping);
+ }
+
+ /**
+ * Expression lineage from Filter.
+ */
+ public Set<RexNode> getExpressionLineage(Filter rel,
+ RelMetadataQuery mq, RexNode outputExpression) {
+ return mq.getExpressionLineage(rel.getInput(), outputExpression);
+ }
+
+ /**
+ * Expression lineage from Sort.
+ */
+ public Set<RexNode> getExpressionLineage(Sort rel, RelMetadataQuery mq,
+ RexNode outputExpression) {
+ return mq.getExpressionLineage(rel.getInput(), outputExpression);
+ }
+
+ /**
+ * Expression lineage from Exchange.
+ */
+ public Set<RexNode> getExpressionLineage(Exchange rel,
+ RelMetadataQuery mq, RexNode outputExpression) {
+ return mq.getExpressionLineage(rel.getInput(), outputExpression);
+ }
+
+ /**
+ * Given an expression, it will create all equivalent expressions resulting
+ * from replacing all possible combinations of references in the mapping by
+ * the corresponding expressions.
+ *
+ * @param rexBuilder rexBuilder
+ * @param expr expression
+ * @param mapping mapping
+ * @return set of resulting expressions equivalent to the input expression
+ */
+ protected static Set<RexNode> createAllPossibleExpressions(RexBuilder rexBuilder,
+ RexNode expr, Map<RexInputRef, Set<RexNode>> mapping) {
+ // Extract input fields referenced by expression
+ final Set<RelDataTypeField> inputExtraFields = new LinkedHashSet<>();
+ final RelOptUtil.InputFinder inputFinder = new RelOptUtil.InputFinder(inputExtraFields);
+ expr.accept(inputFinder);
+ final ImmutableBitSet predFieldsUsed = inputFinder.inputBitSet.build();
+
+ return createAllPossibleExpressions(rexBuilder, expr, predFieldsUsed, mapping,
+ new HashMap<RexInputRef, RexNode>());
+ }
+
+ private static Set<RexNode> createAllPossibleExpressions(RexBuilder rexBuilder,
+ RexNode expr, ImmutableBitSet predFieldsUsed, Map<RexInputRef, Set<RexNode>> mapping,
+ Map<RexInputRef, RexNode> singleMapping) {
+ final RexInputRef inputRef = mapping.keySet().iterator().next();
+ final Set<RexNode> replacements = mapping.remove(inputRef);
+ Set<RexNode> result = new HashSet<>();
+ assert !replacements.isEmpty();
+ if (predFieldsUsed.indexOf(inputRef.getIndex()) != -1) {
+ for (RexNode replacement : replacements) {
+ singleMapping.put(inputRef, replacement);
+ createExpressions(rexBuilder, expr, predFieldsUsed, mapping, singleMapping, result);
+ singleMapping.remove(inputRef);
+ }
+ } else {
+ createExpressions(rexBuilder, expr, predFieldsUsed, mapping, singleMapping, result);
+ }
+ mapping.put(inputRef, replacements);
+ return result;
+ }
+
+ private static void createExpressions(RexBuilder rexBuilder,
+ RexNode expr, ImmutableBitSet predFieldsUsed, Map<RexInputRef, Set<RexNode>> mapping,
+ Map<RexInputRef, RexNode> singleMapping, Set<RexNode> result) {
+ if (mapping.isEmpty()) {
+ final RexReplacer replacer = new RexReplacer(singleMapping);
+ final List<RexNode> updatedPreds = new ArrayList<>(
+ RelOptUtil.conjunctions(
+ rexBuilder.copy(expr)));
+ replacer.mutate(updatedPreds);
+ result.addAll(updatedPreds);
+ } else {
+ result.addAll(
+ createAllPossibleExpressions(
+ rexBuilder, expr, predFieldsUsed, mapping, singleMapping));
+ }
+ }
+
+ /**
+ * Replaces expressions with their equivalences. Note that we only have to
+ * look for RexInputRef.
+ */
+ private static class RexReplacer extends RexShuttle {
+ private final Map<RexInputRef, RexNode> replacementValues;
+
+ RexReplacer(Map<RexInputRef, RexNode> replacementValues) {
+ this.replacementValues = replacementValues;
+ }
+
+ @Override public RexNode visitInputRef(RexInputRef inputRef) {
+ return replacementValues.get(inputRef);
+ }
+ }
+
+}
+
+// End RelMdExpressionLineage.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java
new file mode 100644
index 0000000..566df3a
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdNodeTypes.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+import org.apache.calcite.plan.hep.HepRelVertex;
+import org.apache.calcite.plan.volcano.RelSubset;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Calc;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Intersect;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.Minus;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.SemiJoin;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.TableScan;
+import org.apache.calcite.rel.core.Union;
+import org.apache.calcite.rel.core.Values;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.calcite.util.Util;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * RelMdNodeTypeCount supplies a default implementation of
+ * {@link RelMetadataQuery#getNodeTypes} for the standard logical algebra.
+ */
+public class RelMdNodeTypes
+ implements MetadataHandler<BuiltInMetadata.NodeTypes> {
+ public static final RelMetadataProvider SOURCE =
+ ReflectiveRelMetadataProvider.reflectiveSource(
+ BuiltInMethod.NODE_TYPES.method, new RelMdNodeTypes());
+
+ //~ Methods ----------------------------------------------------------------
+
+ public MetadataDef<BuiltInMetadata.NodeTypes> getDef() {
+ return BuiltInMetadata.NodeTypes.DEF;
+ }
+
+ /** Catch-all implementation for
+ * {@link BuiltInMetadata.NodeTypeCount#getNodeTypes()},
+ * invoked using reflection.
+ *
+ * @see org.apache.calcite.rel.metadata.RelMetadataQuery#getNodeTypes(RelNode)
+ */
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(RelNode rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, RelNode.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(HepRelVertex rel,
+ RelMetadataQuery mq) {
+ return mq.getNodeTypes(rel.getCurrentRel());
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(RelSubset rel,
+ RelMetadataQuery mq) {
+ return mq.getNodeTypes(Util.first(rel.getBest(), rel.getOriginal()));
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Union rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Union.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Intersect rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Intersect.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Minus rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Minus.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Filter rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Filter.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Calc rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Calc.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Project rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Project.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Sort rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Sort.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Join rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Join.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(SemiJoin rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, SemiJoin.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Aggregate rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Aggregate.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(TableScan rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, TableScan.class, mq);
+ }
+
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(Values rel,
+ RelMetadataQuery mq) {
+ return getNodeTypes(rel, Values.class, mq);
+ }
+
+ private static Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(RelNode rel,
+ Class<? extends RelNode> c, RelMetadataQuery mq) {
+ final Multimap<Class<? extends RelNode>, RelNode> nodeTypeCount = ArrayListMultimap.create();
+ for (RelNode input : rel.getInputs()) {
+ Multimap<Class<? extends RelNode>, RelNode> partialNodeTypeCount =
+ mq.getNodeTypes(input);
+ if (partialNodeTypeCount == null) {
+ return null;
+ }
+ nodeTypeCount.putAll(partialNodeTypeCount);
+ }
+ nodeTypeCount.put(c, rel);
+ return nodeTypeCount;
+ }
+
+}
+
+// End RelMdNodeTypes.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
index 7b63ac9..4ef5592 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdUtil.java
@@ -843,6 +843,7 @@ public class RelMdUtil {
}
return alreadySorted && alreadySmaller;
}
+
}
// End RelMdUtil.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
index 85f747c..9dd19dc 100644
--- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMetadataQuery.java
@@ -29,6 +29,7 @@ import org.apache.calcite.util.ImmutableBitSet;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
@@ -84,6 +85,7 @@ public class RelMetadataQuery {
private BuiltInMetadata.Collation.Handler collationHandler;
private BuiltInMetadata.ColumnOrigin.Handler columnOriginHandler;
+ private BuiltInMetadata.ExpressionLineage.Handler expressionLineageHandler;
private BuiltInMetadata.ColumnUniqueness.Handler columnUniquenessHandler;
private BuiltInMetadata.CumulativeCost.Handler cumulativeCostHandler;
private BuiltInMetadata.DistinctRowCount.Handler distinctRowCountHandler;
@@ -97,6 +99,8 @@ public class RelMetadataQuery {
private BuiltInMetadata.PercentageOriginalRows.Handler percentageOriginalRowsHandler;
private BuiltInMetadata.PopulationSize.Handler populationSizeHandler;
private BuiltInMetadata.Predicates.Handler predicatesHandler;
+ private BuiltInMetadata.AllPredicates.Handler allPredicatesHandler;
+ private BuiltInMetadata.NodeTypes.Handler nodeTypesHandler;
private BuiltInMetadata.RowCount.Handler rowCountHandler;
private BuiltInMetadata.Selectivity.Handler selectivityHandler;
private BuiltInMetadata.Size.Handler sizeHandler;
@@ -114,6 +118,7 @@ public class RelMetadataQuery {
this.metadataProvider = Preconditions.checkNotNull(metadataProvider);
this.collationHandler = prototype.collationHandler;
this.columnOriginHandler = prototype.columnOriginHandler;
+ this.expressionLineageHandler = prototype.expressionLineageHandler;
this.columnUniquenessHandler = prototype.columnUniquenessHandler;
this.cumulativeCostHandler = prototype.cumulativeCostHandler;
this.distinctRowCountHandler = prototype.distinctRowCountHandler;
@@ -127,6 +132,8 @@ public class RelMetadataQuery {
this.percentageOriginalRowsHandler = prototype.percentageOriginalRowsHandler;
this.populationSizeHandler = prototype.populationSizeHandler;
this.predicatesHandler = prototype.predicatesHandler;
+ this.allPredicatesHandler = prototype.allPredicatesHandler;
+ this.nodeTypesHandler = prototype.nodeTypesHandler;
this.rowCountHandler = prototype.rowCountHandler;
this.selectivityHandler = prototype.selectivityHandler;
this.sizeHandler = prototype.sizeHandler;
@@ -162,6 +169,7 @@ public class RelMetadataQuery {
this.metadataProvider = null;
this.collationHandler = initialHandler(BuiltInMetadata.Collation.Handler.class);
this.columnOriginHandler = initialHandler(BuiltInMetadata.ColumnOrigin.Handler.class);
+ this.expressionLineageHandler = initialHandler(BuiltInMetadata.ExpressionLineage.Handler.class);
this.columnUniquenessHandler = initialHandler(BuiltInMetadata.ColumnUniqueness.Handler.class);
this.cumulativeCostHandler = initialHandler(BuiltInMetadata.CumulativeCost.Handler.class);
this.distinctRowCountHandler = initialHandler(BuiltInMetadata.DistinctRowCount.Handler.class);
@@ -176,6 +184,8 @@ public class RelMetadataQuery {
initialHandler(BuiltInMetadata.PercentageOriginalRows.Handler.class);
this.populationSizeHandler = initialHandler(BuiltInMetadata.PopulationSize.Handler.class);
this.predicatesHandler = initialHandler(BuiltInMetadata.Predicates.Handler.class);
+ this.allPredicatesHandler = initialHandler(BuiltInMetadata.AllPredicates.Handler.class);
+ this.nodeTypesHandler = initialHandler(BuiltInMetadata.NodeTypes.Handler.class);
this.rowCountHandler = initialHandler(BuiltInMetadata.RowCount.Handler.class);
this.selectivityHandler = initialHandler(BuiltInMetadata.Selectivity.Handler.class);
this.sizeHandler = initialHandler(BuiltInMetadata.Size.Handler.class);
@@ -191,6 +201,24 @@ public class RelMetadataQuery {
/**
* Returns the
+ * {@link BuiltInMetadata.NodeTypeCount#getNodeTypeCount()}
+ * statistic.
+ *
+ * @param rel the relational expression
+ * @return
+ */
+ public Multimap<Class<? extends RelNode>, RelNode> getNodeTypes(RelNode rel) {
+ for (;;) {
+ try {
+ return nodeTypesHandler.getNodeTypes(rel, this);
+ } catch (JaninoRelMetadataProvider.NoHandler e) {
+ nodeTypesHandler = revise(e.relClass, BuiltInMetadata.NodeTypes.DEF);
+ }
+ }
+ }
+
+ /**
+ * Returns the
* {@link BuiltInMetadata.RowCount#getRowCount()}
* statistic.
*
@@ -352,6 +380,20 @@ public class RelMetadataQuery {
}
/**
+ * Determines the origin of a column.
+ */
+ public Set<RexNode> getExpressionLineage(RelNode rel, RexNode expression) {
+ for (;;) {
+ try {
+ return expressionLineageHandler.getExpressionLineage(rel, this, expression);
+ } catch (JaninoRelMetadataProvider.NoHandler e) {
+ expressionLineageHandler =
+ revise(e.relClass, BuiltInMetadata.ExpressionLineage.DEF);
+ }
+ }
+ }
+
+ /**
* Determines the origin of a {@link RelNode}, provided it maps to a single
* table, optionally with filtering and projection.
*
@@ -749,6 +791,24 @@ public class RelMetadataQuery {
/**
* Returns the
+ * {@link BuiltInMetadata.AllPredicates#getAllPredicates()}
+ * statistic.
+ *
+ * @param rel the relational expression
+ * @return All predicates within and below this RelNode
+ */
+ public RelOptPredicateList getAllPredicates(RelNode rel) {
+ for (;;) {
+ try {
+ return allPredicatesHandler.getAllPredicates(rel, this);
+ } catch (JaninoRelMetadataProvider.NoHandler e) {
+ allPredicatesHandler = revise(e.relClass, BuiltInMetadata.AllPredicates.DEF);
+ }
+ }
+ }
+
+ /**
+ * Returns the
* {@link BuiltInMetadata.ExplainVisibility#isVisibleInExplain(SqlExplainLevel)}
* statistic.
*
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rel/metadata/RelTableRef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelTableRef.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelTableRef.java
new file mode 100644
index 0000000..bdd032f
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelTableRef.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rel.metadata;
+
+/**
+ *
+ *
+ */
+public class RelTableRef {
+
+ private final String qualifiedName;
+ private final int identifier;
+ private final String digest;
+
+ public RelTableRef(String qualifiedName, int identifier) {
+ this.qualifiedName = qualifiedName;
+ this.identifier = identifier;
+ this.digest = qualifiedName + ".#" + identifier;
+ }
+
+ //~ Methods ----------------------------------------------------------------
+
+ @Override public boolean equals(Object obj) {
+ return this == obj
+ || obj instanceof RelTableRef
+ && qualifiedName.equals(((RelTableRef) obj).qualifiedName)
+ && identifier == ((RelTableRef) obj).identifier;
+ }
+
+ @Override public int hashCode() {
+ return digest.hashCode();
+ }
+
+ public String getQualifiedName() {
+ return qualifiedName;
+ }
+
+ public int getIdentifier() {
+ return identifier;
+ }
+
+ @Override public String toString() {
+ return digest;
+ }
+
+}
+
+// End RelTableRef.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
index a16a834..f632e94 100644
--- a/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/LogicVisitor.java
@@ -163,6 +163,10 @@ public class LogicVisitor implements RexBiVisitor<Logic, Logic> {
return end(subQuery, arg);
}
+ @Override public Logic visitTableInputRef(RexTableInputRef ref, Logic arg) {
+ return end(ref, arg);
+ }
+
@Override public Logic visitPatternFieldRef(RexPatternFieldRef ref, Logic arg) {
return end(ref, arg);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java b/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
index 8e0b014..aa494c2 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexBiVisitor.java
@@ -48,6 +48,8 @@ public interface RexBiVisitor<R, P> {
R visitSubQuery(RexSubQuery subQuery, P arg);
+ R visitTableInputRef(RexTableInputRef ref, P arg);
+
R visitPatternFieldRef(RexPatternFieldRef ref, P arg);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
index b642503..6714be1 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexShuttle.java
@@ -89,6 +89,10 @@ public class RexShuttle implements RexVisitor<RexNode> {
}
}
+ @Override public RexNode visitTableInputRef(RexTableInputRef ref) {
+ return ref;
+ }
+
@Override public RexNode visitPatternFieldRef(RexPatternFieldRef fieldRef) {
return fieldRef;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index a910778..fb791f5 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -29,9 +29,11 @@ import org.apache.calcite.util.Util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.BoundType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
+import com.google.common.collect.Range;
import java.util.ArrayList;
import java.util.Collection;
@@ -563,8 +565,8 @@ public class RexSimplify {
return simplify(terms.get(0));
}
// Try to simplify the expression
- final Multimap<String, Pair<String, RexNode>> equalityTerms =
- ArrayListMultimap.create();
+ final Multimap<String, Pair<String, RexNode>> equalityTerms = ArrayListMultimap.create();
+ final Map<String, Pair<Range, List<RexNode>>> rangeTerms = new HashMap<>();
final Map<String, String> equalityConstantTerms = new HashMap<>();
final Set<String> negatedTerms = new HashSet<>();
final Set<String> nullOperands = new HashSet<>();
@@ -611,23 +613,23 @@ public class RexSimplify {
RexCall rightCast = (RexCall) right;
comparedOperands.add(rightCast.getOperands().get(0).toString());
}
- // Check for equality on different constants. If the same ref or
- // CAST(ref) is equal to different constants, this condition cannot be
- // satisfied, and hence it can be evaluated to FALSE.
+ // Check for equality on different constants. If the same ref or CAST(ref)
+ // is equal to different constants, this condition cannot be satisfied,
+ // and hence it can be evaluated to FALSE
+ final boolean leftRef = RexUtil.isReferenceOrAccess(left, true);
+ final boolean rightRef = RexUtil.isReferenceOrAccess(right, true);
+ final boolean leftConstant = left.isA(SqlKind.LITERAL);
+ final boolean rightConstant = right.isA(SqlKind.LITERAL);
if (term.getKind() == SqlKind.EQUALS) {
- final boolean leftRef = RexUtil.isReferenceOrAccess(left, true);
- final boolean rightRef = RexUtil.isReferenceOrAccess(right, true);
- if (right instanceof RexLiteral && leftRef) {
+ if (leftRef && rightConstant) {
final String literal = right.toString();
- final String prevLiteral =
- equalityConstantTerms.put(left.toString(), literal);
+ final String prevLiteral = equalityConstantTerms.put(left.toString(), literal);
if (prevLiteral != null && !literal.equals(prevLiteral)) {
return rexBuilder.makeLiteral(false);
}
- } else if (left instanceof RexLiteral && rightRef) {
+ } else if (leftConstant && rightRef) {
final String literal = left.toString();
- final String prevLiteral =
- equalityConstantTerms.put(right.toString(), literal);
+ final String prevLiteral = equalityConstantTerms.put(right.toString(), literal);
if (prevLiteral != null && !literal.equals(prevLiteral)) {
return rexBuilder.makeLiteral(false);
}
@@ -637,19 +639,39 @@ public class RexSimplify {
}
// Assume the expression a > 5 is part of a Filter condition.
// Then we can derive the negated term: a <= 5.
- // But as comparison is string-based and thus operands order-dependent,
+ // But as the comparison is string based and thus operands order dependent,
// we should also add the inverted negated term: 5 >= a.
- // Observe that for creating the inverted term we invert the list of
- // operands.
+ // Observe that for creating the inverted term we invert the list of operands.
RexNode negatedTerm = RexUtil.negate(rexBuilder, call);
if (negatedTerm != null) {
negatedTerms.add(negatedTerm.toString());
- RexNode invertNegatedTerm =
- RexUtil.invert(rexBuilder, (RexCall) negatedTerm);
+ RexNode invertNegatedTerm = RexUtil.invert(rexBuilder, (RexCall) negatedTerm);
if (invertNegatedTerm != null) {
negatedTerms.add(invertNegatedTerm.toString());
}
}
+ // Range
+ SqlKind comparison = null;
+ RexNode ref = null;
+ RexLiteral constant = null;
+ if (leftRef && rightConstant) {
+ comparison = term.getKind();
+ ref = left;
+ constant = (RexLiteral) right;
+ } else if (leftConstant && rightRef) {
+ comparison = term.getKind().reverse();
+ constant = (RexLiteral) left;
+ ref = right;
+ }
+ if (comparison != null
+ && comparison != SqlKind.NOT_EQUALS) { // NOT_EQUALS not supported
+ final RexNode result = processRange(rexBuilder, terms, rangeTerms,
+ term, ref, constant, comparison);
+ if (result != null) {
+ // Not satisfiable
+ return result;
+ }
+ }
break;
case IN:
comparedOperands.add(((RexCall) term).operands.get(0).toString());
@@ -717,8 +739,7 @@ public class RexSimplify {
if (!RexUtil.isDeterministic(notDisjunction)) {
continue;
}
- final List<String> terms2Set =
- RexUtil.strings(RelOptUtil.conjunctions(notDisjunction));
+ final List<String> terms2Set = RexUtil.strings(RelOptUtil.conjunctions(notDisjunction));
if (termsSet.containsAll(terms2Set)) {
return rexBuilder.makeLiteral(false);
}
@@ -800,6 +821,219 @@ public class RexSimplify {
}
}
+ private static RexNode processRange(RexBuilder rexBuilder,
+ List<RexNode> terms, Map<String, Pair<Range, List<RexNode>>> rangeTerms,
+ RexNode term, RexNode ref, RexLiteral constant, SqlKind comparison) {
+ final Comparable v0 = constant.getValue();
+ Pair<Range, List<RexNode>> p = rangeTerms.get(ref.toString());
+ if (p == null) {
+ Range r;
+ switch (comparison) {
+ case EQUALS:
+ r = Range.singleton(v0);
+ break;
+ case LESS_THAN:
+ r = Range.lessThan(v0);
+ break;
+ case LESS_THAN_OR_EQUAL:
+ r = Range.atMost(v0);
+ break;
+ case GREATER_THAN:
+ r = Range.greaterThan(v0);
+ break;
+ case GREATER_THAN_OR_EQUAL:
+ r = Range.atLeast(v0);
+ break;
+ default:
+ throw new AssertionError();
+ }
+ rangeTerms.put(ref.toString(),
+ new Pair(r, ImmutableList.of(term)));
+ } else {
+ // Exists
+ boolean removeUpperBound = false;
+ boolean removeLowerBound = false;
+ Range r = p.left;
+ switch (comparison) {
+ case EQUALS:
+ if (!r.contains(v0)) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+ rangeTerms.put(ref.toString(),
+ new Pair(Range.singleton(v0), ImmutableList.of(term)));
+ // remove
+ terms.removeAll(p.right);
+ break;
+ case LESS_THAN: {
+ int comparisonResult = 0;
+ if (r.hasUpperBound()) {
+ comparisonResult = v0.compareTo(r.upperEndpoint());
+ }
+ if (comparisonResult <= 0) {
+ // 1) No upper bound, or
+ // 2) We need to open the upper bound, or
+ // 3) New upper bound is lower than old upper bound
+ if (r.hasLowerBound()) {
+ if (v0.compareTo(r.lowerEndpoint()) < 0) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+ // a <= x < b OR a < x < b
+ r = Range.range(r.lowerEndpoint(), r.lowerBoundType(),
+ v0, BoundType.OPEN);
+ } else {
+ // x < b
+ r = Range.lessThan(v0);
+ }
+
+ if (r.isEmpty()) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+
+ // remove prev upper bound
+ removeUpperBound = true;
+ } else {
+ // Remove this term as it is contained in current upper bound
+ terms.remove(term);
+ }
+ break;
+ }
+ case LESS_THAN_OR_EQUAL: {
+ int comparisonResult = -1;
+ if (r.hasUpperBound()) {
+ comparisonResult = v0.compareTo(r.upperEndpoint());
+ }
+ if (comparisonResult < 0) {
+ // 1) No upper bound, or
+ // 2) New upper bound is lower than old upper bound
+ if (r.hasLowerBound()) {
+ if (v0.compareTo(r.lowerEndpoint()) < 0) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+ // a <= x <= b OR a < x <= b
+ r = Range.range(r.lowerEndpoint(), r.lowerBoundType(),
+ v0, BoundType.CLOSED);
+ } else {
+ // x <= b
+ r = Range.atMost(v0);
+ }
+
+ if (r.isEmpty()) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+
+ // remove prev upper bound
+ removeUpperBound = true;
+ } else {
+ // Remove this term as it is contained in current upper bound
+ terms.remove(term);
+ }
+ break;
+ }
+ case GREATER_THAN: {
+ int comparisonResult = 0;
+ if (r.hasLowerBound()) {
+ comparisonResult = v0.compareTo(r.lowerEndpoint());
+ }
+ if (comparisonResult >= 0) {
+ // 1) No lower bound, or
+ // 2) We need to open the lower bound, or
+ // 3) New lower bound is greater than old lower bound
+ if (r.hasUpperBound()) {
+ if (v0.compareTo(r.upperEndpoint()) > 0) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+ // a < x <= b OR a < x < b
+ r = Range.range(v0, BoundType.OPEN,
+ r.upperEndpoint(), r.upperBoundType());
+ } else {
+ // x > a
+ r = Range.greaterThan(v0);
+ }
+
+ if (r.isEmpty()) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+
+ // remove prev lower bound
+ removeLowerBound = true;
+ } else {
+ // Remove this term as it is contained in current lower bound
+ terms.remove(term);
+ }
+ break;
+ }
+ case GREATER_THAN_OR_EQUAL: {
+ int comparisonResult = 1;
+ if (r.hasLowerBound()) {
+ comparisonResult = v0.compareTo(r.lowerEndpoint());
+ }
+ if (comparisonResult > 0) {
+ // 1) No lower bound, or
+ // 2) New lower bound is greater than old lower bound
+ if (r.hasUpperBound()) {
+ if (v0.compareTo(r.upperEndpoint()) > 0) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+ // a <= x <= b OR a <= x < b
+ r = Range.range(v0, BoundType.CLOSED,
+ r.upperEndpoint(), r.upperBoundType());
+ } else {
+ // x >= a
+ r = Range.atLeast(v0);
+ }
+
+ if (r.isEmpty()) {
+ // Range is empty, not satisfiable
+ return rexBuilder.makeLiteral(false);
+ }
+
+ // remove prev lower bound
+ removeLowerBound = true;
+ } else {
+ // Remove this term as it is contained in current lower bound
+ terms.remove(term);
+ }
+ break;
+ }
+ default:
+ throw new AssertionError();
+ }
+ if (removeUpperBound) {
+ ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder();
+ for (RexNode e : p.right) {
+ if (e.isA(SqlKind.LESS_THAN) || e.isA(SqlKind.LESS_THAN_OR_EQUAL)) {
+ terms.remove(e);
+ } else {
+ newBounds.add(e);
+ }
+ }
+ newBounds.add(term);
+ rangeTerms.put(ref.toString(), new Pair(r, newBounds.build()));
+ } else if (removeLowerBound) {
+ ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder();
+ for (RexNode e : p.right) {
+ if (e.isA(SqlKind.GREATER_THAN) || e.isA(SqlKind.GREATER_THAN_OR_EQUAL)) {
+ terms.remove(e);
+ } else {
+ newBounds.add(e);
+ }
+ }
+ newBounds.add(term);
+ rangeTerms.put(ref.toString(), new Pair(r, newBounds.build()));
+ }
+ }
+ // Default
+ return null;
+ }
+
}
// End RexSimplify.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java b/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
new file mode 100644
index 0000000..7e2d26b
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/rex/RexTableInputRef.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.rex;
+
+import org.apache.calcite.rel.metadata.RelTableRef;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.sql.SqlKind;
+
+/**
+ * Variable which references a field of an input relational expression
+ */
+public class RexTableInputRef extends RexInputRef {
+
+ private final RelTableRef tableRef;
+
+ public RexTableInputRef(RelTableRef tableRef, int index, RelDataType type) {
+ super(index, type);
+ this.tableRef = tableRef;
+ this.digest = tableRef.toString() + ".$" + index;
+ }
+
+ //~ Methods ----------------------------------------------------------------
+
+ @Override public boolean equals(Object obj) {
+ return this == obj
+ || obj instanceof RexTableInputRef
+ && tableRef.equals(((RexTableInputRef) obj).tableRef)
+ && index == ((RexTableInputRef) obj).index;
+ }
+
+ @Override public int hashCode() {
+ return digest.hashCode();
+ }
+
+ public RelTableRef getTableRef() {
+ return tableRef;
+ }
+
+ public String getQualifiedName() {
+ return tableRef.getQualifiedName();
+ }
+
+ public int getIdentifier() {
+ return tableRef.getIdentifier();
+ }
+
+ public static RexTableInputRef of(RelTableRef tableRef, int index, RelDataType type) {
+ return new RexTableInputRef(tableRef, index, type);
+ }
+
+ public static RexTableInputRef of(RelTableRef tableRef, RexInputRef ref) {
+ return new RexTableInputRef(tableRef, ref.getIndex(), ref.getType());
+ }
+
+ @Override public <R> R accept(RexVisitor<R> visitor) {
+ return visitor.visitTableInputRef(this);
+ }
+
+ @Override public <R, P> R accept(RexBiVisitor<R, P> visitor, P arg) {
+ return visitor.visitTableInputRef(this, arg);
+ }
+
+ @Override public SqlKind getKind() {
+ return SqlKind.TABLE_INPUT_REF;
+ }
+}
+
+// End RexTableInputRef.java
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexUtil.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexUtil.java b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
index f174f41..0af3fc7 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -24,6 +24,7 @@ import org.apache.calcite.rel.RelFieldCollation;
import org.apache.calcite.rel.core.Filter;
import org.apache.calcite.rel.core.Join;
import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.metadata.RelTableRef;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeFamily;
@@ -490,6 +491,10 @@ public class RexUtil {
return false;
}
+ @Override public Boolean visitTableInputRef(RexTableInputRef ref) {
+ return false;
+ }
+
@Override public Boolean visitPatternFieldRef(RexPatternFieldRef fieldRef) {
return false;
}
@@ -821,6 +826,28 @@ public class RexUtil {
return false;
}
+ /**
+ * Returns whether a given tree contains any {link RexTableInputRef} nodes.
+ *
+ * @param node a RexNode tree
+ * @return first such node found or null if it there is no such node
+ */
+ public static RexTableInputRef containsTableInputRef(RexNode node) {
+ try {
+ RexVisitor<Void> visitor =
+ new RexVisitorImpl<Void>(true) {
+ public Void visitTableInputRef(RexTableInputRef inputRef) {
+ throw new Util.FoundOne(inputRef);
+ }
+ };
+ node.accept(visitor);
+ return null;
+ } catch (Util.FoundOne e) {
+ Util.swallow(e, null);
+ return (RexTableInputRef) e.getNode();
+ }
+ }
+
public static boolean isAtomic(RexNode expr) {
return (expr instanceof RexLiteral) || (expr instanceof RexVariable);
}
@@ -1840,6 +1867,61 @@ public class RexUtil {
}
}
+ public static RexNode swapTableReferences(final RexBuilder rexBuilder,
+ final RexNode node, final Map<RelTableRef, RelTableRef> tableMapping) {
+ return swapTableColumnReferences(rexBuilder, node, tableMapping, null);
+ }
+
+ public static RexNode swapColumnReferences(final RexBuilder rexBuilder,
+ final RexNode node, final Map<RexTableInputRef, Set<RexTableInputRef>> ec) {
+ return swapTableColumnReferences(rexBuilder, node, null, ec);
+ }
+
+ public static RexNode swapTableColumnReferences(final RexBuilder rexBuilder,
+ final RexNode node, final Map<RelTableRef, RelTableRef> tableMapping,
+ final Map<RexTableInputRef, Set<RexTableInputRef>> ec) {
+ RexShuttle visitor =
+ new RexShuttle() {
+ @Override public RexNode visitTableInputRef(RexTableInputRef inputRef) {
+ if (tableMapping != null) {
+ inputRef = new RexTableInputRef(
+ tableMapping.get(inputRef.getTableRef()),
+ inputRef.getIndex(),
+ inputRef.getType());
+ }
+ if (ec != null) {
+ Set<RexTableInputRef> s = ec.get(inputRef);
+ if (s != null) {
+ inputRef = s.iterator().next();
+ }
+ }
+ return inputRef;
+ }
+ };
+ return visitor.apply(node);
+ }
+
+ /**
+ * Gather all table references in input expressions.
+ *
+ * @param nodes expressions
+ * @return set of table references
+ */
+ public static Set<RelTableRef> gatherTableReferences(final List<RexNode> nodes) {
+ final Set<RelTableRef> occurrences = new HashSet<>();
+ RexVisitor<Void> visitor =
+ new RexVisitorImpl<Void>(true) {
+ @Override public Void visitTableInputRef(RexTableInputRef ref) {
+ occurrences.add(ref.getTableRef());
+ return super.visitTableInputRef(ref);
+ }
+ };
+ for (RexNode e : nodes) {
+ e.accept(visitor);
+ }
+ return occurrences;
+ }
+
//~ Inner Classes ----------------------------------------------------------
/**
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexVisitor.java b/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
index bf6dd99..a2a01d0 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexVisitor.java
@@ -48,6 +48,8 @@ public interface RexVisitor<R> {
R visitSubQuery(RexSubQuery subQuery);
+ R visitTableInputRef(RexTableInputRef fieldRef);
+
R visitPatternFieldRef(RexPatternFieldRef fieldRef);
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java b/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
index 4710f98..6e40a67 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexVisitorImpl.java
@@ -110,6 +110,10 @@ public class RexVisitorImpl<R> implements RexVisitor<R> {
return r;
}
+ @Override public R visitTableInputRef(RexTableInputRef ref) {
+ return null;
+ }
+
@Override public R visitPatternFieldRef(RexPatternFieldRef fieldRef) {
return null;
}
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/sql/SqlKind.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/sql/SqlKind.java b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
index 7a33772..07164b3 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -561,6 +561,13 @@ public enum SqlKind {
INPUT_REF,
/**
+ * Reference to an input field, with a qualified name and an identifier
+ *
+ * <p>(Only used at the RexNode level.)</p>
+ */
+ TABLE_INPUT_REF,
+
+ /**
* Reference to an input field, with pattern var as modifier
*
* <p>(Only used at the RexNode level.)</p>
http://git-wip-us.apache.org/repos/asf/calcite/blob/41b05d78/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index f5161b2..df2f2f4 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -43,6 +43,7 @@ import org.apache.calcite.linq4j.function.Predicate2;
import org.apache.calcite.linq4j.tree.FunctionExpression;
import org.apache.calcite.linq4j.tree.Primitive;
import org.apache.calcite.linq4j.tree.Types;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.AllPredicates;
import org.apache.calcite.rel.metadata.BuiltInMetadata.Collation;
import org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnOrigin;
import org.apache.calcite.rel.metadata.BuiltInMetadata.ColumnUniqueness;
@@ -50,9 +51,11 @@ import org.apache.calcite.rel.metadata.BuiltInMetadata.CumulativeCost;
import org.apache.calcite.rel.metadata.BuiltInMetadata.DistinctRowCount;
import org.apache.calcite.rel.metadata.BuiltInMetadata.Distribution;
import org.apache.calcite.rel.metadata.BuiltInMetadata.ExplainVisibility;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.ExpressionLineage;
import org.apache.calcite.rel.metadata.BuiltInMetadata.MaxRowCount;
import org.apache.calcite.rel.metadata.BuiltInMetadata.Memory;
import org.apache.calcite.rel.metadata.BuiltInMetadata.MinRowCount;
+import org.apache.calcite.rel.metadata.BuiltInMetadata.NodeTypes;
import org.apache.calcite.rel.metadata.BuiltInMetadata.NonCumulativeCost;
import org.apache.calcite.rel.metadata.BuiltInMetadata.Parallelism;
import org.apache.calcite.rel.metadata.BuiltInMetadata.PercentageOriginalRows;
@@ -361,6 +364,7 @@ public enum BuiltInMethod {
ImmutableBitSet.class, boolean.class),
COLLATIONS(Collation.class, "collations"),
DISTRIBUTION(Distribution.class, "distribution"),
+ NODE_TYPES(NodeTypes.class, "getNodeTypes"),
ROW_COUNT(RowCount.class, "getRowCount"),
MAX_ROW_COUNT(MaxRowCount.class, "getMaxRowCount"),
MIN_ROW_COUNT(MinRowCount.class, "getMinRowCount"),
@@ -371,9 +375,11 @@ public enum BuiltInMethod {
POPULATION_SIZE(PopulationSize.class, "getPopulationSize",
ImmutableBitSet.class),
COLUMN_ORIGIN(ColumnOrigin.class, "getColumnOrigins", int.class),
+ EXPRESSION_LINEAGE(ExpressionLineage.class, "getExpressionLineage", RexNode.class),
CUMULATIVE_COST(CumulativeCost.class, "getCumulativeCost"),
NON_CUMULATIVE_COST(NonCumulativeCost.class, "getNonCumulativeCost"),
PREDICATES(Predicates.class, "getPredicates"),
+ ALL_PREDICATES(AllPredicates.class, "getAllPredicates"),
EXPLAIN_VISIBILITY(ExplainVisibility.class, "isVisibleInExplain",
SqlExplainLevel.class),
SCALAR_EXECUTE1(Scalar.class, "execute", Context.class),