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 2015/07/17 02:10:13 UTC
[1/3] incubator-calcite git commit: [CALCITE-786] Detect if
materialized view can be used to rewrite a query in non-trivial cases (Amogh
Margoor)
Repository: incubator-calcite
Updated Branches:
refs/heads/master 220a08513 -> d38e6b1b9
[CALCITE-786] Detect if materialized view can be used to rewrite a query in non-trivial cases (Amogh Margoor)
Close apache/incubator-calcite#102
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/4a9b1939
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/4a9b1939
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/4a9b1939
Branch: refs/heads/master
Commit: 4a9b19390b2f9ab5a6d3bc2340c323d3a39cfa80
Parents: 220a085
Author: Amogh Margoor <am...@qubole.com>
Authored: Thu Jun 11 15:18:52 2015 +0530
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jul 16 13:22:39 2015 -0700
----------------------------------------------------------------------
.../MaterializedViewSubstitutionVisitor.java | 170 +++++++
.../org/apache/calcite/plan/RelOptUtil.java | 4 +-
.../calcite/plan/RexImplicationChecker.java | 377 ++++++++++++++++
.../calcite/plan/SubstitutionVisitor.java | 183 +++++---
.../apache/calcite/plan/VisitorDataContext.java | 186 ++++++++
.../calcite/plan/volcano/VolcanoPlanner.java | 9 +-
.../java/org/apache/calcite/rex/RexUtil.java | 105 +++++
.../org/apache/calcite/test/CalciteSuite.java | 1 +
.../calcite/test/MaterializationTest.java | 140 +++++-
.../calcite/test/RexImplicationCheckerTest.java | 448 +++++++++++++++++++
10 files changed, 1558 insertions(+), 65 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
new file mode 100644
index 0000000..e367f93
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
@@ -0,0 +1,170 @@
+/*
+ * 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.plan;
+
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.type.RelDataTypeField;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexShuttle;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.List;
+/**
+ * Substitutes part of a tree of relational expressions with another tree.
+ *
+ * <p>The call {@code new MaterializedSubstitutionVisitor(target, query).go(replacement))}
+ * will return {@code query} with every occurrence of {@code target} replaced
+ * by {@code replacement}.</p>
+ *
+ * <p>The following example shows how {@code MaterializedSubstitutionVisitor} can be used
+ * for materialized view recognition.</p>
+ *
+ * <ul>
+ * <li>query = SELECT a, c FROM t WHERE x = 5 AND b = 4</li>
+ * <li>target = SELECT a, b, c FROM t WHERE x = 5</li>
+ * <li>replacement = SELECT * FROM mv</li>
+ * <li>result = SELECT a, c FROM mv WHERE b = 4</li>
+ * </ul>
+ *
+ * <p>Note that {@code result} uses the materialized view table {@code mv} and a
+ * simplified condition {@code b = 4}.</p>
+ *
+ * <p>Uses a bottom-up matching algorithm. Nodes do not need to be identical.
+ * At each level, returns the residue.</p>
+ *
+ * <p>The inputs must only include the core relational operators:
+ * {@link org.apache.calcite.rel.logical.LogicalTableScan},
+ * {@link LogicalFilter},
+ * {@link LogicalProject},
+ * {@link org.apache.calcite.rel.logical.LogicalJoin},
+ * {@link LogicalUnion},
+ * {@link LogicalAggregate}.</p>
+ */
+public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
+
+ public MaterializedViewSubstitutionVisitor(RelNode target_, RelNode query_) {
+ super(target_, query_);
+ ImmutableList.Builder<UnifyRule> builder = new ImmutableList.Builder<UnifyRule>();
+ builder.addAll(this.unifyRules);
+ builder.add(ProjectToProjectUnifyRule1.INSTANCE);
+ this.unifyRules = builder.build();
+ }
+
+ public RelNode go(RelNode replacement_) {
+ return super.go(replacement_);
+ }
+
+ /**
+ * Project to Project Unify rule.
+ */
+
+ private static class ProjectToProjectUnifyRule1 extends AbstractUnifyRule {
+ public static final ProjectToProjectUnifyRule1 INSTANCE =
+ new ProjectToProjectUnifyRule1();
+
+ private ProjectToProjectUnifyRule1() {
+ super(operand(MutableProject.class, query(0)),
+ operand(MutableProject.class, target(0)), 1);
+ }
+
+ @Override
+ protected UnifyResult apply(UnifyRuleCall call) {
+ final MutableProject query = (MutableProject) call.query;
+
+ final List<RelDataTypeField> oldFieldList = query.getInput().getRowType().getFieldList();
+ final List<RelDataTypeField> newFieldList = call.target.getRowType().getFieldList();
+ List<RexNode> newProjects;
+ try {
+ newProjects = transformRex(query.getProjects(), oldFieldList, newFieldList);
+ } catch (MatchFailed e) {
+ return null;
+ }
+
+ final MutableProject newProject =
+ MutableProject.of(
+ query.getRowType(), call.target, newProjects);
+
+ final MutableRel newProject2 = MutableRels.strip(newProject);
+ return call.result(newProject2);
+ }
+
+ @Override
+ protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query,
+ MutableRel target) {
+ assert query instanceof MutableProject && target instanceof MutableProject;
+
+ if (queryOperand.matches(visitor, query)) {
+ if (targetOperand.matches(visitor, target)) {
+ return null;
+ } else if (targetOperand.isWeaker(visitor, target)) {
+
+ final MutableProject queryProject = (MutableProject) query;
+ if (queryProject.getInput() instanceof MutableFilter) {
+
+ final MutableFilter innerFilter = (MutableFilter) (queryProject.getInput());
+ RexNode newCondition;
+ try {
+ newCondition = transformRex(innerFilter.getCondition(),
+ innerFilter.getInput().getRowType().getFieldList(),
+ target.getRowType().getFieldList());
+ } catch (MatchFailed e) {
+ return null;
+ }
+ final MutableFilter newFilter = MutableFilter.of(target,
+ newCondition);
+
+ return visitor.new UnifyRuleCall(this, query, newFilter,
+ copy(visitor.getSlots(), slotCount));
+ }
+ }
+ }
+ return null;
+ }
+
+ private RexNode transformRex(
+ RexNode node,
+ final List<RelDataTypeField> oldFields,
+ final List<RelDataTypeField> newFields) {
+ List<RexNode> nodes = transformRex(ImmutableList.of(node), oldFields, newFields);
+ return nodes.get(0);
+ }
+
+ private List<RexNode> transformRex(
+ List<RexNode> nodes,
+ final List<RelDataTypeField> oldFields,
+ final List<RelDataTypeField> newFields) {
+ RexShuttle shuttle = new RexShuttle() {
+ @Override public RexNode visitInputRef(RexInputRef ref) {
+ RelDataTypeField f = oldFields.get(ref.getIndex());
+ for (int index = 0; index < newFields.size(); index++) {
+ RelDataTypeField newf = newFields.get(index);
+ if (f.getKey().equals(newf.getKey())
+ && f.getValue() == newf.getValue()) {
+ return new RexInputRef(index, f.getValue());
+ }
+ }
+ throw MatchFailed.INSTANCE;
+ }
+ };
+ return shuttle.apply(nodes);
+ }
+ }
+}
+
+// End MaterializedViewSubstitutionVisitor.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/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 5aa2177..c1f8af5 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -1199,7 +1199,7 @@ public abstract class RelOptUtil {
false);
}
- private static SqlKind reverse(SqlKind kind) {
+ public static SqlKind reverse(SqlKind kind) {
switch (kind) {
case GREATER_THAN:
return SqlKind.LESS_THAN;
@@ -1214,7 +1214,7 @@ public abstract class RelOptUtil {
}
}
- private static SqlOperator op(SqlKind kind, SqlOperator operator) {
+ public static SqlOperator op(SqlKind kind, SqlOperator operator) {
switch (kind) {
case EQUALS:
return SqlStdOperatorTable.EQUALS;
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
new file mode 100644
index 0000000..1eb019d
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
@@ -0,0 +1,377 @@
+/*
+ * 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.plan;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexExecutable;
+import org.apache.calcite.rex.RexExecutorImpl;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.rex.RexUtil;
+import org.apache.calcite.rex.RexVisitorImpl;
+import org.apache.calcite.sql.SqlKind;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.fun.SqlCastFunction;
+import org.apache.calcite.util.Pair;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+
+/** Checks if Condition X logically implies Condition Y
+ *
+ * <p>(x > 10) implies (x > 5)</p>
+ *
+ * <p>(y = 10) implies (y < 30 AND x > 30)</p>
+ */
+public class RexImplicationChecker {
+ final RexBuilder builder;
+ final RexExecutorImpl rexImpl;
+ final RelDataType rowType;
+
+ public RexImplicationChecker(
+ RexBuilder builder,
+ RexExecutorImpl rexImpl,
+ RelDataType rowType) {
+ this.builder = builder;
+ this.rexImpl = rexImpl;
+ this.rowType = rowType;
+ }
+
+ /**
+ * Checks if condition first implies (=>) condition second
+ * This reduces to SAT problem which is NP-Complete
+ * When func says first => second then it is definitely true
+ * It cannot prove if first doesnot imply second.
+ * @param first first condition
+ * @param second second condition
+ * @return true if it can prove first => second, otherwise false i.e.,
+ * it doesn't know if implication holds .
+ */
+ public boolean implies(RexNode first, RexNode second) {
+
+ // Validation
+ if (!validate(first, second)) {
+ return false;
+ }
+
+ RexCall firstCond = (RexCall) first;
+ RexCall secondCond = (RexCall) second;
+
+ // Get DNF
+ RexNode firstDnf = RexUtil.toDnf(builder, first);
+ RexNode secondDnf = RexUtil.toDnf(builder, second);
+
+ // Check Trivial Cases
+ if (firstDnf.isAlwaysFalse()
+ || secondDnf.isAlwaysTrue()) {
+ return true;
+ }
+
+ /** Decompose DNF into List of Conjunctions
+ *
+ * (x > 10 AND y > 30) OR (z > 90) will be converted to
+ * list of 2 conditions:
+ * 1. (x > 10 AND y > 30)
+ * 2. (z > 90)
+ *
+ */
+ List<RexNode> firstDnfs = RelOptUtil.disjunctions(firstDnf);
+ List<RexNode> secondDnfs = RelOptUtil.disjunctions(secondDnf);
+
+ for (RexNode f : firstDnfs) {
+ if (!f.isAlwaysFalse()) {
+ //Check if f implies atleast
+ // one of the conjunctions in list secondDnfs
+ boolean implyOneConjuntion = false;
+ for (RexNode s : secondDnfs) {
+ if (s.isAlwaysFalse()) { // f cannot imply s
+ continue;
+ }
+
+ if (impliesConjunction(f, s)) {
+ // Satisfies one of the condition, so lets
+ // move to next conjunction in firstDnfs
+ implyOneConjuntion = true;
+ break;
+ }
+ } //end of inner loop
+
+ // If f couldnot imply even one conjunction in
+ // secondDnfs, then final implication may be false
+ if (!implyOneConjuntion) {
+ return false;
+ }
+ }
+ } //end of outer loop
+
+ return true;
+ }
+
+ /** Checks if Conjunction first => Conjunction second**/
+ private boolean impliesConjunction(RexNode first, RexNode second) {
+
+ InputUsageFinder firstUsgFinder = new InputUsageFinder();
+ InputUsageFinder secondUsgFinder = new InputUsageFinder();
+
+ RexUtil.apply(firstUsgFinder, new ArrayList<RexNode>(), first);
+ RexUtil.apply(secondUsgFinder, new ArrayList<RexNode>(), second);
+
+ // Check Support
+ if (!checkSupport(firstUsgFinder, secondUsgFinder)) {
+ return false;
+ }
+
+ List<Pair<RexInputRef, RexNode>> usgList = new ArrayList<>();
+ for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator,
+ RexNode>> entry: firstUsgFinder.usageMap.entrySet()) {
+ final List<Pair<SqlOperator, RexNode>> list = entry.getValue().getUsageList();
+ usgList.add(Pair.of(entry.getKey(), list.get(0).getValue()));
+ }
+
+ /* Get the literals from first conjunction and execute second conjunction using them
+ * E.g., for x >30 => x > 10,
+ * we will replace x by 30 in second expression and execute it i.e., 30>10
+ * If it's true then we infer implication.
+ */
+ final DataContext dataValues = VisitorDataContext.getDataContext(rowType, usgList);
+
+ if (dataValues == null) {
+ return false;
+ }
+
+ ImmutableList<RexNode> constExps = ImmutableList.of(second);
+ final RexExecutable exec = rexImpl.getExecutable(builder,
+ constExps, rowType);
+
+ Object[] result;
+ exec.setDataContext(dataValues);
+ try {
+ result = exec.execute();
+ } catch (Exception e) {
+ // TODO: CheckSupport should not allow this exception to be thrown
+ // Need to monitor it and handle all the cases raising them.
+ return false;
+ }
+ return result != null && result.length == 1 && result[0] instanceof Boolean
+ && (Boolean) result[0];
+ }
+
+ /**
+ * Looks at the usage of variables in first and second conjunction to decide
+ * if this kind of expression is currently supported for proving first => second.
+ * 1. Variables should be used only once in both the conjunction against
+ * given set of operations only: >,<,<=,>=,=,!=
+ * 2. All the variables used in second condition should be used even in the first.
+ * 3. If operator used for variable in first is op1 and op2 for second, then we support
+ * these combination for conjunction (op1, op2) then op1, op2 belongs to
+ * one of the following sets:
+ * a. (<,<=) X (<,<=) , X represents cartesian product
+ * b. (>/>=) X (>,>=)
+ * c. (=) X (>,>=,<,<=,=,!=)
+ * d. (!=, =)
+ * @return true, if input usage pattern is supported. Otherwise, false.
+ */
+ private boolean checkSupport(
+ InputUsageFinder firstUsgFinder,
+ InputUsageFinder secondUsgFinder) {
+ Map<RexInputRef, InputRefUsage<SqlOperator,
+ RexNode>> firstUsgMap = firstUsgFinder.usageMap;
+ Map<RexInputRef, InputRefUsage<SqlOperator,
+ RexNode>> secondUsgMap = secondUsgFinder.usageMap;
+
+ for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator,
+ RexNode>> entry: firstUsgMap.entrySet()) {
+ if (entry.getValue().usageCount > 1) {
+ return false;
+ }
+ }
+
+ for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator,
+ RexNode>> entry: secondUsgMap.entrySet()) {
+ final InputRefUsage<SqlOperator, RexNode> secondUsage = entry.getValue();
+ if (secondUsage.getUsageCount() > 1
+ || secondUsage.getUsageList().size() != 1) {
+ return false;
+ }
+
+ final InputRefUsage<SqlOperator, RexNode> firstUsage = firstUsgMap.get(entry.getKey());
+ if (firstUsage == null
+ || firstUsage.getUsageList().size() != 1) {
+ return false;
+ }
+
+ final Pair<SqlOperator, RexNode> fUse = firstUsage.getUsageList().get(0);
+ final Pair<SqlOperator, RexNode> sUse = secondUsage.getUsageList().get(0);
+
+ final SqlKind fkind = fUse.getKey().getKind();
+
+ if (fkind != SqlKind.EQUALS) {
+ switch (sUse.getKey().getKind()) {
+ case GREATER_THAN:
+ case GREATER_THAN_OR_EQUAL:
+ if (!(fkind == SqlKind.GREATER_THAN)
+ && !(fkind == SqlKind.GREATER_THAN_OR_EQUAL)) {
+ return false;
+ }
+ break;
+ case LESS_THAN:
+ case LESS_THAN_OR_EQUAL:
+ if (!(fkind == SqlKind.LESS_THAN)
+ && !(fkind == SqlKind.LESS_THAN_OR_EQUAL)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean validate(RexNode first, RexNode second) {
+ if (first == null || second == null) {
+ return false;
+ }
+ if (!(first instanceof RexCall)
+ || !(second instanceof RexCall)) {
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ * Visitor which builds a Usage Map of inputs used by an expression.
+ * E.g: for x >10 AND y < 20 AND x =40, Usage Map would look like:
+ * key:x value: {(>,10),(=,40), usageCount = 2}
+ * key:y value: {(>,20),usageCount=1}
+ */
+ private static class InputUsageFinder extends RexVisitorImpl<Void> {
+ public final Map<RexInputRef, InputRefUsage<SqlOperator,
+ RexNode>> usageMap = new HashMap<>();
+
+ public InputUsageFinder() {
+ super(true);
+ }
+
+ public Void visitInputRef(RexInputRef inputRef) {
+ InputRefUsage<SqlOperator,
+ RexNode> inputRefUse = getUsageMap(inputRef);
+ inputRefUse.incrUsage();
+ return null;
+ }
+
+ @Override public Void visitCall(RexCall call) {
+ switch (call.getOperator().getKind()) {
+ case GREATER_THAN:
+ case GREATER_THAN_OR_EQUAL:
+ case LESS_THAN:
+ case LESS_THAN_OR_EQUAL:
+ case EQUALS:
+ case NOT_EQUALS:
+ updateUsage(call);
+ break;
+ default:
+ }
+ return super.visitCall(call);
+ }
+
+ private void updateUsage(RexCall call) {
+ final List<RexNode> operands = call.getOperands();
+ RexNode first = removeCast(operands.get(0));
+ RexNode second = removeCast(operands.get(1));
+
+ if (first.isA(SqlKind.INPUT_REF)
+ && second.isA(SqlKind.LITERAL)) {
+ updateUsage(call.getOperator(), (RexInputRef) first, second);
+ }
+
+ if (first.isA(SqlKind.LITERAL)
+ && second.isA(SqlKind.INPUT_REF)) {
+ updateUsage(reverse(call.getOperator()), (RexInputRef) second, first);
+ }
+ }
+
+ private SqlOperator reverse(SqlOperator op) {
+ return RelOptUtil.op(
+ RelOptUtil.reverse(op.getKind()), op);
+ }
+
+ private static RexNode removeCast(RexNode inputRef) {
+ if (inputRef instanceof RexCall) {
+ final RexCall castedRef = (RexCall) inputRef;
+ final SqlOperator operator = castedRef.getOperator();
+ if (operator instanceof SqlCastFunction) {
+ inputRef = castedRef.getOperands().get(0);
+ }
+ }
+ return inputRef;
+ }
+
+ private void updateUsage(SqlOperator op, RexInputRef inputRef, RexNode literal) {
+ InputRefUsage<SqlOperator,
+ RexNode> inputRefUse = getUsageMap(inputRef);
+ Pair<SqlOperator, RexNode> use = Pair.of(op, literal);
+ inputRefUse.getUsageList().add(use);
+ }
+
+ private InputRefUsage<SqlOperator, RexNode> getUsageMap(RexInputRef rex) {
+ InputRefUsage<SqlOperator, RexNode> inputRefUse = usageMap.get(rex);
+ if (inputRefUse == null) {
+ inputRefUse = new InputRefUsage<>();
+ usageMap.put(rex, inputRefUse);
+ }
+
+ return inputRefUse;
+ }
+ }
+
+ /**
+ * DataStructure to store usage of InputRefs in expression
+ */
+
+ private static class InputRefUsage<T1, T2> {
+ private final List<Pair<T1, T2>> usageList =
+ new ArrayList<Pair<T1, T2>>();
+ private int usageCount = 0;
+
+ public InputRefUsage() {}
+
+ public int getUsageCount() {
+ return usageCount;
+ }
+
+ public void incrUsage() {
+ usageCount++;
+ }
+
+ public List<Pair<T1, T2>> getUsageList() {
+ return usageList;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index b1c89e6..62b2d9d 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -42,6 +42,7 @@ import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeField;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexExecutorImpl;
import org.apache.calcite.rex.RexInputRef;
import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
@@ -144,15 +145,7 @@ public class SubstitutionVisitor {
private static final Equivalence<List<?>> PAIRWISE_STRING_EQUIVALENCE =
(Equivalence) STRING_EQUIVALENCE.pairwise();
- private static final List<UnifyRule> RULES =
- ImmutableList.<UnifyRule>of(
-// TrivialRule.INSTANCE,
- ProjectToProjectUnifyRule.INSTANCE,
- FilterToProjectUnifyRule.INSTANCE,
-// ProjectToFilterUnifyRule.INSTANCE,
- FilterToFilterUnifyRule.INSTANCE,
- AggregateToAggregateUnifyRule.INSTANCE,
- AggregateOnProjectToAggregateUnifyRule.INSTANCE);
+ protected static List<UnifyRule> unifyRules;
private static final Map<Pair<Class, Class>, List<UnifyRule>> RULE_MAP =
new HashMap<>();
@@ -208,6 +201,28 @@ public class SubstitutionVisitor {
visitor.go(query);
allNodes.removeAll(parents);
queryLeaves = ImmutableList.copyOf(allNodes);
+ initUnifyRules();
+ initRuleMap();
+ }
+
+ public void initUnifyRules() {
+ unifyRules =
+ ImmutableList.<UnifyRule>of(
+// TrivialRule.INSTANCE,
+ ProjectToProjectUnifyRule.INSTANCE,
+ FilterToProjectUnifyRule.INSTANCE,
+// ProjectToFilterUnifyRule.INSTANCE,
+ FilterToFilterUnifyRule.INSTANCE,
+ AggregateToAggregateUnifyRule.INSTANCE,
+ AggregateOnProjectToAggregateUnifyRule.INSTANCE);
+ }
+
+ public void initRuleMap() {
+ this.RULE_MAP.clear();
+ }
+
+ public MutableRel[] getSlots() {
+ return slots;
}
private static MutableRel toMutable(RelNode rel) {
@@ -457,7 +472,8 @@ public class SubstitutionVisitor {
final UnifyResult result = rule.apply(call);
if (result != null) {
++count;
- result.call.query.replaceInParent(result.result);
+ MutableRel parent = result.call.query.replaceInParent(result.result);
+
// Replace previous equivalents with new equivalents, higher up
// the tree.
for (int i = 0; i < rule.slotCount; i++) {
@@ -612,7 +628,7 @@ public class SubstitutionVisitor {
if (list == null) {
final ImmutableList.Builder<UnifyRule> builder =
ImmutableList.builder();
- for (UnifyRule rule : RULES) {
+ for (UnifyRule rule : unifyRules) {
//noinspection unchecked
if (mightMatch(rule, queryClass, targetClass)) {
builder.add(rule);
@@ -631,8 +647,8 @@ public class SubstitutionVisitor {
}
/** Exception thrown to exit a matcher. Not really an error. */
- private static class MatchFailed extends ControlFlowException {
- static final MatchFailed INSTANCE = new MatchFailed();
+ protected static class MatchFailed extends ControlFlowException {
+ public static final MatchFailed INSTANCE = new MatchFailed();
}
/** Rule that attempts to match a query relational expression
@@ -641,7 +657,7 @@ public class SubstitutionVisitor {
* <p>The rule declares the query and target types; this allows the
* engine to fire only a few rules in a given context.</p>
*/
- private abstract static class UnifyRule {
+ protected abstract static class UnifyRule {
protected final int slotCount;
protected final Operand queryOperand;
protected final Operand targetOperand;
@@ -679,9 +695,9 @@ public class SubstitutionVisitor {
*
* @param call Input parameters
*/
- abstract UnifyResult apply(UnifyRuleCall call);
+ protected abstract UnifyResult apply(UnifyRuleCall call);
- UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query,
+ protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query,
MutableRel target) {
if (queryOperand.matches(visitor, query)) {
if (targetOperand.matches(visitor, target)) {
@@ -692,7 +708,7 @@ public class SubstitutionVisitor {
return null;
}
- private <E> ImmutableList<E> copy(E[] slots, int slotCount) {
+ protected <E> ImmutableList<E> copy(E[] slots, int slotCount) {
// Optimize if there are 0 or 1 slots.
switch (slotCount) {
case 0:
@@ -708,11 +724,11 @@ public class SubstitutionVisitor {
/**
* Arguments to an application of a {@link UnifyRule}.
*/
- private class UnifyRuleCall {
- final UnifyRule rule;
- final MutableRel query;
- final MutableRel target;
- final ImmutableList<MutableRel> slots;
+ protected class UnifyRuleCall {
+ protected final UnifyRule rule;
+ public final MutableRel query;
+ public final MutableRel target;
+ protected final ImmutableList<MutableRel> slots;
public UnifyRuleCall(UnifyRule rule, MutableRel query, MutableRel target,
ImmutableList<MutableRel> slots) {
@@ -722,7 +738,7 @@ public class SubstitutionVisitor {
this.slots = Preconditions.checkNotNull(slots);
}
- UnifyResult result(MutableRel result) {
+ public UnifyResult result(MutableRel result) {
assert MutableRels.contains(result, target);
assert MutableRels.equalType("result", result, "query", query, true);
MutableRel replace = replacementMap.get(target);
@@ -753,7 +769,7 @@ public class SubstitutionVisitor {
* generated a {@code result} that is equivalent to {@code query} and
* contains {@code target}.
*/
- private static class UnifyResult {
+ protected static class UnifyResult {
private final UnifyRuleCall call;
// equivalent to "query", contains "result"
private final MutableRel result;
@@ -766,7 +782,7 @@ public class SubstitutionVisitor {
}
/** Abstract base class for implementing {@link UnifyRule}. */
- private abstract static class AbstractUnifyRule extends UnifyRule {
+ protected abstract static class AbstractUnifyRule extends UnifyRule {
public AbstractUnifyRule(Operand queryOperand, Operand targetOperand,
int slotCount) {
super(slotCount, queryOperand, targetOperand);
@@ -787,24 +803,24 @@ public class SubstitutionVisitor {
}
/** Creates an operand with given inputs. */
- static Operand operand(Class<? extends MutableRel> clazz,
+ protected static Operand operand(Class<? extends MutableRel> clazz,
Operand... inputOperands) {
return new InternalOperand(clazz, ImmutableList.copyOf(inputOperands));
}
/** Creates an operand that doesn't check inputs. */
- static Operand any(Class<? extends MutableRel> clazz) {
+ protected static Operand any(Class<? extends MutableRel> clazz) {
return new AnyOperand(clazz);
}
/** Creates an operand that matches a relational expression in the query. */
- static Operand query(int ordinal) {
+ protected static Operand query(int ordinal) {
return new QueryOperand(ordinal);
}
/** Creates an operand that matches a relational expression in the
* target. */
- static Operand target(int ordinal) {
+ protected static Operand target(int ordinal) {
return new TargetOperand(ordinal);
}
}
@@ -860,6 +876,7 @@ public class SubstitutionVisitor {
}
}
+
/** Implementation of {@link UnifyRule} that matches a {@link MutableFilter}
* to a {@link MutableProject}. */
private static class FilterToProjectUnifyRule extends AbstractUnifyRule {
@@ -899,7 +916,7 @@ public class SubstitutionVisitor {
}
}
- private MutableRel invert(List<Pair<RexNode, String>> namedProjects,
+ protected MutableRel invert(List<Pair<RexNode, String>> namedProjects,
MutableRel input,
RexShuttle shuttle) {
if (LOGGER.isLoggable(Level.FINER)) {
@@ -924,7 +941,7 @@ public class SubstitutionVisitor {
return MutableProject.of(input, exprList, Pair.right(namedProjects));
}
- private MutableRel invert(MutableRel model, MutableRel input,
+ protected MutableRel invert(MutableRel model, MutableRel input,
MutableProject project) {
if (LOGGER.isLoggable(Level.FINER)) {
LOGGER.finer("SubstitutionVisitor: invert:\n"
@@ -1194,7 +1211,7 @@ public class SubstitutionVisitor {
/** Builds a shuttle that stores a list of expressions, and can map incoming
* expressions to references to them. */
- private static RexShuttle getRexShuttle(MutableProject target) {
+ protected static RexShuttle getRexShuttle(MutableProject target) {
final Map<String, Integer> map = new HashMap<>();
for (RexNode e : target.getProjects()) {
map.put(e.toString(), map.size());
@@ -1258,7 +1275,7 @@ public class SubstitutionVisitor {
* For this reason, you should use {@code MutableRel} for short-lived
* operations, and transcribe back to {@code RelNode} when you are done.</p>
*/
- private abstract static class MutableRel {
+ protected abstract static class MutableRel {
MutableRel parent;
int ordinalInParent;
public final RelOptCluster cluster;
@@ -1317,6 +1334,8 @@ public class SubstitutionVisitor {
@Override public final String toString() {
return deep();
}
+
+ public MutableRel getParent() { return parent; }
}
/** Implementation of {@link MutableRel} whose only purpose is to have a
@@ -1337,7 +1356,7 @@ public class SubstitutionVisitor {
/** Abstract base class for implementations of {@link MutableRel} that have
* no inputs. */
- private abstract static class MutableLeafRel extends MutableRel {
+ protected abstract static class MutableLeafRel extends MutableRel {
protected final RelNode rel;
MutableLeafRel(MutableRelType type, RelNode rel) {
@@ -1359,7 +1378,7 @@ public class SubstitutionVisitor {
}
/** Mutable equivalent of {@link SingleRel}. */
- private abstract static class MutableSingleRel extends MutableRel {
+ protected abstract static class MutableSingleRel extends MutableRel {
protected MutableRel input;
MutableSingleRel(MutableRelType type, RelDataType rowType,
@@ -1396,7 +1415,7 @@ public class SubstitutionVisitor {
/** Mutable equivalent of
* {@link org.apache.calcite.rel.logical.LogicalTableScan}. */
- private static class MutableScan extends MutableLeafRel {
+ protected static class MutableScan extends MutableLeafRel {
private MutableScan(TableScan rel) {
super(MutableRelType.SCAN, rel);
}
@@ -1422,7 +1441,7 @@ public class SubstitutionVisitor {
}
/** Mutable equivalent of {@link org.apache.calcite.rel.core.Values}. */
- private static class MutableValues extends MutableLeafRel {
+ protected static class MutableValues extends MutableLeafRel {
private MutableValues(Values rel) {
super(MutableRelType.VALUES, rel);
}
@@ -1449,7 +1468,7 @@ public class SubstitutionVisitor {
/** Mutable equivalent of
* {@link org.apache.calcite.rel.logical.LogicalProject}. */
- private static class MutableProject extends MutableSingleRel {
+ protected static class MutableProject extends MutableSingleRel {
private final List<RexNode> projects;
private MutableProject(RelDataType rowType, MutableRel input,
@@ -1459,7 +1478,7 @@ public class SubstitutionVisitor {
assert RexUtil.compatibleTypes(projects, rowType, true);
}
- static MutableProject of(RelDataType rowType, MutableRel input,
+ public static MutableProject of(RelDataType rowType, MutableRel input,
List<RexNode> projects) {
return new MutableProject(rowType, input, projects);
}
@@ -1467,7 +1486,7 @@ public class SubstitutionVisitor {
/** Equivalent to
* {@link RelOptUtil#createProject(org.apache.calcite.rel.RelNode, java.util.List, java.util.List)}
* for {@link MutableRel}. */
- static MutableRel of(MutableRel child, List<RexNode> exprList,
+ public static MutableRel of(MutableRel child, List<RexNode> exprList,
List<String> fieldNameList) {
final RelDataType rowType =
RexUtil.createStructType(child.cluster.getTypeFactory(), exprList,
@@ -1512,7 +1531,7 @@ public class SubstitutionVisitor {
/** Mutable equivalent of
* {@link org.apache.calcite.rel.logical.LogicalFilter}. */
- private static class MutableFilter extends MutableSingleRel {
+ protected static class MutableFilter extends MutableSingleRel {
private final RexNode condition;
private MutableFilter(MutableRel input, RexNode condition) {
@@ -1520,7 +1539,7 @@ public class SubstitutionVisitor {
this.condition = condition;
}
- static MutableFilter of(MutableRel input, RexNode condition) {
+ public static MutableFilter of(MutableRel input, RexNode condition) {
return new MutableFilter(input, condition);
}
@@ -1547,7 +1566,7 @@ public class SubstitutionVisitor {
/** Mutable equivalent of
* {@link org.apache.calcite.rel.logical.LogicalAggregate}. */
- private static class MutableAggregate extends MutableSingleRel {
+ protected static class MutableAggregate extends MutableSingleRel {
public final boolean indicator;
private final ImmutableBitSet groupSet;
private final ImmutableList<ImmutableBitSet> groupSets;
@@ -1611,7 +1630,7 @@ public class SubstitutionVisitor {
}
/** Mutable equivalent of {@link org.apache.calcite.rel.core.Sort}. */
- private static class MutableSort extends MutableSingleRel {
+ protected static class MutableSort extends MutableSingleRel {
private final RelCollation collation;
private final RexNode offset;
private final RexNode fetch;
@@ -1655,7 +1674,7 @@ public class SubstitutionVisitor {
}
/** Base class for set-operations. */
- private abstract static class MutableSetOp extends MutableRel {
+ protected abstract static class MutableSetOp extends MutableRel {
protected final List<MutableRel> inputs;
private MutableSetOp(RelOptCluster cluster, RelDataType rowType,
@@ -1681,7 +1700,7 @@ public class SubstitutionVisitor {
/** Mutable equivalent of
* {@link org.apache.calcite.rel.logical.LogicalUnion}. */
- private static class MutableUnion extends MutableSetOp {
+ protected static class MutableUnion extends MutableSetOp {
public boolean all;
private MutableUnion(RelOptCluster cluster, RelDataType rowType,
@@ -1820,7 +1839,7 @@ public class SubstitutionVisitor {
}
/** Utilities for dealing with {@link MutableRel}s. */
- private static class MutableRels {
+ protected static class MutableRels {
public static boolean contains(MutableRel ancestor,
final MutableRel target) {
if (ancestor.equals(target)) {
@@ -1951,7 +1970,7 @@ public class SubstitutionVisitor {
}
/** Visitor that prints an indented tree of {@link MutableRel}s. */
- private static class MutableRelDumper extends MutableRelVisitor {
+ protected static class MutableRelDumper extends MutableRelVisitor {
private final StringBuilder buf = new StringBuilder();
private int level;
@@ -1975,14 +1994,18 @@ public class SubstitutionVisitor {
}
/** Operand to a {@link UnifyRule}. */
- private abstract static class Operand {
+ protected abstract static class Operand {
protected final Class<? extends MutableRel> clazz;
protected Operand(Class<? extends MutableRel> clazz) {
this.clazz = clazz;
}
- abstract boolean matches(SubstitutionVisitor visitor, MutableRel rel);
+ public abstract boolean matches(SubstitutionVisitor visitor, MutableRel rel);
+
+ public boolean isWeaker(SubstitutionVisitor visitor, MutableRel rel) {
+ return false;
+ }
}
/** Operand to a {@link UnifyRule} that matches a relational expression of a
@@ -1995,11 +2018,15 @@ public class SubstitutionVisitor {
this.inputs = inputs;
}
- @Override boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
+ @Override public boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
return clazz.isInstance(rel)
&& allMatch(visitor, inputs, rel.getInputs());
}
+ @Override public boolean isWeaker(SubstitutionVisitor visitor, MutableRel rel) {
+ return clazz.isInstance(rel)
+ && allWeaker(visitor, inputs, rel.getInputs());
+ }
private static boolean allMatch(SubstitutionVisitor visitor,
List<Operand> operands, List<MutableRel> rels) {
if (operands.size() != rels.size()) {
@@ -2012,6 +2039,20 @@ public class SubstitutionVisitor {
}
return true;
}
+
+ private static boolean allWeaker(
+ SubstitutionVisitor visitor,
+ List<Operand> operands, List<MutableRel> rels) {
+ if (operands.size() != rels.size()) {
+ return false;
+ }
+ for (Pair<Operand, MutableRel> pair : Pair.zip(operands, rels)) {
+ if (!pair.left.isWeaker(visitor, pair.right)) {
+ return false;
+ }
+ }
+ return true;
+ }
}
/** Operand to a {@link UnifyRule} that matches a relational expression of a
@@ -2021,7 +2062,7 @@ public class SubstitutionVisitor {
super(clazz);
}
- @Override boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
+ @Override public boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
return clazz.isInstance(rel);
}
}
@@ -2042,7 +2083,7 @@ public class SubstitutionVisitor {
this.ordinal = ordinal;
}
- @Override boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
+ @Override public boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
visitor.slots[ordinal] = rel;
return true;
}
@@ -2058,11 +2099,45 @@ public class SubstitutionVisitor {
this.ordinal = ordinal;
}
- @Override boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
+ @Override public boolean matches(SubstitutionVisitor visitor, MutableRel rel) {
final MutableRel rel0 = visitor.slots[ordinal];
assert rel0 != null : "QueryOperand should have been called first";
return rel0 == rel || visitor.equivalents.get(rel0).contains(rel);
}
+
+ @Override public boolean isWeaker(SubstitutionVisitor visitor, MutableRel rel) {
+ final MutableRel rel0 = visitor.slots[ordinal];
+ assert rel0 != null : "QueryOperand should have been called first";
+
+ if (rel0 == rel || visitor.equivalents.get(rel0).contains(rel)) {
+ return false;
+ }
+
+ if (!(rel0 instanceof MutableFilter)
+ || !(rel instanceof MutableFilter)) {
+ return false;
+ }
+
+ if (!rel.getRowType().equals(rel0.getRowType())) {
+ return false;
+ }
+
+ final MutableRel rel0input = ((MutableFilter) rel0).getInput();
+ final MutableRel relinput = ((MutableFilter) rel).getInput();
+ if (rel0input != relinput
+ && !visitor.equivalents.get(rel0input).contains(relinput)) {
+ return false;
+ }
+
+ RexExecutorImpl rexImpl =
+ (RexExecutorImpl) (rel.cluster.getPlanner().getExecutor());
+ RexImplicationChecker rexImplicationChecker = new RexImplicationChecker(
+ rel.cluster.getRexBuilder(),
+ rexImpl, rel.getRowType());
+
+ return rexImplicationChecker.implies(((MutableFilter) rel0).getCondition(),
+ ((MutableFilter) rel).getCondition());
+ }
}
/** Visitor that counts how many {@link QueryOperand} and
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
new file mode 100644
index 0000000..a941347
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
@@ -0,0 +1,186 @@
+/*
+ * 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.plan;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.adapter.java.JavaTypeFactory;
+import org.apache.calcite.linq4j.QueryProvider;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.logical.LogicalFilter;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.fun.SqlCastFunction;
+import org.apache.calcite.util.NlsString;
+import org.apache.calcite.util.Pair;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.util.Calendar;
+import java.util.List;
+
+/**
+ * DataContext for evaluating an RexExpression
+ */
+public class VisitorDataContext implements DataContext {
+ private final Object[] values;
+
+ public VisitorDataContext(Object[] values) {
+ this.values = values;
+ }
+
+ public SchemaPlus getRootSchema() {
+ throw new RuntimeException("Unsupported");
+ }
+
+ public JavaTypeFactory getTypeFactory() {
+ throw new RuntimeException("Unsupported");
+ }
+
+ public QueryProvider getQueryProvider() {
+ throw new RuntimeException("Unsupported");
+ }
+
+ public Object get(String name) {
+ if (name.equals("inputRecord")) {
+ return values;
+ } else {
+ return null;
+ }
+ }
+ public static DataContext getDataContext(RelNode targetRel, LogicalFilter queryRel) {
+ return getDataContext(targetRel.getRowType(), queryRel.getCondition());
+ }
+
+ public static DataContext getDataContext(RelDataType rowType, RexNode rex) {
+ int size = rowType.getFieldList().size();
+ Object[] values = new Object[size];
+ List<RexNode> operands = ((RexCall) rex).getOperands();
+ final RexNode firstOperand = operands.get(0);
+ final RexNode secondOperand = operands.get(1);
+ final Pair<Integer, ? extends Object> value = getValue(firstOperand, secondOperand);
+ if (value != null) {
+ int index = value.getKey();
+ values[index] = value.getValue();
+ return new VisitorDataContext(values);
+ } else {
+ return null;
+ }
+ }
+
+ public static DataContext getDataContext(RelDataType rowType, List<Pair<RexInputRef,
+ RexNode>> usgList) {
+ int size = rowType.getFieldList().size();
+ Object[] values = new Object[size];
+ for (Pair<RexInputRef, RexNode> elem: usgList) {
+ Pair<Integer, ? extends Object> value = getValue(elem.getKey(), elem.getValue());
+ if (value == null) {
+ return null;
+ }
+ int index = value.getKey();
+ values[index] = value.getValue();
+ }
+ return new VisitorDataContext(values);
+ }
+
+ public static Pair<Integer, ? extends Object> getValue(RexNode inputRef, RexNode literal) {
+ inputRef = removeCast(inputRef);
+ literal = removeCast(literal);
+
+ if (inputRef instanceof RexInputRef
+ && literal instanceof RexLiteral) {
+ Integer index = ((RexInputRef) inputRef).getIndex();
+ Object value = ((RexLiteral) literal).getValue();
+ final RelDataType type = inputRef.getType();
+
+ switch (type.getSqlTypeName()) {
+ case INTEGER:
+ if (value instanceof BigDecimal) {
+ final Integer intValue = new Integer(((BigDecimal) value).intValue());
+ return Pair.of(index, intValue);
+ }
+ case DOUBLE:
+ if (value instanceof BigDecimal) {
+ return Pair.of(index,
+ new Double(((BigDecimal) value).doubleValue()));
+ }
+ case REAL:
+ if (value instanceof BigDecimal) {
+ return Pair.of(index,
+ new Float(((BigDecimal) value).floatValue()));
+ }
+ case BIGINT:
+ if (value instanceof BigDecimal) {
+ return Pair.of(index,
+ new Long(((BigDecimal) value).longValue()));
+ }
+ case SMALLINT:
+ if (value instanceof BigDecimal) {
+ return Pair.of(index,
+ new Short(((BigDecimal) value).shortValue()));
+ }
+ case TINYINT:
+ if (value instanceof BigDecimal) {
+ return Pair.of(index,
+ new Short(((BigDecimal) value).byteValue()));
+ }
+ case DECIMAL:
+ if (value instanceof BigDecimal) {
+ return Pair.of(index, value);
+ }
+ case DATE:
+ if (value instanceof NlsString) {
+ value = ((RexLiteral) literal).getValue2();
+ final Date dateValue = Date.valueOf((String) value);
+ return Pair.of(index, dateValue);
+ } else if (value instanceof Calendar) {
+ final long timeInMillis = ((Calendar) value).getTimeInMillis();
+ return Pair.of(index, new Date(timeInMillis));
+ }
+ case CHAR:
+ if (value instanceof NlsString) {
+ // TODO: Support coallation. Not supported in {@link #NlsString} compare too.
+ final NlsString nl = (NlsString) value;
+ Character c = new Character(nl.getValue().charAt(0));
+ return Pair.of(index, c);
+ }
+ default:
+ //TODO: Support few more supported cases
+ return Pair.of(index, value);
+ }
+ }
+
+ //Unsupported Arguments
+ return null;
+ }
+
+ private static RexNode removeCast(RexNode inputRef) {
+ if (inputRef instanceof RexCall) {
+ final RexCall castedRef = (RexCall) inputRef;
+ final SqlOperator operator = castedRef.getOperator();
+ if (operator instanceof SqlCastFunction) {
+ inputRef = castedRef.getOperands().get(0);
+ }
+ }
+ return inputRef;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index 3bb3a16..6634cf3 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -23,6 +23,7 @@ import org.apache.calcite.plan.AbstractRelOptPlanner;
import org.apache.calcite.plan.Context;
import org.apache.calcite.plan.Convention;
import org.apache.calcite.plan.ConventionTraitDef;
+import org.apache.calcite.plan.MaterializedViewSubstitutionVisitor;
import org.apache.calcite.plan.RelOptCost;
import org.apache.calcite.plan.RelOptCostFactory;
import org.apache.calcite.plan.RelOptLattice;
@@ -38,7 +39,6 @@ import org.apache.calcite.plan.RelOptUtil;
import org.apache.calcite.plan.RelTrait;
import org.apache.calcite.plan.RelTraitDef;
import org.apache.calcite.plan.RelTraitSet;
-import org.apache.calcite.plan.SubstitutionVisitor;
import org.apache.calcite.plan.hep.HepPlanner;
import org.apache.calcite.plan.hep.HepProgram;
import org.apache.calcite.plan.hep.HepProgramBuilder;
@@ -365,6 +365,9 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
// query. If that is possible, register the remnant query as equivalent
// to the root.
//
+
+ // This call modifies originalRoot. Doesn't look like originalRoot should be mutable though.
+ // Need to check.
RelNode sub = substitute(originalRoot, materialization);
if (sub != null) {
// TODO: try to substitute other materializations in the remnant.
@@ -407,8 +410,8 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
hepPlanner.setRoot(root);
root = hepPlanner.findBestExp();
- return new SubstitutionVisitor(target, root)
- .go(materialization.tableRel);
+ return new MaterializedViewSubstitutionVisitor(target, root)
+ .go(materialization.tableRel);
}
private void useApplicableMaterializations() {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/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 ee384e1..5347855 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java
@@ -1024,6 +1024,38 @@ public class RexUtil {
return new CnfHelper(rexBuilder).toCnf(rex);
}
+ /** Converts an expression to disjunctive normal form (DNF).
+ *
+ * <p>DNF: It is a form of logical formula which is disjunction of conjunctive clauses</p>
+ *
+ * <p>All logicl formulas can be converted into DNF.</p>
+ *
+ * <p>The following expression is in DNF:
+ *
+ * <blockquote>(a AND b) OR (c AND d)</blockquote>
+ *
+ * <p>The following expression is not in CNF:
+ *
+ * <blockquote>(a OR b) AND c</blockquote>
+ *
+ * but can be converted to DNF:
+ *
+ * <blockquote>(a AND c) OR (b AND c)</blockquote>
+ *
+ * <p>The following expression is not in CNF:
+ *
+ * <blockquote>NOT (a OR NOT b)</blockquote>
+ *
+ * but can be converted to DNF by applying de Morgan's theorem:
+ *
+ * <blockquote>NOT a AND b</blockquote>
+ *
+ * <p>Expressions not involving AND, OR or NOT at the top level are in DNF.
+ */
+ public static RexNode toDnf(RexBuilder rexBuilder, RexNode rex) {
+ return new DnfHelper(rexBuilder).toDnf(rex);
+ }
+
/**
* Returns whether an operator is associative. AND is associative,
* which means that "(x AND y) and z" is equivalent to "x AND (y AND z)".
@@ -1723,6 +1755,79 @@ public class RexUtil {
}
}
+ /** Helps {@link org.apache.calcite.rex.RexUtil#toDnf}. */
+ private static class DnfHelper {
+ final RexBuilder rexBuilder;
+
+ private DnfHelper(RexBuilder rexBuilder) {
+ this.rexBuilder = rexBuilder;
+ }
+
+ public RexNode toDnf(RexNode rex) {
+ final List<RexNode> operands;
+ switch (rex.getKind()) {
+ case AND:
+ operands = flattenAnd(((RexCall) rex).getOperands());
+ final RexNode head = operands.get(0);
+ final RexNode headDnf = toDnf(head);
+ final List<RexNode> headDnfs = RelOptUtil.disjunctions(headDnf);
+ final RexNode tail = and(Util.skip(operands));
+ final RexNode tailDnf = toDnf(tail);
+ final List<RexNode> tailDnfs = RelOptUtil.disjunctions(tailDnf);
+ final List<RexNode> list = Lists.newArrayList();
+ for (RexNode h : headDnfs) {
+ for (RexNode t : tailDnfs) {
+ list.add(and(ImmutableList.of(h, t)));
+ }
+ }
+ return or(list);
+ case OR:
+ operands = flattenOr(((RexCall) rex).getOperands());
+ return or(toDnfs(operands));
+ case NOT:
+ final RexNode arg = ((RexCall) rex).getOperands().get(0);
+ switch (arg.getKind()) {
+ case NOT:
+ return toDnf(((RexCall) arg).getOperands().get(0));
+ case OR:
+ operands = ((RexCall) arg).getOperands();
+ return toDnf(and(Lists.transform(flattenOr(operands), ADD_NOT)));
+ case AND:
+ operands = ((RexCall) arg).getOperands();
+ return toDnf(or(Lists.transform(flattenAnd(operands), ADD_NOT)));
+ default:
+ return rex;
+ }
+ default:
+ return rex;
+ }
+ }
+
+ private List<RexNode> toDnfs(List<RexNode> nodes) {
+ final List<RexNode> list = Lists.newArrayList();
+ for (RexNode node : nodes) {
+ RexNode dnf = toDnf(node);
+ switch (dnf.getKind()) {
+ case OR:
+ list.addAll(((RexCall) dnf).getOperands());
+ break;
+ default:
+ list.add(dnf);
+ }
+ }
+ return list;
+ }
+
+ private RexNode and(Iterable<? extends RexNode> nodes) {
+ return composeConjunction(rexBuilder, nodes, false);
+ }
+
+ private RexNode or(Iterable<? extends RexNode> nodes) {
+ return composeDisjunction(rexBuilder, nodes, false);
+ }
+ }
+
+
/** Shuttle that adds {@code offset} to each {@link RexInputRef} in an
* expression. */
private static class RexShiftShuttle extends RexShuttle {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
index 5a87733..0621a73 100644
--- a/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
+++ b/core/src/test/java/org/apache/calcite/test/CalciteSuite.java
@@ -114,6 +114,7 @@ import org.junit.runners.Suite;
// slow tests (above 1s)
PlannerTest.class,
RelBuilderTest.class,
+ RexImplicationCheckerTest.class,
MaterializationTest.class,
JdbcAdapterTest.class,
LinqFrontJdbcBackTest.class,
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/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 f0fb54b..ae235ab 100644
--- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
+++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java
@@ -179,7 +179,8 @@ public class MaterializationTest {
+ "from \"emps\" where \"deptno\" - 10 = 2",
JdbcTest.HR_MODEL,
CalciteAssert.checkResultContains(
- "EnumerableCalc(expr#0..2=[{inputs}], expr#3=[2], expr#4=[=($t0, $t3)], name=[$t2], E=[$t1], $condition=[$t4])\n"
+ "EnumerableCalc(expr#0..2=[{inputs}], expr#3=[2], "
+ + "expr#4=[=($t0, $t3)], name=[$t2], E=[$t1], $condition=[$t4])\n"
+ " EnumerableTableScan(table=[[hr, m0]]"));
}
@@ -212,7 +213,8 @@ public class MaterializationTest {
@Test public void testFilterQueryOnFilterView2() {
checkMaterialize(
"select \"deptno\", \"empid\", \"name\" from \"emps\" where \"deptno\" = 10",
- "select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10 and \"empid\" < 150");
+ "select \"empid\" + 1 as x, \"name\" from \"emps\" "
+ + "where \"deptno\" = 10 and \"empid\" < 150");
}
/** As {@link #testFilterQueryOnFilterView()} but condition is weaker in
@@ -220,14 +222,131 @@ public class MaterializationTest {
@Ignore("not implemented")
@Test public void testFilterQueryOnFilterView3() {
checkMaterialize(
- "select \"deptno\", \"empid\", \"name\" from \"emps\" where \"deptno\" = 10 or \"deptno\" = 20 or \"empid\" < 160",
+ "select \"deptno\", \"empid\", \"name\" from \"emps\" "
+ + "where \"deptno\" = 10 or \"deptno\" = 20 or \"empid\" < 160",
"select \"empid\" + 1 as x, \"name\" from \"emps\" where \"deptno\" = 10",
JdbcTest.HR_MODEL,
CalciteAssert.checkResultContains(
- "EnumerableCalcRel(expr#0..2=[{inputs}], expr#3=[1], expr#4=[+($t1, $t3)], X=[$t4], name=[$t2], condition=?)\n"
+ "EnumerableCalcRel(expr#0..2=[{inputs}], expr#3=[1], "
+ + "expr#4=[+($t1, $t3)], X=[$t4], name=[$t2], condition=?)\n"
+ " EnumerableTableScan(table=[[hr, m0]])"));
}
+ /** As {@link #testFilterQueryOnFilterView()} but condition is stronger in
+ * query. */
+ @Test public void testFilterQueryOnFilterView4() {
+ checkMaterialize(
+ "select * from \"emps\" where \"deptno\" > 10",
+ "select \"name\" from \"emps\" where \"deptno\" > 30");
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is stronger in
+ * query and columns selected are subset of columns in materialized view */
+ @Test public void testFilterQueryOnFilterView5() {
+ checkMaterialize(
+ "select \"name\", \"deptno\" from \"emps\" where \"deptno\" > 10",
+ "select \"name\" from \"emps\" where \"deptno\" > 30");
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is stronger in
+ * query and columns selected are subset of columns in materialized view */
+ @Test public void testFilterQueryOnFilterView6() {
+ checkMaterialize(
+ "select \"name\", \"deptno\", \"salary\" from \"emps\" "
+ + "where \"salary\" > 2000.5",
+ "select \"name\" from \"emps\" where \"deptno\" > 30 and \"salary\" > 3000");
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is stronger in
+ * query and columns selected are subset of columns in materialized view
+ * Condition here is complex*/
+ @Test public void testFilterQueryOnFilterView7() {
+ checkMaterialize(
+ "select * from \"emps\" where "
+ + "((\"salary\" < 1111.9 and \"deptno\" > 10)"
+ + "or (\"empid\" > 400 and \"salary\" > 5000) "
+ + "or \"salary\" > 500)",
+ "select \"name\" from \"emps\" where (\"salary\" > 1000 "
+ + "or (\"deptno\" >= 30 and \"salary\" <= 500))");
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is stronger in
+ * query. However, columns selected are not present in columns of materialized view,
+ * hence should not use materialized view*/
+ @Test public void testFilterQueryOnFilterView8() {
+ checkNoMaterialize(
+ "select \"name\", \"deptno\" from \"emps\" where \"deptno\" > 10",
+ "select \"name\", \"empid\" from \"emps\" where \"deptno\" > 30",
+ JdbcTest.HR_MODEL);
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is weaker in
+ * query.*/
+ @Test public void testFilterQueryOnFilterView9() {
+ checkNoMaterialize(
+ "select \"name\", \"deptno\" from \"emps\" where \"deptno\" > 10",
+ "select \"name\", \"empid\" from \"emps\" "
+ + "where \"deptno\" > 30 or \"empid\" > 10",
+ JdbcTest.HR_MODEL);
+ }
+ /** As {@link #testFilterQueryOnFilterView()} but condition currently
+ * has unsupported type being checked on query.
+ */
+ @Test public void testFilterQueryOnFilterView10() {
+ checkNoMaterialize(
+ "select \"name\", \"deptno\" from \"emps\" where \"deptno\" > 10 "
+ + "and \"name\" = \'calcite\'",
+ "select \"name\", \"empid\" from \"emps\" where \"deptno\" > 30 "
+ + "or \"empid\" > 10",
+ JdbcTest.HR_MODEL);
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is weaker in
+ * query and columns selected are subset of columns in materialized view
+ * Condition here is complex*/
+ @Test public void testFilterQueryOnFilterView11() {
+ checkNoMaterialize(
+ "select \"name\", \"deptno\" from \"emps\" where "
+ + "(\"salary\" < 1111.9 and \"deptno\" > 10)"
+ + "or (\"empid\" > 400 and \"salary\" > 5000)",
+ "select \"name\" from \"emps\" where \"deptno\" > 30 and \"salary\" > 3000",
+ JdbcTest.HR_MODEL);
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition of
+ * query is stronger but is on the column not present in MV (salary).
+ */
+ @Test public void testFilterQueryOnFilterView12() {
+ checkNoMaterialize(
+ "select \"name\", \"deptno\" from \"emps\" where \"salary\" > 2000.5",
+ "select \"name\" from \"emps\" where \"deptno\" > 30 and \"salary\" > 3000",
+ JdbcTest.HR_MODEL);
+ }
+
+ /** As {@link #testFilterQueryOnFilterView()} but condition is weaker in
+ * query and columns selected are subset of columns in materialized view
+ * Condition here is complex*/
+ @Test public void testFilterQueryOnFilterView13() {
+ checkNoMaterialize(
+ "select * from \"emps\" where "
+ + "(\"salary\" < 1111.9 and \"deptno\" > 10)"
+ + "or (\"empid\" > 400 and \"salary\" > 5000)",
+ "select \"name\" from \"emps\" where \"salary\" > 1000 "
+ + "or (\"deptno\" > 30 and \"salary\" > 3000)",
+ JdbcTest.HR_MODEL);
+ }
+
+ /** As {@link #testFilterQueryOnFilterView13()} but using alias
+ * and condition of query is stronger*/
+ @Test public void testAlias() {
+ checkMaterialize(
+ "select * from \"emps\" as em where "
+ + "(em.\"salary\" < 1111.9 and em.\"deptno\" > 10)"
+ + "or (em.\"empid\" > 400 and em.\"salary\" > 5000)",
+ "select \"name\" as n from \"emps\" as e where "
+ + "(e.\"empid\" > 500 and e.\"salary\" > 6000)");
+ }
+
/** Aggregation query at same level of aggregation as aggregation
* materialization. */
@Test public void testAggregate() {
@@ -241,11 +360,13 @@ public class MaterializationTest {
* COUNT is rolled up using SUM. */
@Test public void testAggregateRollUp() {
checkMaterialize(
- "select \"empid\", \"deptno\", count(*) as c, sum(\"empid\") as s from \"emps\" group by \"empid\", \"deptno\"",
+ "select \"empid\", \"deptno\", count(*) as c, sum(\"empid\") as s from \"emps\" "
+ + "group by \"empid\", \"deptno\"",
"select count(*) + 1 as c, \"deptno\" from \"emps\" group by \"deptno\"",
JdbcTest.HR_MODEL,
CalciteAssert.checkResultContains(
- "EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], expr#3=[+($t1, $t2)], C=[$t3], deptno=[$t0])\n"
+ "EnumerableCalc(expr#0..1=[{inputs}], expr#2=[1], "
+ + "expr#3=[+($t1, $t2)], C=[$t3], deptno=[$t0])\n"
+ " EnumerableAggregate(group=[{1}], agg#0=[$SUM0($2)])\n"
+ " EnumerableTableScan(table=[[hr, m0]])"));
}
@@ -621,6 +742,13 @@ public class MaterializationTest {
+ "join \"depts\" using (\"deptno\")";
checkNoMaterialize(q, q, JdbcTest.HR_MODEL);
}
+
+ @Test public void testJoinMaterialization() {
+ String q = "select *\n"
+ + "from (select * from \"emps\" where \"empid\" < 300)\n"
+ + "join \"depts\" using (\"deptno\")";
+ checkMaterialize("select * from \"emps\" where \"empid\" < 500", q);
+ }
}
// End MaterializationTest.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/4a9b1939/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
new file mode 100644
index 0000000..7e693b0
--- /dev/null
+++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
@@ -0,0 +1,448 @@
+/*
+ * 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.test;
+
+import org.apache.calcite.DataContext;
+import org.apache.calcite.jdbc.JavaTypeFactoryImpl;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelOptSchema;
+import org.apache.calcite.plan.RexImplicationChecker;
+import org.apache.calcite.rel.type.RelDataType;
+import org.apache.calcite.rel.type.RelDataTypeFactory;
+import org.apache.calcite.rel.type.RelDataTypeSystem;
+import org.apache.calcite.rex.RexBuilder;
+import org.apache.calcite.rex.RexExecutorImpl;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.schema.SchemaPlus;
+import org.apache.calcite.schema.Schemas;
+import org.apache.calcite.server.CalciteServerStatement;
+import org.apache.calcite.sql.SqlCollation;
+import org.apache.calcite.sql.fun.SqlStdOperatorTable;
+import org.apache.calcite.tools.Frameworks;
+import org.apache.calcite.util.NlsString;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.math.BigDecimal;
+import java.sql.Date;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+
+/**
+ * Tests the RexImplication checker
+ */
+public class RexImplicationCheckerTest {
+ //~ Instance fields --------------------------------------------------------
+
+ private RexBuilder rexBuilder = null;
+ private RexNode bl;
+ private RexNode i;
+ private RexNode dec;
+ private RexNode lg;
+ private RexNode sh;
+ private RexNode by;
+ private RexNode fl;
+ private RexNode dt;
+ private RexNode ch;
+ private RexNode ts;
+ private RexNode t;
+
+ private RelDataType boolRelDataType;
+ private RelDataType intRelDataType;
+ private RelDataType decRelDataType;
+ private RelDataType longRelDataType;
+ private RelDataType shortDataType;
+ private RelDataType byteDataType;
+ private RelDataType floatDataType;
+ private RelDataType charDataType;
+ private RelDataType dateDataType;
+ private RelDataType timeStampDataType;
+ private RelDataType timeDataType;
+ private RelDataTypeFactory typeFactory;
+ private RexImplicationChecker checker;
+ private RelDataType rowType;
+ private RexExecutorImpl executor;
+
+ //~ Methods ----------------------------------------------------------------
+
+ @Before
+ public void setUp() {
+ typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ rexBuilder = new RexBuilder(typeFactory);
+ boolRelDataType = typeFactory.createJavaType(Boolean.class);
+ intRelDataType = typeFactory.createJavaType(Integer.class);
+ decRelDataType = typeFactory.createJavaType(Double.class);
+ longRelDataType = typeFactory.createJavaType(Long.class);
+ shortDataType = typeFactory.createJavaType(Short.class);
+ byteDataType = typeFactory.createJavaType(Byte.class);
+ floatDataType = typeFactory.createJavaType(Float.class);
+ charDataType = typeFactory.createJavaType(Character.class);
+ dateDataType = typeFactory.createJavaType(Date.class);
+ timeStampDataType = typeFactory.createJavaType(Timestamp.class);
+ timeDataType = typeFactory.createJavaType(Time.class);
+
+ bl = new RexInputRef(
+ 0,
+ typeFactory.createTypeWithNullability(boolRelDataType, true));
+ i = new RexInputRef(
+ 1,
+ typeFactory.createTypeWithNullability(intRelDataType, true));
+ dec = new RexInputRef(
+ 2,
+ typeFactory.createTypeWithNullability(decRelDataType, true));
+ lg = new RexInputRef(
+ 3,
+ typeFactory.createTypeWithNullability(longRelDataType, true));
+ sh = new RexInputRef(
+ 4,
+ typeFactory.createTypeWithNullability(shortDataType, true));
+ by = new RexInputRef(
+ 5,
+ typeFactory.createTypeWithNullability(byteDataType, true));
+ fl = new RexInputRef(
+ 6,
+ typeFactory.createTypeWithNullability(floatDataType, true));
+ ch = new RexInputRef(
+ 7,
+ typeFactory.createTypeWithNullability(charDataType, true));
+ dt = new RexInputRef(
+ 8,
+ typeFactory.createTypeWithNullability(dateDataType, true));
+ ts = new RexInputRef(
+ 9,
+ typeFactory.createTypeWithNullability(timeStampDataType, true));
+ t = new RexInputRef(
+ 10,
+ typeFactory.createTypeWithNullability(timeDataType, true));
+
+ rowType = typeFactory.builder()
+ .add("bool", boolRelDataType)
+ .add("int", intRelDataType)
+ .add("dec", decRelDataType)
+ .add("long", longRelDataType)
+ .add("short", shortDataType)
+ .add("byte", byteDataType)
+ .add("float", floatDataType)
+ .add("char", charDataType)
+ .add("date", dateDataType)
+ .add("timestamp", timeStampDataType)
+ .add("time", timeDataType)
+ .build();
+
+ Frameworks.withPrepare(
+ new Frameworks.PrepareAction<Void>() {
+ public Void apply(RelOptCluster cluster,
+ RelOptSchema relOptSchema,
+ SchemaPlus rootSchema,
+ CalciteServerStatement statement) {
+ DataContext dataContext =
+ Schemas.createDataContext(statement.getConnection());
+ executor = new RexExecutorImpl(dataContext);
+ return null;
+ }
+ });
+
+ checker = new RexImplicationChecker(rexBuilder, executor, rowType);
+ }
+
+ private void checkImplies(RexNode node1, RexNode node2) {
+ assertTrue(node1.toString() + " doesnot imply " + node2.toString()
+ + " when it should.", checker.implies(node1, node2));
+ }
+
+ private void checkNotImplies(RexNode node1, RexNode node2) {
+ assertFalse(node1.toString() + " implies " + node2.toString()
+ + " when it should not", checker.implies(node1, node2));
+ }
+
+ // Simple Tests for Operators
+ @Test public void testSimpleGreaterCond() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(30)));
+
+ RexNode node3 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(30)));
+
+ RexNode node4 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ RexNode node5 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.EQUALS,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(30)));
+
+ RexNode node6 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.NOT_EQUALS,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ checkImplies(node2, node1);
+ checkNotImplies(node1, node2);
+ checkNotImplies(node1, node3);
+ checkImplies(node3, node1);
+ checkImplies(node5, node1);
+ checkNotImplies(node1, node5);
+ checkNotImplies(node1, node6);
+ checkNotImplies(node4, node6);
+ // TODO: Need to support Identity
+ //checkImplies(node1, node1);
+ //checkImplies(node3, node3);
+ }
+
+ @Test public void testSimpleLesserCond() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(30)));
+
+ RexNode node3 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(30)));
+
+ RexNode node4 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ RexNode node5 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.EQUALS,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ RexNode node6 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.NOT_EQUALS,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ checkImplies(node1, node2);
+ checkNotImplies(node2, node1);
+ checkImplies(node1, node3);
+ checkNotImplies(node3, node1);
+ checkImplies(node5, node2);
+ checkNotImplies(node2, node5);
+ checkNotImplies(node1, node5);
+ checkNotImplies(node1, node6);
+ checkNotImplies(node4, node6);
+ // TODO: Need to support Identity
+ //checkImplies(node1, node1);
+ //checkImplies(node3, node3);
+ }
+
+ @Test public void testSimpleEq() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.EQUALS,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(30)));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.NOT_EQUALS,
+ i,
+ rexBuilder.makeExactLiteral(new BigDecimal(10)));
+
+ //Check Identity
+ checkImplies(node1, node1);
+ //TODO: Support Identity
+ // checkImplies(node2, node2);
+ checkImplies(node1, node2);
+ checkNotImplies(node2, node1);
+ }
+
+ // Simple Tests for DataTypes
+ @Test public void testSimpleDec() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN,
+ dec,
+ rexBuilder.makeApproxLiteral(new BigDecimal(30.9)));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN,
+ dec,
+ rexBuilder.makeApproxLiteral(new BigDecimal(40.33)));
+
+ checkImplies(node1, node2);
+ checkNotImplies(node2, node1);
+ }
+
+ @Test public void testSimpleBoolean() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.EQUALS,
+ bl,
+ rexBuilder.makeLiteral(true));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.EQUALS,
+ bl,
+ rexBuilder.makeLiteral(false));
+
+ //TODO: Need to support false => true
+ //checkImplies(node2, node1);
+ checkNotImplies(node1, node2);
+ }
+
+ @Test public void testSimpleLong() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ lg,
+ rexBuilder.makeLiteral(new Long(324324L), longRelDataType, true));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN,
+ lg,
+ rexBuilder.makeLiteral(new Long(324325L), longRelDataType, true));
+
+ checkImplies(node2, node1);
+ checkNotImplies(node1, node2);
+ }
+
+ @Test public void testSimpleShort() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ sh,
+ rexBuilder.makeLiteral(new Short((short) 10), shortDataType, true));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ sh,
+ rexBuilder.makeLiteral(new Short((short) 11), shortDataType, true));
+
+ checkImplies(node2, node1);
+ checkNotImplies(node1, node2);
+ }
+
+ @Test public void testSimpleChar() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ ch,
+ rexBuilder.makeCharLiteral(new NlsString("b", null, SqlCollation.COERCIBLE)));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ ch,
+ rexBuilder.makeCharLiteral(new NlsString("a", null, SqlCollation.COERCIBLE)));
+
+ checkImplies(node1, node2);
+ checkNotImplies(node2, node1);
+ }
+
+ @Test public void testSimpleDate() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
+ dt,
+ rexBuilder.makeDateLiteral(Calendar.getInstance()));
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.EQUALS,
+ dt,
+ rexBuilder.makeDateLiteral(Calendar.getInstance()));
+
+ checkImplies(node2, node1);
+ checkNotImplies(node1, node2);
+ }
+
+ @Ignore("work in progress")
+ @Test public void testSimpleTimeStamp() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ ts,
+ rexBuilder.makeTimestampLiteral(Calendar.getInstance(),
+ timeStampDataType.getPrecision()));
+
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ ts,
+ rexBuilder.makeTimestampLiteral(Calendar.getInstance(),
+ timeStampDataType.getPrecision()));
+
+ checkImplies(node1, node2);
+ checkNotImplies(node2, node1);
+ }
+
+ @Ignore("work in progress")
+ @Test public void testSimpleTime() {
+ RexNode node1 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ t,
+ rexBuilder.makeTimeLiteral(Calendar.getInstance(),
+ timeDataType.getPrecision()));
+
+
+ RexNode node2 =
+ rexBuilder.makeCall(
+ SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ t,
+ rexBuilder.makeTimestampLiteral(Calendar.getInstance(),
+ timeDataType.getPrecision()));
+
+ checkImplies(node1, node2);
+ checkNotImplies(node2, node1);
+ }
+
+}
[2/3] incubator-calcite git commit: Fix up previous commit;
add some tests for constant reduction
Posted by jh...@apache.org.
Fix up previous commit; add some tests for constant reduction
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/5a365609
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/5a365609
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/5a365609
Branch: refs/heads/master
Commit: 5a365609c0d050e5e832449bdadad1e309f0f204
Parents: 4a9b193
Author: Julian Hyde <jh...@apache.org>
Authored: Wed Jul 15 21:48:51 2015 -0700
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jul 16 15:03:20 2015 -0700
----------------------------------------------------------------------
.../adapter/enumerable/EnumerableWindow.java | 2 +-
.../MaterializedViewSubstitutionVisitor.java | 69 +--
.../org/apache/calcite/plan/RelOptUtil.java | 17 +-
.../calcite/plan/RexImplicationChecker.java | 256 ++++----
.../calcite/plan/SubstitutionVisitor.java | 53 +-
.../apache/calcite/plan/VisitorDataContext.java | 57 +-
.../rel/rules/AggregateFilterTransposeRule.java | 2 +-
.../rel/rules/AggregateUnionTransposeRule.java | 2 +-
.../rel/rules/FilterAggregateTransposeRule.java | 2 +-
.../java/org/apache/calcite/sql/SqlKind.java | 20 +
.../org/apache/calcite/rex/RexExecutorTest.java | 136 ++--
.../org/apache/calcite/test/DiffRepository.java | 1 -
.../calcite/test/RexImplicationCheckerTest.java | 614 ++++++++-----------
13 files changed, 592 insertions(+), 639 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
index 15f77f5..433c6cd 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableWindow.java
@@ -746,7 +746,7 @@ public class EnumerableWindow extends Window implements EnumerableRel {
BlockBuilder builder, final Result result, int windowIdx,
List<AggImpState> aggs, PhysType outputPhysType,
List<Expression> outputRow) {
- for (final AggImpState agg: aggs) {
+ for (final AggImpState agg : aggs) {
agg.context =
new WinAggContext() {
public SqlAggFunction aggregation() {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
index e367f93..5c4ccdd 100644
--- a/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/MaterializedViewSubstitutionVisitor.java
@@ -25,45 +25,19 @@ import org.apache.calcite.rex.RexShuttle;
import com.google.common.collect.ImmutableList;
import java.util.List;
+
/**
- * Substitutes part of a tree of relational expressions with another tree.
- *
- * <p>The call {@code new MaterializedSubstitutionVisitor(target, query).go(replacement))}
- * will return {@code query} with every occurrence of {@code target} replaced
- * by {@code replacement}.</p>
- *
- * <p>The following example shows how {@code MaterializedSubstitutionVisitor} can be used
- * for materialized view recognition.</p>
- *
- * <ul>
- * <li>query = SELECT a, c FROM t WHERE x = 5 AND b = 4</li>
- * <li>target = SELECT a, b, c FROM t WHERE x = 5</li>
- * <li>replacement = SELECT * FROM mv</li>
- * <li>result = SELECT a, c FROM mv WHERE b = 4</li>
- * </ul>
- *
- * <p>Note that {@code result} uses the materialized view table {@code mv} and a
- * simplified condition {@code b = 4}.</p>
- *
- * <p>Uses a bottom-up matching algorithm. Nodes do not need to be identical.
- * At each level, returns the residue.</p>
- *
- * <p>The inputs must only include the core relational operators:
- * {@link org.apache.calcite.rel.logical.LogicalTableScan},
- * {@link LogicalFilter},
- * {@link LogicalProject},
- * {@link org.apache.calcite.rel.logical.LogicalJoin},
- * {@link LogicalUnion},
- * {@link LogicalAggregate}.</p>
+ * Extension to {@link SubstitutionVisitor}.
*/
public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
+ private static final ImmutableList<UnifyRule> EXTENDED_RULES =
+ ImmutableList.<UnifyRule>builder()
+ .addAll(DEFAULT_RULES)
+ .add(ProjectToProjectUnifyRule1.INSTANCE)
+ .build();
public MaterializedViewSubstitutionVisitor(RelNode target_, RelNode query_) {
- super(target_, query_);
- ImmutableList.Builder<UnifyRule> builder = new ImmutableList.Builder<UnifyRule>();
- builder.addAll(this.unifyRules);
- builder.add(ProjectToProjectUnifyRule1.INSTANCE);
- this.unifyRules = builder.build();
+ super(target_, query_, EXTENDED_RULES);
}
public RelNode go(RelNode replacement_) {
@@ -73,7 +47,6 @@ public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
/**
* Project to Project Unify rule.
*/
-
private static class ProjectToProjectUnifyRule1 extends AbstractUnifyRule {
public static final ProjectToProjectUnifyRule1 INSTANCE =
new ProjectToProjectUnifyRule1();
@@ -83,12 +56,13 @@ public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
operand(MutableProject.class, target(0)), 1);
}
- @Override
- protected UnifyResult apply(UnifyRuleCall call) {
+ @Override protected UnifyResult apply(UnifyRuleCall call) {
final MutableProject query = (MutableProject) call.query;
- final List<RelDataTypeField> oldFieldList = query.getInput().getRowType().getFieldList();
- final List<RelDataTypeField> newFieldList = call.target.getRowType().getFieldList();
+ final List<RelDataTypeField> oldFieldList =
+ query.getInput().getRowType().getFieldList();
+ final List<RelDataTypeField> newFieldList =
+ call.target.getRowType().getFieldList();
List<RexNode> newProjects;
try {
newProjects = transformRex(query.getProjects(), oldFieldList, newFieldList);
@@ -104,9 +78,8 @@ public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
return call.result(newProject2);
}
- @Override
- protected UnifyRuleCall match(SubstitutionVisitor visitor, MutableRel query,
- MutableRel target) {
+ @Override protected UnifyRuleCall match(SubstitutionVisitor visitor,
+ MutableRel query, MutableRel target) {
assert query instanceof MutableProject && target instanceof MutableProject;
if (queryOperand.matches(visitor, query)) {
@@ -116,8 +89,8 @@ public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
final MutableProject queryProject = (MutableProject) query;
if (queryProject.getInput() instanceof MutableFilter) {
-
- final MutableFilter innerFilter = (MutableFilter) (queryProject.getInput());
+ final MutableFilter innerFilter =
+ (MutableFilter) queryProject.getInput();
RexNode newCondition;
try {
newCondition = transformRex(innerFilter.getCondition(),
@@ -130,18 +103,18 @@ public class MaterializedViewSubstitutionVisitor extends SubstitutionVisitor {
newCondition);
return visitor.new UnifyRuleCall(this, query, newFilter,
- copy(visitor.getSlots(), slotCount));
+ copy(visitor.slots, slotCount));
}
}
}
return null;
}
- private RexNode transformRex(
- RexNode node,
+ private RexNode transformRex(RexNode node,
final List<RelDataTypeField> oldFields,
final List<RelDataTypeField> newFields) {
- List<RexNode> nodes = transformRex(ImmutableList.of(node), oldFields, newFields);
+ List<RexNode> nodes =
+ transformRex(ImmutableList.of(node), oldFields, newFields);
return nodes.get(0);
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/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 c1f8af5..7a79aeb 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptUtil.java
@@ -1158,7 +1158,7 @@ public abstract class RelOptUtil {
&& kind != SqlKind.EQUALS
&& kind != SqlKind.IS_DISTINCT_FROM) {
if (reverse) {
- kind = reverse(kind);
+ kind = kind.reverse();
}
rangeOp.add(op(kind, call.getOperator()));
}
@@ -1199,21 +1199,6 @@ public abstract class RelOptUtil {
false);
}
- public static SqlKind reverse(SqlKind kind) {
- switch (kind) {
- case GREATER_THAN:
- return SqlKind.LESS_THAN;
- case GREATER_THAN_OR_EQUAL:
- return SqlKind.LESS_THAN_OR_EQUAL;
- case LESS_THAN:
- return SqlKind.GREATER_THAN;
- case LESS_THAN_OR_EQUAL:
- return SqlKind.GREATER_THAN_OR_EQUAL;
- default:
- return kind;
- }
- }
-
public static SqlOperator op(SqlKind kind, SqlOperator operator) {
switch (kind) {
case EQUALS:
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
index 1eb019d..42c7b43 100644
--- a/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
+++ b/core/src/main/java/org/apache/calcite/plan/RexImplicationChecker.java
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.calcite.plan;
import org.apache.calcite.DataContext;
@@ -39,40 +38,44 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-
-
-/** Checks if Condition X logically implies Condition Y
+/**
+ * Checks whether one condition logically implies another.
*
- * <p>(x > 10) implies (x > 5)</p>
+ * <p>If A ⇒ B, whenever A is true, B will be true also.
*
- * <p>(y = 10) implies (y < 30 AND x > 30)</p>
+ * <p>For example:
+ * <ul>
+ * <li>(x > 10) ⇒ (x > 5)
+ * <li>(y = 10) ⇒ (y < 30 OR x > 30)
+ * </ul>
*/
public class RexImplicationChecker {
final RexBuilder builder;
- final RexExecutorImpl rexImpl;
+ final RexExecutorImpl executor;
final RelDataType rowType;
public RexImplicationChecker(
RexBuilder builder,
- RexExecutorImpl rexImpl,
+ RexExecutorImpl executor,
RelDataType rowType) {
this.builder = builder;
- this.rexImpl = rexImpl;
+ this.executor = executor;
this.rowType = rowType;
}
/**
- * Checks if condition first implies (=>) condition second
- * This reduces to SAT problem which is NP-Complete
- * When func says first => second then it is definitely true
- * It cannot prove if first doesnot imply second.
+ * Checks if condition first implies (⇒) condition second.
+ *
+ * <p>This reduces to SAT problem which is NP-Complete.
+ * When this method says first implies second then it is definitely true.
+ * But it cannot prove that first does not imply second.
+ *
* @param first first condition
* @param second second condition
- * @return true if it can prove first => second, otherwise false i.e.,
- * it doesn't know if implication holds .
+ * @return true if it can prove first ⇒ second; otherwise false i.e.,
+ * it doesn't know if implication holds
*/
public boolean implies(RexNode first, RexNode second) {
-
// Validation
if (!validate(first, second)) {
return false;
@@ -91,22 +94,26 @@ public class RexImplicationChecker {
return true;
}
- /** Decompose DNF into List of Conjunctions
+ /** Decomposes DNF into List of Conjunctions.
*
- * (x > 10 AND y > 30) OR (z > 90) will be converted to
+ * <p>For example,
+ * {@code x > 10 AND y > 30) OR (z > 90)}
+ * will be converted to
* list of 2 conditions:
- * 1. (x > 10 AND y > 30)
- * 2. (z > 90)
*
+ * <ul>
+ * <li>(x > 10 AND y > 30)</li>
+ * <li>z > 90</li>
+ * </ul>
*/
List<RexNode> firstDnfs = RelOptUtil.disjunctions(firstDnf);
List<RexNode> secondDnfs = RelOptUtil.disjunctions(secondDnf);
for (RexNode f : firstDnfs) {
if (!f.isAlwaysFalse()) {
- //Check if f implies atleast
+ // Check if f implies at least
// one of the conjunctions in list secondDnfs
- boolean implyOneConjuntion = false;
+ boolean implyOneConjunction = false;
for (RexNode s : secondDnfs) {
if (s.isAlwaysFalse()) { // f cannot imply s
continue;
@@ -115,57 +122,61 @@ public class RexImplicationChecker {
if (impliesConjunction(f, s)) {
// Satisfies one of the condition, so lets
// move to next conjunction in firstDnfs
- implyOneConjuntion = true;
+ implyOneConjunction = true;
break;
}
- } //end of inner loop
+ }
- // If f couldnot imply even one conjunction in
+ // If f could not imply even one conjunction in
// secondDnfs, then final implication may be false
- if (!implyOneConjuntion) {
+ if (!implyOneConjunction) {
return false;
}
}
- } //end of outer loop
+ }
return true;
}
- /** Checks if Conjunction first => Conjunction second**/
+ /** Returns whether first implies second (both are conjunctions). */
private boolean impliesConjunction(RexNode first, RexNode second) {
+ final InputUsageFinder firstUsageFinder = new InputUsageFinder();
+ final InputUsageFinder secondUsageFinder = new InputUsageFinder();
- InputUsageFinder firstUsgFinder = new InputUsageFinder();
- InputUsageFinder secondUsgFinder = new InputUsageFinder();
-
- RexUtil.apply(firstUsgFinder, new ArrayList<RexNode>(), first);
- RexUtil.apply(secondUsgFinder, new ArrayList<RexNode>(), second);
+ RexUtil.apply(firstUsageFinder, new ArrayList<RexNode>(), first);
+ RexUtil.apply(secondUsageFinder, new ArrayList<RexNode>(), second);
// Check Support
- if (!checkSupport(firstUsgFinder, secondUsgFinder)) {
+ if (!checkSupport(firstUsageFinder, secondUsageFinder)) {
return false;
}
- List<Pair<RexInputRef, RexNode>> usgList = new ArrayList<>();
- for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator,
- RexNode>> entry: firstUsgFinder.usageMap.entrySet()) {
- final List<Pair<SqlOperator, RexNode>> list = entry.getValue().getUsageList();
- usgList.add(Pair.of(entry.getKey(), list.get(0).getValue()));
+ List<Pair<RexInputRef, RexNode>> usageList = new ArrayList<>();
+ for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator, RexNode>> entry
+ : firstUsageFinder.usageMap.entrySet()) {
+ final Pair<SqlOperator, RexNode> pair = entry.getValue().usageList.get(0);
+ usageList.add(Pair.of(entry.getKey(), pair.getValue()));
}
- /* Get the literals from first conjunction and execute second conjunction using them
- * E.g., for x >30 => x > 10,
- * we will replace x by 30 in second expression and execute it i.e., 30>10
- * If it's true then we infer implication.
- */
- final DataContext dataValues = VisitorDataContext.getDataContext(rowType, usgList);
+ // Get the literals from first conjunction and executes second conjunction
+ // using them.
+ //
+ // E.g., for
+ // x > 30 ⇒ x > 10,
+ // we will replace x by 30 in second expression and execute it i.e.,
+ // 30 > 10
+ //
+ // If it's true then we infer implication.
+ final DataContext dataValues =
+ VisitorDataContext.of(rowType, usageList);
if (dataValues == null) {
return false;
}
ImmutableList<RexNode> constExps = ImmutableList.of(second);
- final RexExecutable exec = rexImpl.getExecutable(builder,
- constExps, rowType);
+ final RexExecutable exec =
+ executor.getExecutable(builder, constExps, rowType);
Object[] result;
exec.setDataContext(dataValues);
@@ -176,72 +187,85 @@ public class RexImplicationChecker {
// Need to monitor it and handle all the cases raising them.
return false;
}
- return result != null && result.length == 1 && result[0] instanceof Boolean
+ return result != null
+ && result.length == 1
+ && result[0] instanceof Boolean
&& (Boolean) result[0];
}
/**
* Looks at the usage of variables in first and second conjunction to decide
- * if this kind of expression is currently supported for proving first => second.
- * 1. Variables should be used only once in both the conjunction against
- * given set of operations only: >,<,<=,>=,=,!=
- * 2. All the variables used in second condition should be used even in the first.
- * 3. If operator used for variable in first is op1 and op2 for second, then we support
- * these combination for conjunction (op1, op2) then op1, op2 belongs to
- * one of the following sets:
- * a. (<,<=) X (<,<=) , X represents cartesian product
- * b. (>/>=) X (>,>=)
- * c. (=) X (>,>=,<,<=,=,!=)
- * d. (!=, =)
- * @return true, if input usage pattern is supported. Otherwise, false.
+ * whether this kind of expression is currently supported for proving first
+ * implies second.
+ *
+ * <ol>
+ * <li>Variables should be used only once in both the conjunction against
+ * given set of operations only: >, <, <=, >=, =, !=
+ *
+ * <li>All the variables used in second condition should be used even in the
+ * first.
+ *
+ * <li>If operator used for variable in first is op1 and op2 for second, then
+ * we support these combination for conjunction (op1, op2) then op1, op2
+ * belongs to one of the following sets:
+ *
+ * <ul>
+ * <li>(<, <=) X (<, <=) <i>note: X represents cartesian product</i>
+ * <li>(> / >=) X (>, >=)
+ * <li>(=) X (>, >=, <, <=, =, !=)
+ * <li>(!=, =)
+ * </ul>
+ * </ol>
+ *
+ * @return whether input usage pattern is supported
*/
- private boolean checkSupport(
- InputUsageFinder firstUsgFinder,
- InputUsageFinder secondUsgFinder) {
- Map<RexInputRef, InputRefUsage<SqlOperator,
- RexNode>> firstUsgMap = firstUsgFinder.usageMap;
- Map<RexInputRef, InputRefUsage<SqlOperator,
- RexNode>> secondUsgMap = secondUsgFinder.usageMap;
-
- for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator,
- RexNode>> entry: firstUsgMap.entrySet()) {
+ private boolean checkSupport(InputUsageFinder firstUsageFinder,
+ InputUsageFinder secondUsageFinder) {
+ final Map<RexInputRef, InputRefUsage<SqlOperator, RexNode>> firstUsageMap =
+ firstUsageFinder.usageMap;
+ final Map<RexInputRef, InputRefUsage<SqlOperator, RexNode>> secondUsageMap =
+ secondUsageFinder.usageMap;
+
+ for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator, RexNode>> entry
+ : firstUsageMap.entrySet()) {
if (entry.getValue().usageCount > 1) {
return false;
}
}
- for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator,
- RexNode>> entry: secondUsgMap.entrySet()) {
+ for (Map.Entry<RexInputRef, InputRefUsage<SqlOperator, RexNode>> entry
+ : secondUsageMap.entrySet()) {
final InputRefUsage<SqlOperator, RexNode> secondUsage = entry.getValue();
- if (secondUsage.getUsageCount() > 1
- || secondUsage.getUsageList().size() != 1) {
+ if (secondUsage.usageCount > 1
+ || secondUsage.usageList.size() != 1) {
return false;
}
- final InputRefUsage<SqlOperator, RexNode> firstUsage = firstUsgMap.get(entry.getKey());
+ final InputRefUsage<SqlOperator, RexNode> firstUsage =
+ firstUsageMap.get(entry.getKey());
if (firstUsage == null
- || firstUsage.getUsageList().size() != 1) {
+ || firstUsage.usageList.size() != 1) {
return false;
}
- final Pair<SqlOperator, RexNode> fUse = firstUsage.getUsageList().get(0);
- final Pair<SqlOperator, RexNode> sUse = secondUsage.getUsageList().get(0);
+ final Pair<SqlOperator, RexNode> fUse = firstUsage.usageList.get(0);
+ final Pair<SqlOperator, RexNode> sUse = secondUsage.usageList.get(0);
- final SqlKind fkind = fUse.getKey().getKind();
+ final SqlKind fKind = fUse.getKey().getKind();
- if (fkind != SqlKind.EQUALS) {
+ if (fKind != SqlKind.EQUALS) {
switch (sUse.getKey().getKind()) {
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
- if (!(fkind == SqlKind.GREATER_THAN)
- && !(fkind == SqlKind.GREATER_THAN_OR_EQUAL)) {
+ if (!(fKind == SqlKind.GREATER_THAN)
+ && !(fKind == SqlKind.GREATER_THAN_OR_EQUAL)) {
return false;
}
break;
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
- if (!(fkind == SqlKind.LESS_THAN)
- && !(fkind == SqlKind.LESS_THAN_OR_EQUAL)) {
+ if (!(fKind == SqlKind.LESS_THAN)
+ && !(fKind == SqlKind.LESS_THAN_OR_EQUAL)) {
return false;
}
break;
@@ -254,35 +278,29 @@ public class RexImplicationChecker {
}
private boolean validate(RexNode first, RexNode second) {
- if (first == null || second == null) {
- return false;
- }
- if (!(first instanceof RexCall)
- || !(second instanceof RexCall)) {
- return false;
- }
- return true;
+ return first instanceof RexCall && second instanceof RexCall;
}
-
/**
- * Visitor which builds a Usage Map of inputs used by an expression.
- * E.g: for x >10 AND y < 20 AND x =40, Usage Map would look like:
- * key:x value: {(>,10),(=,40), usageCount = 2}
- * key:y value: {(>,20),usageCount=1}
+ * Visitor that builds a usage map of inputs used by an expression.
+ *
+ * <p>E.g: for x > 10 AND y < 20 AND x = 40, usage map is as follows:
+ * <ul>
+ * <li>key: x value: {(>, 10),(=, 40), usageCount = 2}
+ * <li>key: y value: {(>, 20), usageCount = 1}
+ * </ul>
*/
private static class InputUsageFinder extends RexVisitorImpl<Void> {
- public final Map<RexInputRef, InputRefUsage<SqlOperator,
- RexNode>> usageMap = new HashMap<>();
+ public final Map<RexInputRef, InputRefUsage<SqlOperator, RexNode>>
+ usageMap = new HashMap<>();
public InputUsageFinder() {
super(true);
}
public Void visitInputRef(RexInputRef inputRef) {
- InputRefUsage<SqlOperator,
- RexNode> inputRefUse = getUsageMap(inputRef);
- inputRefUse.incrUsage();
+ InputRefUsage<SqlOperator, RexNode> inputRefUse = getUsageMap(inputRef);
+ inputRefUse.usageCount++;
return null;
}
@@ -318,8 +336,7 @@ public class RexImplicationChecker {
}
private SqlOperator reverse(SqlOperator op) {
- return RelOptUtil.op(
- RelOptUtil.reverse(op.getKind()), op);
+ return RelOptUtil.op(op.getKind().reverse(), op);
}
private static RexNode removeCast(RexNode inputRef) {
@@ -333,11 +350,12 @@ public class RexImplicationChecker {
return inputRef;
}
- private void updateUsage(SqlOperator op, RexInputRef inputRef, RexNode literal) {
- InputRefUsage<SqlOperator,
- RexNode> inputRefUse = getUsageMap(inputRef);
+ private void updateUsage(SqlOperator op, RexInputRef inputRef,
+ RexNode literal) {
+ final InputRefUsage<SqlOperator, RexNode> inputRefUse =
+ getUsageMap(inputRef);
Pair<SqlOperator, RexNode> use = Pair.of(op, literal);
- inputRefUse.getUsageList().add(use);
+ inputRefUse.usageList.add(use);
}
private InputRefUsage<SqlOperator, RexNode> getUsageMap(RexInputRef rex) {
@@ -352,26 +370,12 @@ public class RexImplicationChecker {
}
/**
- * DataStructure to store usage of InputRefs in expression
+ * Usage of a {@link RexInputRef} in an expression.
*/
-
private static class InputRefUsage<T1, T2> {
- private final List<Pair<T1, T2>> usageList =
- new ArrayList<Pair<T1, T2>>();
+ private final List<Pair<T1, T2>> usageList = new ArrayList<>();
private int usageCount = 0;
-
- public InputRefUsage() {}
-
- public int getUsageCount() {
- return usageCount;
- }
-
- public void incrUsage() {
- usageCount++;
- }
-
- public List<Pair<T1, T2>> getUsageList() {
- return usageList;
- }
}
}
+
+// End RexImplicationChecker.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
index 62b2d9d..14e836a 100644
--- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
+++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java
@@ -145,11 +145,19 @@ public class SubstitutionVisitor {
private static final Equivalence<List<?>> PAIRWISE_STRING_EQUIVALENCE =
(Equivalence) STRING_EQUIVALENCE.pairwise();
- protected static List<UnifyRule> unifyRules;
+ protected static final ImmutableList<UnifyRule> DEFAULT_RULES =
+ ImmutableList.<UnifyRule>of(
+// TrivialRule.INSTANCE,
+ ProjectToProjectUnifyRule.INSTANCE,
+ FilterToProjectUnifyRule.INSTANCE,
+// ProjectToFilterUnifyRule.INSTANCE,
+ FilterToFilterUnifyRule.INSTANCE,
+ AggregateToAggregateUnifyRule.INSTANCE,
+ AggregateOnProjectToAggregateUnifyRule.INSTANCE);
- private static final Map<Pair<Class, Class>, List<UnifyRule>> RULE_MAP =
+ private final ImmutableList<UnifyRule> rules;
+ private final Map<Pair<Class, Class>, List<UnifyRule>> ruleMap =
new HashMap<>();
-
private final RelOptCluster cluster;
private final Holder query;
private final MutableRel target;
@@ -174,8 +182,16 @@ public class SubstitutionVisitor {
* Assumes no rule needs more than 2 slots. */
protected final MutableRel[] slots = new MutableRel[2];
+ /** Creates a SubstitutionVisitor with the default rule set. */
public SubstitutionVisitor(RelNode target_, RelNode query_) {
+ this(target_, query_, DEFAULT_RULES);
+ }
+
+ /** Creates a SubstitutionVisitor. */
+ public SubstitutionVisitor(RelNode target_, RelNode query_,
+ ImmutableList<UnifyRule> rules) {
this.cluster = target_.getCluster();
+ this.rules = rules;
this.query = Holder.of(toMutable(query_));
this.target = toMutable(target_);
final Set<MutableRel> parents = Sets.newIdentityHashSet();
@@ -201,28 +217,6 @@ public class SubstitutionVisitor {
visitor.go(query);
allNodes.removeAll(parents);
queryLeaves = ImmutableList.copyOf(allNodes);
- initUnifyRules();
- initRuleMap();
- }
-
- public void initUnifyRules() {
- unifyRules =
- ImmutableList.<UnifyRule>of(
-// TrivialRule.INSTANCE,
- ProjectToProjectUnifyRule.INSTANCE,
- FilterToProjectUnifyRule.INSTANCE,
-// ProjectToFilterUnifyRule.INSTANCE,
- FilterToFilterUnifyRule.INSTANCE,
- AggregateToAggregateUnifyRule.INSTANCE,
- AggregateOnProjectToAggregateUnifyRule.INSTANCE);
- }
-
- public void initRuleMap() {
- this.RULE_MAP.clear();
- }
-
- public MutableRel[] getSlots() {
- return slots;
}
private static MutableRel toMutable(RelNode rel) {
@@ -619,23 +613,23 @@ public class SubstitutionVisitor {
return rule.apply(call);
}
- private static List<UnifyRule> applicableRules(MutableRel query,
+ private List<UnifyRule> applicableRules(MutableRel query,
MutableRel target) {
final Class queryClass = query.getClass();
final Class targetClass = target.getClass();
final Pair<Class, Class> key = Pair.of(queryClass, targetClass);
- List<UnifyRule> list = RULE_MAP.get(key);
+ List<UnifyRule> list = ruleMap.get(key);
if (list == null) {
final ImmutableList.Builder<UnifyRule> builder =
ImmutableList.builder();
- for (UnifyRule rule : unifyRules) {
+ for (UnifyRule rule : rules) {
//noinspection unchecked
if (mightMatch(rule, queryClass, targetClass)) {
builder.add(rule);
}
}
list = builder.build();
- RULE_MAP.put(key, list);
+ ruleMap.put(key, list);
}
return list;
}
@@ -648,6 +642,7 @@ public class SubstitutionVisitor {
/** Exception thrown to exit a matcher. Not really an error. */
protected static class MatchFailed extends ControlFlowException {
+ @SuppressWarnings("ThrowableInstanceNeverThrown")
public static final MatchFailed INSTANCE = new MatchFailed();
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
index a941347..c0fe8a8 100644
--- a/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
+++ b/core/src/main/java/org/apache/calcite/plan/VisitorDataContext.java
@@ -67,17 +67,17 @@ public class VisitorDataContext implements DataContext {
return null;
}
}
- public static DataContext getDataContext(RelNode targetRel, LogicalFilter queryRel) {
- return getDataContext(targetRel.getRowType(), queryRel.getCondition());
+ public static DataContext of(RelNode targetRel, LogicalFilter queryRel) {
+ return of(targetRel.getRowType(), queryRel.getCondition());
}
- public static DataContext getDataContext(RelDataType rowType, RexNode rex) {
- int size = rowType.getFieldList().size();
- Object[] values = new Object[size];
- List<RexNode> operands = ((RexCall) rex).getOperands();
+ public static DataContext of(RelDataType rowType, RexNode rex) {
+ final int size = rowType.getFieldList().size();
+ final Object[] values = new Object[size];
+ final List<RexNode> operands = ((RexCall) rex).getOperands();
final RexNode firstOperand = operands.get(0);
final RexNode secondOperand = operands.get(1);
- final Pair<Integer, ? extends Object> value = getValue(firstOperand, secondOperand);
+ final Pair<Integer, ?> value = getValue(firstOperand, secondOperand);
if (value != null) {
int index = value.getKey();
values[index] = value.getValue();
@@ -87,12 +87,12 @@ public class VisitorDataContext implements DataContext {
}
}
- public static DataContext getDataContext(RelDataType rowType, List<Pair<RexInputRef,
- RexNode>> usgList) {
- int size = rowType.getFieldList().size();
- Object[] values = new Object[size];
- for (Pair<RexInputRef, RexNode> elem: usgList) {
- Pair<Integer, ? extends Object> value = getValue(elem.getKey(), elem.getValue());
+ public static DataContext of(RelDataType rowType,
+ List<Pair<RexInputRef, RexNode>> usageList) {
+ final int size = rowType.getFieldList().size();
+ final Object[] values = new Object[size];
+ for (Pair<RexInputRef, RexNode> elem : usageList) {
+ Pair<Integer, ?> value = getValue(elem.getKey(), elem.getValue());
if (value == null) {
return null;
}
@@ -102,46 +102,40 @@ public class VisitorDataContext implements DataContext {
return new VisitorDataContext(values);
}
- public static Pair<Integer, ? extends Object> getValue(RexNode inputRef, RexNode literal) {
+ public static Pair<Integer, ?> getValue(RexNode inputRef, RexNode literal) {
inputRef = removeCast(inputRef);
literal = removeCast(literal);
if (inputRef instanceof RexInputRef
&& literal instanceof RexLiteral) {
- Integer index = ((RexInputRef) inputRef).getIndex();
+ final int index = ((RexInputRef) inputRef).getIndex();
Object value = ((RexLiteral) literal).getValue();
final RelDataType type = inputRef.getType();
switch (type.getSqlTypeName()) {
case INTEGER:
if (value instanceof BigDecimal) {
- final Integer intValue = new Integer(((BigDecimal) value).intValue());
- return Pair.of(index, intValue);
+ return Pair.of(index, ((BigDecimal) value).intValue());
}
case DOUBLE:
if (value instanceof BigDecimal) {
- return Pair.of(index,
- new Double(((BigDecimal) value).doubleValue()));
+ return Pair.of(index, ((BigDecimal) value).doubleValue());
}
case REAL:
if (value instanceof BigDecimal) {
- return Pair.of(index,
- new Float(((BigDecimal) value).floatValue()));
+ return Pair.of(index, ((BigDecimal) value).floatValue());
}
case BIGINT:
if (value instanceof BigDecimal) {
- return Pair.of(index,
- new Long(((BigDecimal) value).longValue()));
+ return Pair.of(index, ((BigDecimal) value).longValue());
}
case SMALLINT:
if (value instanceof BigDecimal) {
- return Pair.of(index,
- new Short(((BigDecimal) value).shortValue()));
+ return Pair.of(index, ((BigDecimal) value).shortValue());
}
case TINYINT:
if (value instanceof BigDecimal) {
- return Pair.of(index,
- new Short(((BigDecimal) value).byteValue()));
+ return Pair.of(index, (short) ((BigDecimal) value).byteValue());
}
case DECIMAL:
if (value instanceof BigDecimal) {
@@ -158,13 +152,12 @@ public class VisitorDataContext implements DataContext {
}
case CHAR:
if (value instanceof NlsString) {
- // TODO: Support coallation. Not supported in {@link #NlsString} compare too.
+ // TODO: Support collation. Not supported in NlsString compare too.
final NlsString nl = (NlsString) value;
- Character c = new Character(nl.getValue().charAt(0));
- return Pair.of(index, c);
+ return Pair.of(index, nl.getValue().charAt(0));
}
default:
- //TODO: Support few more supported cases
+ // TODO: Support few more supported cases
return Pair.of(index, value);
}
}
@@ -184,3 +177,5 @@ public class VisitorDataContext implements DataContext {
return inputRef;
}
}
+
+// End VisitorDataContext.java
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
index 302a02e..52b5497 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateFilterTransposeRule.java
@@ -113,7 +113,7 @@ public class AggregateFilterTransposeRule extends RelOptRule {
if (aggregate.indicator) {
ImmutableList.Builder<ImmutableBitSet> newGroupingSetsBuilder =
ImmutableList.builder();
- for (ImmutableBitSet groupingSet: aggregate.getGroupSets()) {
+ for (ImmutableBitSet groupingSet : aggregate.getGroupSets()) {
final ImmutableBitSet.Builder newGroupingSet =
ImmutableBitSet.builder();
for (int c : groupingSet) {
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
index 5f78489..df55f0d 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/AggregateUnionTransposeRule.java
@@ -156,7 +156,7 @@ public class AggregateUnionTransposeRule extends RelOptRule {
private List<AggregateCall> transformAggCalls(RelNode input, int groupCount,
List<AggregateCall> origCalls) {
final List<AggregateCall> newCalls = Lists.newArrayList();
- for (Ord<AggregateCall> ord: Ord.zip(origCalls)) {
+ for (Ord<AggregateCall> ord : Ord.zip(origCalls)) {
final AggregateCall origCall = ord.e;
if (origCall.isDistinct()
|| !SUPPORTED_AGGREGATES.containsKey(origCall.getAggregation()
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
index 720e33d..b364ef3 100644
--- a/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
+++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterAggregateTransposeRule.java
@@ -131,7 +131,7 @@ public class FilterAggregateTransposeRule extends RelOptRule {
// If grouping sets are used, the filter can be pushed if
// the columns referenced in the predicate are present in
// all the grouping sets.
- for (ImmutableBitSet groupingSet: aggregate.getGroupSets()) {
+ for (ImmutableBitSet groupingSet : aggregate.getGroupSets()) {
if (!groupingSet.contains(rCols)) {
return false;
}
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/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 244a241..0bdb82c 100644
--- a/core/src/main/java/org/apache/calcite/sql/SqlKind.java
+++ b/core/src/main/java/org/apache/calcite/sql/SqlKind.java
@@ -728,6 +728,26 @@ public enum SqlKind {
LESS_THAN, GREATER_THAN,
GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL);
+ /** Returns the kind that corresponds to this operator but in the opposite
+ * direction. Or returns this, if this kind is not reversible.
+ *
+ * <p>For example, {@code GREATER_THAN.reverse()} returns {@link #LESS_THAN}.
+ */
+ public SqlKind reverse() {
+ switch (this) {
+ case GREATER_THAN:
+ return LESS_THAN;
+ case GREATER_THAN_OR_EQUAL:
+ return LESS_THAN_OR_EQUAL;
+ case LESS_THAN:
+ return GREATER_THAN;
+ case LESS_THAN_OR_EQUAL:
+ return GREATER_THAN_OR_EQUAL;
+ default:
+ return this;
+ }
+ }
+
/**
* Returns whether this {@code SqlKind} belongs to a given category.
*
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
index b56c928..d4780a6 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexExecutorTest.java
@@ -31,6 +31,7 @@ import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.tools.Frameworks;
import org.apache.calcite.util.NlsString;
+import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import org.junit.Assert;
@@ -38,6 +39,7 @@ import org.junit.Test;
import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Calendar;
import java.util.List;
import static org.hamcrest.CoreMatchers.equalTo;
@@ -70,48 +72,48 @@ public class RexExecutorTest {
/** Tests an executor that uses variables stored in a {@link DataContext}.
* Can change the value of the variable and execute again. */
@Test public void testVariableExecution() throws Exception {
- check(new Action() {
- public void check(RexBuilder rexBuilder, RexExecutorImpl executor) {
- Object[] values = new Object[1];
- final DataContext testContext = new TestDataContext(values);
- final RelDataType varchar = rexBuilder.getTypeFactory().createSqlType(
- SqlTypeName.VARCHAR);
- final RelDataType integer = rexBuilder.getTypeFactory().createSqlType(
- SqlTypeName.INTEGER);
- // calcite is internally creating the creating the input ref via a
- // RexRangeRef
- // which eventually leads to a RexInputRef. So we are good.
- final RexInputRef input = rexBuilder.makeInputRef(varchar, 0);
- final RexNode lengthArg = rexBuilder.makeLiteral(3, integer, true);
- final RexNode substr =
- rexBuilder.makeCall(SqlStdOperatorTable.SUBSTRING, input,
- lengthArg);
- ImmutableList<RexNode> constExps = ImmutableList.of(substr);
-
- final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory();
- final RelDataType rowType = typeFactory.builder()
- .add("someStr", varchar)
- .build();
-
- final RexExecutable exec = executor.getExecutable(rexBuilder,
- constExps, rowType);
- exec.setDataContext(testContext);
- values[0] = "Hello World";
- Object[] result = exec.execute();
- assertTrue(result[0] instanceof String);
- assertThat((String) result[0], equalTo("llo World"));
- values[0] = "Calcite";
- result = exec.execute();
- assertTrue(result[0] instanceof String);
- assertThat((String) result[0], equalTo("lcite"));
- }
- });
+ check(
+ new Action() {
+ public void check(RexBuilder rexBuilder, RexExecutorImpl executor) {
+ Object[] values = new Object[1];
+ final DataContext testContext = new TestDataContext(values);
+ final RelDataTypeFactory typeFactory = rexBuilder.getTypeFactory();
+ final RelDataType varchar =
+ typeFactory.createSqlType(SqlTypeName.VARCHAR);
+ final RelDataType integer =
+ typeFactory.createSqlType(SqlTypeName.INTEGER);
+ // Calcite is internally creating the input ref via a RexRangeRef
+ // which eventually leads to a RexInputRef. So we are good.
+ final RexInputRef input = rexBuilder.makeInputRef(varchar, 0);
+ final RexNode lengthArg = rexBuilder.makeLiteral(3, integer, true);
+ final RexNode substr =
+ rexBuilder.makeCall(SqlStdOperatorTable.SUBSTRING, input,
+ lengthArg);
+ ImmutableList<RexNode> constExps = ImmutableList.of(substr);
+
+ final RelDataType rowType = typeFactory.builder()
+ .add("someStr", varchar)
+ .build();
+
+ final RexExecutable exec = executor.getExecutable(rexBuilder,
+ constExps, rowType);
+ exec.setDataContext(testContext);
+ values[0] = "Hello World";
+ Object[] result = exec.execute();
+ assertTrue(result[0] instanceof String);
+ assertThat((String) result[0], equalTo("llo World"));
+ values[0] = "Calcite";
+ result = exec.execute();
+ assertTrue(result[0] instanceof String);
+ assertThat((String) result[0], equalTo("lcite"));
+ }
+ });
}
@Test public void testConstant() throws Exception {
check(new Action() {
public void check(RexBuilder rexBuilder, RexExecutorImpl executor) {
- final List<RexNode> reducedValues = new ArrayList<RexNode>();
+ final List<RexNode> reducedValues = new ArrayList<>();
final RexLiteral ten = rexBuilder.makeExactLiteral(BigDecimal.TEN);
executor.reduce(rexBuilder, ImmutableList.<RexNode>of(ten),
reducedValues);
@@ -123,10 +125,68 @@ public class RexExecutorTest {
});
}
+ /** Reduces several expressions to constants. */
+ @Test public void testConstant2() throws Exception {
+ // Same as testConstant; 10 -> 10
+ checkConstant(10L,
+ new Function<RexBuilder, RexNode>() {
+ public RexNode apply(RexBuilder rexBuilder) {
+ return rexBuilder.makeExactLiteral(BigDecimal.TEN);
+ }
+ });
+ // 10 + 1 -> 11
+ checkConstant(11L,
+ new Function<RexBuilder, RexNode>() {
+ public RexNode apply(RexBuilder rexBuilder) {
+ return rexBuilder.makeCall(SqlStdOperatorTable.PLUS,
+ rexBuilder.makeExactLiteral(BigDecimal.TEN),
+ rexBuilder.makeExactLiteral(BigDecimal.ONE));
+ }
+ });
+ // date 'today' <= date 'today' -> true
+ checkConstant(true,
+ new Function<RexBuilder, RexNode>() {
+ public RexNode apply(RexBuilder rexBuilder) {
+ Calendar calendar = Calendar.getInstance();
+ return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
+ rexBuilder.makeDateLiteral(calendar),
+ rexBuilder.makeDateLiteral(calendar));
+ }
+ });
+ // date 'today' < date 'today' -> false
+ checkConstant(false,
+ new Function<RexBuilder, RexNode>() {
+ public RexNode apply(RexBuilder rexBuilder) {
+ Calendar calendar = Calendar.getInstance();
+ return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN,
+ rexBuilder.makeDateLiteral(calendar),
+ rexBuilder.makeDateLiteral(calendar));
+ }
+ });
+ }
+
+ private void checkConstant(final Object operand,
+ final Function<RexBuilder, RexNode> function) throws Exception {
+ check(
+ new Action() {
+ public void check(RexBuilder rexBuilder, RexExecutorImpl executor) {
+ final List<RexNode> reducedValues = new ArrayList<>();
+ final RexNode expression = function.apply(rexBuilder);
+ assert expression != null;
+ executor.reduce(rexBuilder, ImmutableList.of(expression),
+ reducedValues);
+ assertThat(reducedValues.size(), equalTo(1));
+ assertThat(reducedValues.get(0), instanceOf(RexLiteral.class));
+ assertThat(((RexLiteral) reducedValues.get(0)).getValue2(),
+ equalTo(operand));
+ }
+ });
+ }
+
@Test public void testSubstring() throws Exception {
check(new Action() {
public void check(RexBuilder rexBuilder, RexExecutorImpl executor) {
- final List<RexNode> reducedValues = new ArrayList<RexNode>();
+ final List<RexNode> reducedValues = new ArrayList<>();
final RexLiteral hello =
rexBuilder.makeCharLiteral(
new NlsString("Hello world!", null, null));
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/test/java/org/apache/calcite/test/DiffRepository.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/DiffRepository.java b/core/src/test/java/org/apache/calcite/test/DiffRepository.java
index fb6b325..572dde9 100644
--- a/core/src/test/java/org/apache/calcite/test/DiffRepository.java
+++ b/core/src/test/java/org/apache/calcite/test/DiffRepository.java
@@ -435,7 +435,6 @@ public class DiffRepository {
tag,
expected2Canonical,
actualCanonical);
- amend(expected, actual);
} catch (ComparisonFailure e) {
amend(expected, actual);
throw e;
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/5a365609/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
index 7e693b0..720798e 100644
--- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java
@@ -27,6 +27,7 @@ import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexExecutorImpl;
import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexLiteral;
import org.apache.calcite.rex.RexNode;
import org.apache.calcite.schema.SchemaPlus;
import org.apache.calcite.schema.Schemas;
@@ -34,9 +35,9 @@ import org.apache.calcite.server.CalciteServerStatement;
import org.apache.calcite.sql.SqlCollation;
import org.apache.calcite.sql.fun.SqlStdOperatorTable;
import org.apache.calcite.tools.Frameworks;
+import org.apache.calcite.util.Holder;
import org.apache.calcite.util.NlsString;
-import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
@@ -50,399 +51,320 @@ import java.sql.Timestamp;
import java.util.Calendar;
/**
- * Tests the RexImplication checker
+ * Unit tests for {@link RexImplicationChecker}.
*/
public class RexImplicationCheckerTest {
//~ Instance fields --------------------------------------------------------
- private RexBuilder rexBuilder = null;
- private RexNode bl;
- private RexNode i;
- private RexNode dec;
- private RexNode lg;
- private RexNode sh;
- private RexNode by;
- private RexNode fl;
- private RexNode dt;
- private RexNode ch;
- private RexNode ts;
- private RexNode t;
-
- private RelDataType boolRelDataType;
- private RelDataType intRelDataType;
- private RelDataType decRelDataType;
- private RelDataType longRelDataType;
- private RelDataType shortDataType;
- private RelDataType byteDataType;
- private RelDataType floatDataType;
- private RelDataType charDataType;
- private RelDataType dateDataType;
- private RelDataType timeStampDataType;
- private RelDataType timeDataType;
- private RelDataTypeFactory typeFactory;
- private RexImplicationChecker checker;
- private RelDataType rowType;
- private RexExecutorImpl executor;
-
//~ Methods ----------------------------------------------------------------
- @Before
- public void setUp() {
- typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
- rexBuilder = new RexBuilder(typeFactory);
- boolRelDataType = typeFactory.createJavaType(Boolean.class);
- intRelDataType = typeFactory.createJavaType(Integer.class);
- decRelDataType = typeFactory.createJavaType(Double.class);
- longRelDataType = typeFactory.createJavaType(Long.class);
- shortDataType = typeFactory.createJavaType(Short.class);
- byteDataType = typeFactory.createJavaType(Byte.class);
- floatDataType = typeFactory.createJavaType(Float.class);
- charDataType = typeFactory.createJavaType(Character.class);
- dateDataType = typeFactory.createJavaType(Date.class);
- timeStampDataType = typeFactory.createJavaType(Timestamp.class);
- timeDataType = typeFactory.createJavaType(Time.class);
-
- bl = new RexInputRef(
- 0,
- typeFactory.createTypeWithNullability(boolRelDataType, true));
- i = new RexInputRef(
- 1,
- typeFactory.createTypeWithNullability(intRelDataType, true));
- dec = new RexInputRef(
- 2,
- typeFactory.createTypeWithNullability(decRelDataType, true));
- lg = new RexInputRef(
- 3,
- typeFactory.createTypeWithNullability(longRelDataType, true));
- sh = new RexInputRef(
- 4,
- typeFactory.createTypeWithNullability(shortDataType, true));
- by = new RexInputRef(
- 5,
- typeFactory.createTypeWithNullability(byteDataType, true));
- fl = new RexInputRef(
- 6,
- typeFactory.createTypeWithNullability(floatDataType, true));
- ch = new RexInputRef(
- 7,
- typeFactory.createTypeWithNullability(charDataType, true));
- dt = new RexInputRef(
- 8,
- typeFactory.createTypeWithNullability(dateDataType, true));
- ts = new RexInputRef(
- 9,
- typeFactory.createTypeWithNullability(timeStampDataType, true));
- t = new RexInputRef(
- 10,
- typeFactory.createTypeWithNullability(timeDataType, true));
-
- rowType = typeFactory.builder()
- .add("bool", boolRelDataType)
- .add("int", intRelDataType)
- .add("dec", decRelDataType)
- .add("long", longRelDataType)
- .add("short", shortDataType)
- .add("byte", byteDataType)
- .add("float", floatDataType)
- .add("char", charDataType)
- .add("date", dateDataType)
- .add("timestamp", timeStampDataType)
- .add("time", timeDataType)
- .build();
-
- Frameworks.withPrepare(
- new Frameworks.PrepareAction<Void>() {
- public Void apply(RelOptCluster cluster,
- RelOptSchema relOptSchema,
- SchemaPlus rootSchema,
- CalciteServerStatement statement) {
- DataContext dataContext =
- Schemas.createDataContext(statement.getConnection());
- executor = new RexExecutorImpl(dataContext);
- return null;
- }
- });
-
- checker = new RexImplicationChecker(rexBuilder, executor, rowType);
- }
-
- private void checkImplies(RexNode node1, RexNode node2) {
- assertTrue(node1.toString() + " doesnot imply " + node2.toString()
- + " when it should.", checker.implies(node1, node2));
- }
-
- private void checkNotImplies(RexNode node1, RexNode node2) {
- assertFalse(node1.toString() + " implies " + node2.toString()
- + " when it should not", checker.implies(node1, node2));
- }
-
// Simple Tests for Operators
@Test public void testSimpleGreaterCond() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(30)));
-
- RexNode node3 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(30)));
-
- RexNode node4 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- RexNode node5 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.EQUALS,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(30)));
-
- RexNode node6 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.NOT_EQUALS,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- checkImplies(node2, node1);
- checkNotImplies(node1, node2);
- checkNotImplies(node1, node3);
- checkImplies(node3, node1);
- checkImplies(node5, node1);
- checkNotImplies(node1, node5);
- checkNotImplies(node1, node6);
- checkNotImplies(node4, node6);
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.gt(f.i, f.literal(10));
+ final RexNode node2 = f.gt(f.i, f.literal(30));
+ final RexNode node3 = f.ge(f.i, f.literal(30));
+ final RexNode node4 = f.ge(f.i, f.literal(10));
+ final RexNode node5 = f.eq(f.i, f.literal(30));
+ final RexNode node6 = f.ne(f.i, f.literal(10));
+
+ f.checkImplies(node2, node1);
+ f.checkNotImplies(node1, node2);
+ f.checkNotImplies(node1, node3);
+ f.checkImplies(node3, node1);
+ f.checkImplies(node5, node1);
+ f.checkNotImplies(node1, node5);
+ f.checkNotImplies(node1, node6);
+ f.checkNotImplies(node4, node6);
// TODO: Need to support Identity
- //checkImplies(node1, node1);
- //checkImplies(node3, node3);
+ //f.checkImplies(node1, node1);
+ //f.checkImplies(node3, node3);
}
@Test public void testSimpleLesserCond() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(30)));
-
- RexNode node3 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(30)));
-
- RexNode node4 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- RexNode node5 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.EQUALS,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- RexNode node6 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.NOT_EQUALS,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
-
- checkImplies(node1, node2);
- checkNotImplies(node2, node1);
- checkImplies(node1, node3);
- checkNotImplies(node3, node1);
- checkImplies(node5, node2);
- checkNotImplies(node2, node5);
- checkNotImplies(node1, node5);
- checkNotImplies(node1, node6);
- checkNotImplies(node4, node6);
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.lt(f.i, f.literal(10));
+ final RexNode node2 = f.lt(f.i, f.literal(30));
+ final RexNode node3 = f.le(f.i, f.literal(30));
+ final RexNode node4 = f.le(f.i, f.literal(10));
+ final RexNode node5 = f.eq(f.i, f.literal(10));
+ final RexNode node6 = f.ne(f.i, f.literal(10));
+
+ f.checkImplies(node1, node2);
+ f.checkNotImplies(node2, node1);
+ f.checkImplies(node1, node3);
+ f.checkNotImplies(node3, node1);
+ f.checkImplies(node5, node2);
+ f.checkNotImplies(node2, node5);
+ f.checkNotImplies(node1, node5);
+ f.checkNotImplies(node1, node6);
+ f.checkNotImplies(node4, node6);
// TODO: Need to support Identity
- //checkImplies(node1, node1);
- //checkImplies(node3, node3);
+ //f.checkImplies(node1, node1);
+ //f.checkImplies(node3, node3);
}
@Test public void testSimpleEq() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.EQUALS,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(30)));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.NOT_EQUALS,
- i,
- rexBuilder.makeExactLiteral(new BigDecimal(10)));
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.eq(f.i, f.literal(30));
+ final RexNode node2 = f.ne(f.i, f.literal(10));
//Check Identity
- checkImplies(node1, node1);
+ f.checkImplies(node1, node1);
//TODO: Support Identity
- // checkImplies(node2, node2);
- checkImplies(node1, node2);
- checkNotImplies(node2, node1);
+ // f.checkImplies(node2, node2);
+ f.checkImplies(node1, node2);
+ f.checkNotImplies(node2, node1);
}
// Simple Tests for DataTypes
@Test public void testSimpleDec() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN,
- dec,
- rexBuilder.makeApproxLiteral(new BigDecimal(30.9)));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN,
- dec,
- rexBuilder.makeApproxLiteral(new BigDecimal(40.33)));
-
- checkImplies(node1, node2);
- checkNotImplies(node2, node1);
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.lt(f.dec, f.floatLiteral(30.9));
+ final RexNode node2 = f.lt(f.dec, f.floatLiteral(40.33));
+
+ f.checkImplies(node1, node2);
+ f.checkNotImplies(node2, node1);
}
@Test public void testSimpleBoolean() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.EQUALS,
- bl,
- rexBuilder.makeLiteral(true));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.EQUALS,
- bl,
- rexBuilder.makeLiteral(false));
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.eq(f.bl, f.rexBuilder.makeLiteral(true));
+ final RexNode node2 = f.eq(f.bl, f.rexBuilder.makeLiteral(false));
//TODO: Need to support false => true
- //checkImplies(node2, node1);
- checkNotImplies(node1, node2);
+ //f.checkImplies(node2, node1);
+ f.checkNotImplies(node1, node2);
}
@Test public void testSimpleLong() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- lg,
- rexBuilder.makeLiteral(new Long(324324L), longRelDataType, true));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN,
- lg,
- rexBuilder.makeLiteral(new Long(324325L), longRelDataType, true));
-
- checkImplies(node2, node1);
- checkNotImplies(node1, node2);
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.ge(f.lg, f.longLiteral(324324L));
+ final RexNode node2 = f.gt(f.lg, f.longLiteral(324325L));
+
+ f.checkImplies(node2, node1);
+ f.checkNotImplies(node1, node2);
}
@Test public void testSimpleShort() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- sh,
- rexBuilder.makeLiteral(new Short((short) 10), shortDataType, true));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- sh,
- rexBuilder.makeLiteral(new Short((short) 11), shortDataType, true));
-
- checkImplies(node2, node1);
- checkNotImplies(node1, node2);
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.ge(f.sh, f.shortLiteral((short) 10));
+ final RexNode node2 = f.ge(f.sh, f.shortLiteral((short) 11));
+
+ f.checkImplies(node2, node1);
+ f.checkNotImplies(node1, node2);
}
@Test public void testSimpleChar() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- ch,
- rexBuilder.makeCharLiteral(new NlsString("b", null, SqlCollation.COERCIBLE)));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- ch,
- rexBuilder.makeCharLiteral(new NlsString("a", null, SqlCollation.COERCIBLE)));
-
- checkImplies(node1, node2);
- checkNotImplies(node2, node1);
+ final Fixture f = new Fixture();
+ final RexNode node1 = f.ge(f.ch, f.charLiteral("b"));
+ final RexNode node2 = f.ge(f.ch, f.charLiteral("a"));
+
+ f.checkImplies(node1, node2);
+ f.checkNotImplies(node2, node1);
}
+ @Ignore("work in progress")
@Test public void testSimpleDate() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.GREATER_THAN_OR_EQUAL,
- dt,
- rexBuilder.makeDateLiteral(Calendar.getInstance()));
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.EQUALS,
- dt,
- rexBuilder.makeDateLiteral(Calendar.getInstance()));
-
- checkImplies(node2, node1);
- checkNotImplies(node1, node2);
+ final Fixture f = new Fixture();
+ final Calendar instance = Calendar.getInstance();
+ final RexNode node1 = f.ge(f.dt, f.rexBuilder.makeDateLiteral(instance));
+ final RexNode node2 = f.eq(f.dt, f.rexBuilder.makeDateLiteral(instance));
+
+ f.checkImplies(node2, node1);
+ f.checkNotImplies(node1, node2);
}
@Ignore("work in progress")
@Test public void testSimpleTimeStamp() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
- ts,
- rexBuilder.makeTimestampLiteral(Calendar.getInstance(),
- timeStampDataType.getPrecision()));
-
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
- ts,
- rexBuilder.makeTimestampLiteral(Calendar.getInstance(),
- timeStampDataType.getPrecision()));
-
- checkImplies(node1, node2);
- checkNotImplies(node2, node1);
+ final Fixture f = new Fixture();
+ final Calendar calendar = Calendar.getInstance();
+ final RexNode node1 = f.le(f.ts, f.timestampLiteral(calendar));
+ final RexNode node2 = f.le(f.ts, f.timestampLiteral(calendar));
+
+ f.checkImplies(node1, node2);
+ f.checkNotImplies(node2, node1);
}
@Ignore("work in progress")
@Test public void testSimpleTime() {
- RexNode node1 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
- t,
- rexBuilder.makeTimeLiteral(Calendar.getInstance(),
- timeDataType.getPrecision()));
-
-
- RexNode node2 =
- rexBuilder.makeCall(
- SqlStdOperatorTable.LESS_THAN_OR_EQUAL,
- t,
- rexBuilder.makeTimestampLiteral(Calendar.getInstance(),
- timeDataType.getPrecision()));
-
- checkImplies(node1, node2);
- checkNotImplies(node2, node1);
+ final Fixture f = new Fixture();
+ final Calendar calendar = Calendar.getInstance();
+ final RexNode node1 = f.le(f.ts, f.timeLiteral(calendar));
+ final RexNode node2 = f.le(f.ts, f.timeLiteral(calendar));
+
+ f.checkImplies(node1, node2);
+ f.checkNotImplies(node2, node1);
}
+ /** Contains all the nourishment a test case could possibly need.
+ *
+ * <p>We put the data in here, rather than as fields in the test case, so that
+ * the data can be garbage-collected as soon as the test has executed.
+ */
+ private static class Fixture {
+ private final RexBuilder rexBuilder;
+ private final RexNode bl;
+ private final RexNode i;
+ private final RexNode dec;
+ private final RexNode lg;
+ private final RexNode sh;
+ private final RexNode by;
+ private final RexNode fl;
+ private final RexNode dt;
+ private final RexNode ch;
+ private final RexNode ts;
+ private final RexNode t;
+
+ private final RelDataType boolRelDataType;
+ private final RelDataType intRelDataType;
+ private final RelDataType decRelDataType;
+ private final RelDataType longRelDataType;
+ private final RelDataType shortDataType;
+ private final RelDataType byteDataType;
+ private final RelDataType floatDataType;
+ private final RelDataType charDataType;
+ private final RelDataType dateDataType;
+ private final RelDataType timeStampDataType;
+ private final RelDataType timeDataType;
+ private final RelDataTypeFactory typeFactory;
+ private final RexImplicationChecker checker;
+ private final RelDataType rowType;
+ private final RexExecutorImpl executor;
+
+ public Fixture() {
+ typeFactory = new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ rexBuilder = new RexBuilder(typeFactory);
+ boolRelDataType = typeFactory.createJavaType(Boolean.class);
+ intRelDataType = typeFactory.createJavaType(Integer.class);
+ decRelDataType = typeFactory.createJavaType(Double.class);
+ longRelDataType = typeFactory.createJavaType(Long.class);
+ shortDataType = typeFactory.createJavaType(Short.class);
+ byteDataType = typeFactory.createJavaType(Byte.class);
+ floatDataType = typeFactory.createJavaType(Float.class);
+ charDataType = typeFactory.createJavaType(Character.class);
+ dateDataType = typeFactory.createJavaType(Date.class);
+ timeStampDataType = typeFactory.createJavaType(Timestamp.class);
+ timeDataType = typeFactory.createJavaType(Time.class);
+
+ bl = ref(0, this.boolRelDataType);
+ i = ref(1, intRelDataType);
+ dec = ref(2, decRelDataType);
+ lg = ref(3, longRelDataType);
+ sh = ref(4, shortDataType);
+ by = ref(5, byteDataType);
+ fl = ref(6, floatDataType);
+ ch = ref(7, charDataType);
+ dt = ref(8, dateDataType);
+ ts = ref(9, timeStampDataType);
+ t = ref(10, timeDataType);
+
+ rowType = typeFactory.builder()
+ .add("bool", this.boolRelDataType)
+ .add("int", intRelDataType)
+ .add("dec", decRelDataType)
+ .add("long", longRelDataType)
+ .add("short", shortDataType)
+ .add("byte", byteDataType)
+ .add("float", floatDataType)
+ .add("char", charDataType)
+ .add("date", dateDataType)
+ .add("timestamp", timeStampDataType)
+ .add("time", timeDataType)
+ .build();
+
+ final Holder<RexExecutorImpl> holder = Holder.of(null);
+ Frameworks.withPrepare(
+ new Frameworks.PrepareAction<Void>() {
+ public Void apply(RelOptCluster cluster,
+ RelOptSchema relOptSchema,
+ SchemaPlus rootSchema,
+ CalciteServerStatement statement) {
+ DataContext dataContext =
+ Schemas.createDataContext(statement.getConnection());
+ holder.set(new RexExecutorImpl(dataContext));
+ return null;
+ }
+ });
+
+ executor = holder.get();
+ checker = new RexImplicationChecker(rexBuilder, executor, rowType);
+ }
+
+ RexInputRef ref(int i, RelDataType type) {
+ return new RexInputRef(i,
+ typeFactory.createTypeWithNullability(type, true));
+ }
+
+ RexLiteral literal(int i) {
+ return rexBuilder.makeExactLiteral(new BigDecimal(i));
+ }
+
+ RexNode gt(RexNode node1, RexNode node2) {
+ return rexBuilder.makeCall(SqlStdOperatorTable.GREATER_THAN, node1, node2);
+ }
+
+ RexNode ge(RexNode node1, RexNode node2) {
+ return rexBuilder.makeCall(
+ SqlStdOperatorTable.GREATER_THAN_OR_EQUAL, node1, node2);
+ }
+
+ RexNode eq(RexNode node1, RexNode node2) {
+ return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, node1, node2);
+ }
+
+ RexNode ne(RexNode node1, RexNode node2) {
+ return rexBuilder.makeCall(SqlStdOperatorTable.NOT_EQUALS, node1, node2);
+ }
+
+ RexNode lt(RexNode node1, RexNode node2) {
+ return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN, node1, node2);
+ }
+
+ RexNode le(RexNode node1, RexNode node2) {
+ return rexBuilder.makeCall(SqlStdOperatorTable.LESS_THAN_OR_EQUAL, node1,
+ node2);
+ }
+
+ RexNode longLiteral(long value) {
+ return rexBuilder.makeLiteral(value, longRelDataType, true);
+ }
+
+ RexNode shortLiteral(short value) {
+ return rexBuilder.makeLiteral(value, shortDataType, true);
+ }
+
+ RexLiteral floatLiteral(double value) {
+ return rexBuilder.makeApproxLiteral(new BigDecimal(value));
+ }
+
+ RexLiteral charLiteral(String z) {
+ return rexBuilder.makeCharLiteral(
+ new NlsString(z, null, SqlCollation.COERCIBLE));
+ }
+
+ RexNode timestampLiteral(Calendar calendar) {
+ return rexBuilder.makeTimestampLiteral(
+ calendar, timeStampDataType.getPrecision());
+ }
+
+ RexNode timeLiteral(Calendar calendar) {
+ return rexBuilder.makeTimestampLiteral(
+ calendar, timeDataType.getPrecision());
+ }
+
+ void checkImplies(RexNode node1, RexNode node2) {
+ final String message =
+ node1 + " does not imply " + node2 + " when it should";
+ assertTrue(message, checker.implies(node1, node2));
+ }
+
+ void checkNotImplies(RexNode node1, RexNode node2) {
+ final String message =
+ node1 + " does implies " + node2 + " when it should not";
+ assertFalse(message, checker.implies(node1, node2));
+ }
+ }
}
+
+// End RexImplicationCheckerTest.java
[3/3] incubator-calcite git commit: [CALCITE-787] Star table wrongly
assigned to materialized view (Amogh Margoor)
Posted by jh...@apache.org.
[CALCITE-787] Star table wrongly assigned to materialized view (Amogh Margoor)
Close apache/incubator-calcite#103
Project: http://git-wip-us.apache.org/repos/asf/incubator-calcite/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-calcite/commit/d38e6b1b
Tree: http://git-wip-us.apache.org/repos/asf/incubator-calcite/tree/d38e6b1b
Diff: http://git-wip-us.apache.org/repos/asf/incubator-calcite/diff/d38e6b1b
Branch: refs/heads/master
Commit: d38e6b1b92497c0e4e43847c4ab756e888dac1f3
Parents: 5a36560
Author: Amogh Margoor <am...@qubole.com>
Authored: Mon Jul 6 08:26:18 2015 -0400
Committer: Julian Hyde <jh...@apache.org>
Committed: Thu Jul 16 15:22:15 2015 -0700
----------------------------------------------------------------------
.../calcite/plan/RelOptMaterialization.java | 9 +++-
.../calcite/plan/volcano/VolcanoPlanner.java | 7 ++-
.../org/apache/calcite/test/LatticeTest.java | 56 ++++++++++++++++++++
3 files changed, 68 insertions(+), 4 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/d38e6b1b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
index 8c9eaae..f945e65 100644
--- a/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
+++ b/core/src/main/java/org/apache/calcite/plan/RelOptMaterialization.java
@@ -77,8 +77,12 @@ public class RelOptMaterialization {
/**
* Converts a relational expression to one that uses a
* {@link org.apache.calcite.schema.impl.StarTable}.
- * The relational expression is already in leaf-join-form, per
+ *
+ * <p>The relational expression is already in leaf-join-form, per
* {@link #toLeafJoinForm(org.apache.calcite.rel.RelNode)}.
+ *
+ * @return Rewritten expression, or null if expression cannot be rewritten
+ * to use the star
*/
public static RelNode tryUseStar(RelNode rel,
final RelOptTable starRelOptTable) {
@@ -191,7 +195,8 @@ public class RelOptMaterialization {
}
});
if (rel2 == rel) {
- return rel;
+ // No rewrite happened.
+ return null;
}
final Program program = Programs.hep(
ImmutableList.of(ProjectFilterTransposeRule.INSTANCE,
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/d38e6b1b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
index 6634cf3..e57a760 100644
--- a/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
+++ b/core/src/main/java/org/apache/calcite/plan/volcano/VolcanoPlanner.java
@@ -391,8 +391,11 @@ public class VolcanoPlanner extends AbstractRelOptPlanner {
// First, if the materialization is in terms of a star table, rewrite
// the query in terms of the star table.
if (materialization.starTable != null) {
- root = RelOptMaterialization.tryUseStar(
- root, materialization.starRelOptTable);
+ RelNode newRoot = RelOptMaterialization.tryUseStar(root,
+ materialization.starRelOptTable);
+ if (newRoot != null) {
+ root = newRoot;
+ }
}
// Push filters to the bottom, and combine projects on top.
http://git-wip-us.apache.org/repos/asf/incubator-calcite/blob/d38e6b1b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/calcite/test/LatticeTest.java b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
index 92d121b..77e7943 100644
--- a/core/src/test/java/org/apache/calcite/test/LatticeTest.java
+++ b/core/src/test/java/org/apache/calcite/test/LatticeTest.java
@@ -624,6 +624,62 @@ public class LatticeTest {
}
/** Test case for
+ * <a href="https://issues.apache.org/jira/browse/CALCITE-787">[CALCITE-787]
+ * Star table wrongly assigned to materialized view</a>. */
+ @Test public void testOneLatticeOneMV() {
+ final AtomicInteger counter = new AtomicInteger();
+ final Class<JdbcTest.EmpDeptTableFactory> clazz =
+ JdbcTest.EmpDeptTableFactory.class;
+
+ final String mv = " materializations: [\n"
+ + " {\n"
+ + " table: \"m0\",\n"
+ + " view: \"m0v\",\n"
+ + " sql: \"select * from \\\"foodmart\\\".\\\"sales_fact_1997\\\" "
+ + "where \\\"product_id\\\" = 10\" "
+ + " }\n"
+ + " ]\n";
+
+ final String model = ""
+ + "{\n"
+ + " version: '1.0',\n"
+ + " schemas: [\n"
+ + JdbcTest.FOODMART_SCHEMA
+ + ",\n"
+ + " {\n"
+ + " name: 'adhoc',\n"
+ + " tables: [\n"
+ + " {\n"
+ + " name: 'EMPLOYEES',\n"
+ + " type: 'custom',\n"
+ + " factory: '"
+ + clazz.getName()
+ + "',\n"
+ + " operand: {'foo': true, 'bar': 345}\n"
+ + " }\n"
+ + " ],\n"
+ + " lattices: " + "[" + INVENTORY_LATTICE
+ + " ]\n"
+ + " },\n"
+ + " {\n"
+ + " name: 'mat',\n"
+ + mv
+ + " }\n"
+ + " ]\n"
+ + "}";
+
+ CalciteAssert.model(model)
+ .withDefaultSchema("foodmart")
+ .query("select * from \"foodmart\".\"sales_fact_1997\" where \"product_id\" = 10")
+ .enableMaterializations(true)
+ .substitutionMatches(
+ CalciteAssert.checkRel(
+ "EnumerableTableScan(table=[[mat, m0]])\n",
+ counter));
+ assertThat(counter.intValue(), equalTo(1));
+ }
+
+ /** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-760">[CALCITE-760]
* Aggregate recommender blows up if row count estimate is too high</a>. */
@Ignore