Repository: calcite Updated Branches: refs/heads/master e0eeb1b45 -> a9ac3e486
[CALCITE-1995] Remove terms from Filter if predicates indicate they are always true or false Also, strengthen range terms like "field >= constant" to point ranges "field = constant" based on predicates. Project: http://git-wip-us.apache.org/repos/asf/calcite/repo Commit: http://git-wip-us.apache.org/repos/asf/calcite/commit/a9ac3e48 Tree: http://git-wip-us.apache.org/repos/asf/calcite/tree/a9ac3e48 Diff: http://git-wip-us.apache.org/repos/asf/calcite/diff/a9ac3e48 Branch: refs/heads/master Commit: a9ac3e486863b36009a6e5e0f066134ad34b3334 Parents: e0eeb1b Author: Julian Hyde <[email protected]> Authored: Sat Sep 30 16:28:29 2017 -0700 Committer: Julian Hyde <[email protected]> Committed: Wed Oct 4 10:25:07 2017 -0700 ---------------------------------------------------------------------- .../adapter/enumerable/EnumerableCalc.java | 5 +- .../calcite/plan/SubstitutionVisitor.java | 4 +- .../calcite/rel/metadata/RelMdPredicates.java | 4 +- .../rel/rules/AbstractMaterializedViewRule.java | 7 +- .../rel/rules/FilterProjectTransposeRule.java | 5 +- .../rel/rules/ReduceExpressionsRule.java | 2 +- .../java/org/apache/calcite/rex/RexProgram.java | 7 +- .../apache/calcite/rex/RexProgramBuilder.java | 16 +- .../org/apache/calcite/rex/RexSimplify.java | 328 ++++++++++++++----- .../java/org/apache/calcite/rex/RexUtil.java | 42 +-- .../org/apache/calcite/tools/RelBuilder.java | 7 +- .../calcite/test/MaterializationTest.java | 4 +- .../apache/calcite/test/RelOptRulesTest.java | 8 + .../calcite/test/RexImplicationCheckerTest.java | 5 +- .../org/apache/calcite/test/RexProgramTest.java | 80 ++++- .../org/apache/calcite/test/RelOptRulesTest.xml | 25 ++ .../calcite/adapter/druid/DruidRules.java | 67 ++-- site/_docs/adapter.md | 8 +- 18 files changed, 475 insertions(+), 149 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java index 25c882a..9e2018b 100644 --- a/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java +++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/EnumerableCalc.java @@ -28,6 +28,7 @@ import org.apache.calcite.linq4j.tree.MemberDeclaration; import org.apache.calcite.linq4j.tree.ParameterExpression; import org.apache.calcite.linq4j.tree.Types; import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelTraitSet; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollationTraitDef; @@ -150,8 +151,10 @@ public class EnumerableCalc extends Calc implements EnumerableRel { inputJavaType); final RexBuilder rexBuilder = getCluster().getRexBuilder(); + final RelMetadataQuery mq = RelMetadataQuery.instance(); + final RelOptPredicateList predicates = mq.getPulledUpPredicates(child); final RexSimplify simplify = - new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR); + new RexSimplify(rexBuilder, predicates, false, RexUtil.EXECUTOR); final RexProgram program = this.program.normalize(rexBuilder, simplify); BlockStatement moveNextBody; http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/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 5fd8f6e..ffc036a 100644 --- a/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java +++ b/core/src/main/java/org/apache/calcite/plan/SubstitutionVisitor.java @@ -179,7 +179,9 @@ public class SubstitutionVisitor { this.cluster = target_.getCluster(); final RexExecutor executor = Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR); - this.simplify = new RexSimplify(cluster.getRexBuilder(), false, executor); + final RelOptPredicateList predicates = RelOptPredicateList.EMPTY; + this.simplify = + new RexSimplify(cluster.getRexBuilder(), predicates, false, executor); this.rules = rules; this.query = Holder.of(MutableRels.toMutable(query_)); this.target = MutableRels.toMutable(target_); http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java index 1b7dbc3..bf3a241 100644 --- a/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java +++ b/core/src/main/java/org/apache/calcite/rel/metadata/RelMdPredicates.java @@ -419,7 +419,9 @@ public class RelMdPredicates final RelOptCluster cluster = union.getCluster(); final RexExecutor executor = Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR); - final RexSimplify simplify = new RexSimplify(rB, true, executor); + final RelOptPredicateList predicates = RelOptPredicateList.EMPTY; + final RexSimplify simplify = + new RexSimplify(rB, predicates, true, executor); RexNode disjPred = simplify.simplifyOrs(finalResidualPreds); if (!disjPred.isAlwaysTrue()) { preds.add(disjPred); http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java index 7202b81..d563f8b 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/AbstractMaterializedViewRule.java @@ -41,6 +41,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.RexExecutor; import org.apache.calcite.rex.RexInputRef; import org.apache.calcite.rex.RexNode; import org.apache.calcite.rex.RexShuttle; @@ -176,9 +177,11 @@ public abstract class AbstractMaterializedViewRule extends RelOptRule { final RexBuilder rexBuilder = node.getCluster().getRexBuilder(); final RelMetadataQuery mq = RelMetadataQuery.instance(); final RelOptPlanner planner = call.getPlanner(); + final RexExecutor executor = + Util.first(planner.getExecutor(), RexUtil.EXECUTOR); + final RelOptPredicateList predicates = RelOptPredicateList.EMPTY; final RexSimplify simplify = - new RexSimplify(rexBuilder, true, - planner.getExecutor() != null ? planner.getExecutor() : RexUtil.EXECUTOR); + new RexSimplify(rexBuilder, predicates, true, executor); final List<RelOptMaterialization> materializations = (planner instanceof VolcanoPlanner) http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java index d774b2a..aaec8a8 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/FilterProjectTransposeRule.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.rel.rules; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptRuleCall; import org.apache.calcite.plan.RelOptRuleOperand; @@ -119,8 +120,10 @@ public class FilterProjectTransposeRule extends RelOptRule { final RelBuilder relBuilder = call.builder(); RelNode newFilterRel; if (copyFilter) { + final RelOptPredicateList predicates = RelOptPredicateList.EMPTY; final RexSimplify simplify = - new RexSimplify(relBuilder.getRexBuilder(), false, RexUtil.EXECUTOR); + new RexSimplify(relBuilder.getRexBuilder(), predicates, false, + RexUtil.EXECUTOR); newCondition = simplify.removeNullabilityCast(newCondition); newFilterRel = filter.copy(filter.getTraitSet(), project.getInput(), newCondition); http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java index b640c82..b5fd694 100644 --- a/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java +++ b/core/src/main/java/org/apache/calcite/rel/rules/ReduceExpressionsRule.java @@ -461,7 +461,7 @@ public abstract class ReduceExpressionsRule extends RelOptRule { final RexExecutor executor = Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR); final RexSimplify simplify = - new RexSimplify(rexBuilder, unknownAsFalse, executor); + new RexSimplify(rexBuilder, predicates, unknownAsFalse, executor); // Simplify predicates in place boolean reduced = reduceExpressionsInternal(rel, simplify, expList, http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rex/RexProgram.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgram.java b/core/src/main/java/org/apache/calcite/rex/RexProgram.java index 2adcb0e..c91c0f0 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexProgram.java +++ b/core/src/main/java/org/apache/calcite/rex/RexProgram.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.rex; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollations; @@ -786,8 +787,10 @@ public class RexProgram { @Deprecated // to be removed before 2.0 public RexProgram normalize(RexBuilder rexBuilder, boolean simplify) { - return normalize(rexBuilder, - simplify ? new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR) : null); + final RelOptPredicateList predicates = RelOptPredicateList.EMPTY; + return normalize(rexBuilder, simplify + ? new RexSimplify(rexBuilder, predicates, false, RexUtil.EXECUTOR) + : null); } //~ Inner Classes ---------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java index aaea0c2..2964a48 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java +++ b/core/src/main/java/org/apache/calcite/rex/RexProgramBuilder.java @@ -16,6 +16,7 @@ */ package org.apache.calcite.rex; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.rel.type.RelDataTypeField; @@ -323,7 +324,10 @@ public class RexProgramBuilder { * sub-expression exists. */ private RexLocalRef registerInternal(RexNode expr, boolean force) { - expr = new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR).simplify(expr); + final RexSimplify simplify = + new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + RexUtil.EXECUTOR); + expr = simplify.simplify(expr); RexLocalRef ref; final Pair<String, String> key; @@ -543,10 +547,14 @@ public class RexProgramBuilder { final RexNode condition, final RelDataType outputRowType, boolean normalize, - boolean simplify) { + boolean simplify_) { + RexSimplify simplify = null; + if (simplify_) { + simplify = new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + RexUtil.EXECUTOR); + } return new RexProgramBuilder(rexBuilder, inputRowType, exprList, - projectList, condition, outputRowType, normalize, - simplify ? new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR) : null); + projectList, condition, outputRowType, normalize, simplify); } @Deprecated // to be removed before 2.0 http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/rex/RexSimplify.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java index 286a697..f826c77 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java +++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java @@ -17,6 +17,7 @@ package org.apache.calcite.rex; import org.apache.calcite.linq4j.Ord; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.Strong; import org.apache.calcite.rel.core.Project; @@ -50,6 +51,7 @@ import java.util.Set; */ public class RexSimplify { public final RexBuilder rexBuilder; + private final RelOptPredicateList predicates; final boolean unknownAsFalse; private final RexExecutor executor; @@ -57,16 +59,24 @@ public class RexSimplify { * Creates a RexSimplify. * * @param rexBuilder Rex builder + * @param predicates Predicates known to hold on input fields * @param unknownAsFalse Whether to convert UNKNOWN values to FALSE * @param executor Executor for constant reduction, not null */ - public RexSimplify(RexBuilder rexBuilder, boolean unknownAsFalse, - RexExecutor executor) { + public RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates, + boolean unknownAsFalse, RexExecutor executor) { this.rexBuilder = Preconditions.checkNotNull(rexBuilder); + this.predicates = Preconditions.checkNotNull(predicates); this.unknownAsFalse = unknownAsFalse; this.executor = Preconditions.checkNotNull(executor); } + @Deprecated // to be removed before 2.0 + public RexSimplify(RexBuilder rexBuilder, boolean unknownAsFalse, + RexExecutor executor) { + this(rexBuilder, RelOptPredicateList.EMPTY, unknownAsFalse, executor); + } + //~ Methods ---------------------------------------------------------------- /** Returns a RexSimplify the same as this but with a specified @@ -74,7 +84,15 @@ public class RexSimplify { public RexSimplify withUnknownAsFalse(boolean unknownAsFalse) { return unknownAsFalse == this.unknownAsFalse ? this - : new RexSimplify(rexBuilder, unknownAsFalse, executor); + : new RexSimplify(rexBuilder, predicates, unknownAsFalse, executor); + } + + /** Returns a RexSimplify the same as this but with a specified + * {@link #predicates} value. */ + public RexSimplify withPredicates(RelOptPredicateList predicates) { + return predicates == this.predicates + ? this + : new RexSimplify(rexBuilder, predicates, unknownAsFalse, executor); } /** Simplifies a boolean expression, always preserving its type and its @@ -146,6 +164,13 @@ public class RexSimplify { // e must be a comparison (=, >, >=, <, <=, !=) private RexNode simplifyComparison(RexCall e) { + //noinspection unchecked + return simplifyComparison(e, Comparable.class); + } + + // e must be a comparison (=, >, >=, <, <=, !=) + private <C extends Comparable<C>> RexNode simplifyComparison(RexCall e, + Class<C> clazz) { final List<RexNode> operands = new ArrayList<>(e.operands); simplifyList(operands); @@ -177,14 +202,13 @@ public class RexSimplify { if (o0.isA(SqlKind.LITERAL) && o1.isA(SqlKind.LITERAL) && o0.getType().equals(o1.getType())) { - final Comparable v0 = ((RexLiteral) o0).getValueAs(Comparable.class); - final Comparable v1 = ((RexLiteral) o1).getValueAs(Comparable.class); + final C v0 = ((RexLiteral) o0).getValueAs(clazz); + final C v1 = ((RexLiteral) o1).getValueAs(clazz); if (v0 == null || v1 == null) { return unknownAsFalse ? rexBuilder.makeLiteral(false) : rexBuilder.makeNullLiteral(e.getType()); } - @SuppressWarnings("unchecked") final int comparisonResult = v0.compareTo(v1); switch (e.getKind()) { case EQUALS: @@ -205,10 +229,13 @@ public class RexSimplify { } // If none of the arguments were simplified, return the call unchanged. + final RexNode e2; if (operands.equals(e.operands)) { - return e; + e2 = e; + } else { + e2 = rexBuilder.makeCall(e.op, operands); } - return rexBuilder.makeCall(e.op, operands); + return simplifyUsingPredicates(e2, clazz); } /** @@ -522,10 +549,6 @@ public class RexSimplify { if (terms.isEmpty() && notTerms.isEmpty()) { return rexBuilder.makeLiteral(true); } - if (terms.size() == 1 && notTerms.isEmpty()) { - // Make sure "x OR y OR x" (a single-term conjunction) gets simplified. - return simplify(terms.get(0)); - } // If one of the not-disjunctions is a disjunction that is wholly // contained in the disjunctions list, the expression is not // satisfiable. @@ -552,6 +575,12 @@ public class RexSimplify { * returns UNKNOWN it will be interpreted as FALSE. */ RexNode simplifyAnd2ForUnknownAsFalse(List<RexNode> terms, List<RexNode> notTerms) { + //noinspection unchecked + return simplifyAnd2ForUnknownAsFalse(terms, notTerms, Comparable.class); + } + + private <C extends Comparable<C>> RexNode simplifyAnd2ForUnknownAsFalse( + List<RexNode> terms, List<RexNode> notTerms, Class<C> clazz) { for (RexNode term : terms) { if (term.isAlwaysFalse()) { return rexBuilder.makeLiteral(false); @@ -566,12 +595,31 @@ public class RexSimplify { } // Try to simplify the expression final Multimap<String, Pair<String, RexNode>> equalityTerms = ArrayListMultimap.create(); - final Map<String, Pair<Range, List<RexNode>>> rangeTerms = new HashMap<>(); + final Map<String, Pair<Range<C>, List<RexNode>>> rangeTerms = + new HashMap<>(); final Map<String, String> equalityConstantTerms = new HashMap<>(); final Set<String> negatedTerms = new HashSet<>(); final Set<String> nullOperands = new HashSet<>(); final Set<RexNode> notNullOperands = new LinkedHashSet<>(); final Set<String> comparedOperands = new HashSet<>(); + + // Add the predicates from the source to the range terms. + for (RexNode predicate : predicates.pulledUpPredicates) { + final Comparison comparison = Comparison.of(predicate); + if (comparison != null + && comparison.kind != SqlKind.NOT_EQUALS) { // not supported yet + final C v0 = comparison.literal.getValueAs(clazz); + if (v0 != null) { + final RexNode result = processRange(rexBuilder, terms, rangeTerms, + predicate, comparison.ref, v0, comparison.kind); + if (result != null) { + // Not satisfiable + return result; + } + } + } + } + for (int i = 0; i < terms.size(); i++) { RexNode term = terms.get(i); if (!RexUtil.isDeterministic(term)) { @@ -613,32 +661,25 @@ public class RexSimplify { RexCall rightCast = (RexCall) right; comparedOperands.add(rightCast.getOperands().get(0).toString()); } - final boolean leftRef = RexUtil.isReferenceOrAccess(left, true); - final boolean rightRef = RexUtil.isReferenceOrAccess(right, true); - final boolean leftConstant = left.isA(SqlKind.LITERAL); - final boolean rightConstant = right.isA(SqlKind.LITERAL); + final Comparison comparison = Comparison.of(term); // Check for comparison with null values - if (leftConstant && ((RexLiteral) left).getValue() == null - || rightConstant && ((RexLiteral) right).getValue() == null) { + if (comparison != null + && comparison.literal.getValue() == null) { return rexBuilder.makeLiteral(false); } // Check for equality on different constants. If the same ref or CAST(ref) // is equal to different constants, this condition cannot be satisfied, // and hence it can be evaluated to FALSE if (term.getKind() == SqlKind.EQUALS) { - if (leftRef && rightConstant) { - final String literal = right.toString(); - final String prevLiteral = equalityConstantTerms.put(left.toString(), literal); + if (comparison != null) { + final String literal = comparison.literal.toString(); + final String prevLiteral = + equalityConstantTerms.put(comparison.ref.toString(), literal); if (prevLiteral != null && !literal.equals(prevLiteral)) { return rexBuilder.makeLiteral(false); } - } else if (leftConstant && rightRef) { - final String literal = left.toString(); - final String prevLiteral = equalityConstantTerms.put(right.toString(), literal); - if (prevLiteral != null && !literal.equals(prevLiteral)) { - return rexBuilder.makeLiteral(false); - } - } else if (leftRef && rightRef) { + } else if (RexUtil.isReferenceOrAccess(left, true) + && RexUtil.isReferenceOrAccess(right, true)) { equalityTerms.put(left.toString(), Pair.of(right.toString(), term)); } } @@ -655,23 +696,20 @@ public class RexSimplify { negatedTerms.add(invertNegatedTerm.toString()); } } - // Range - SqlKind comparison = null; - RexNode ref = null; - RexLiteral constant = null; - if (leftRef && rightConstant) { - comparison = term.getKind(); - ref = left; - constant = (RexLiteral) right; - } else if (leftConstant && rightRef) { - comparison = term.getKind().reverse(); - constant = (RexLiteral) left; - ref = right; + // Remove terms that are implied by predicates on the input, + // or weaken terms that are partially implied. + // E.g. given predicate "x >= 5" and term "x between 3 and 10" + // we weaken to term to "x between 5 and 10". + final RexNode term2 = simplifyUsingPredicates(term, clazz); + if (term2 != term) { + terms.set(i, term = term2); } + // Range if (comparison != null - && comparison != SqlKind.NOT_EQUALS) { // NOT_EQUALS not supported + && comparison.kind != SqlKind.NOT_EQUALS) { // not supported yet + final C constant = comparison.literal.getValueAs(clazz); final RexNode result = processRange(rexBuilder, terms, rangeTerms, - term, ref, constant, comparison); + term, comparison.ref, constant, comparison.kind); if (result != null) { // Not satisfiable return result; @@ -739,7 +777,7 @@ public class RexSimplify { // Example #1. x AND y AND z AND NOT (x AND y) - not satisfiable // Example #2. x AND y AND NOT (x AND y) - not satisfiable // Example #3. x AND y AND NOT (x AND y AND z) - may be satisfiable - final Set<String> termsSet = new HashSet<String>(RexUtil.strings(terms)); + final Set<String> termsSet = new HashSet<>(RexUtil.strings(terms)); for (RexNode notDisjunction : notTerms) { if (!RexUtil.isDeterministic(notDisjunction)) { continue; @@ -764,6 +802,84 @@ public class RexSimplify { return RexUtil.composeConjunction(rexBuilder, terms, false); } + private <C extends Comparable<C>> RexNode simplifyUsingPredicates(RexNode e, + Class<C> clazz) { + final Comparison comparison = Comparison.of(e); + // Check for comparison with null values + if (comparison == null + || comparison.kind == SqlKind.NOT_EQUALS + || comparison.literal.getValue() == null) { + return e; + } + final C v0 = comparison.literal.getValueAs(clazz); + final Range<C> range = range(comparison.kind, v0); + final Range<C> range2 = + residue(comparison.ref, range, predicates.pulledUpPredicates, + clazz); + if (range2 == null) { + // Term is impossible to satisfy given these predicates + return rexBuilder.makeLiteral(false); + } else if (range2.equals(range)) { + // no change + return e; + } else if (range2.equals(Range.all())) { + // Term is always satisfied given these predicates + return rexBuilder.makeLiteral(true); + } else if (range2.lowerEndpoint().equals(range2.upperEndpoint())) { + // range is now a point; it's worth simplifying + return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, comparison.ref, + rexBuilder.makeLiteral(range2.lowerEndpoint(), + comparison.literal.getType(), comparison.literal.getTypeName())); + } else { + // range has been reduced but it's not worth simplifying + return e; + } + } + + /** Weakens a term so that it checks only what is not implied by predicates. + * + * <p>The term is broken into "ref comparison constant", + * for example "$0 < 5". + * + * <p>Examples: + * <ul> + * + * <li>{@code residue($0 < 10, [$0 < 5])} returns {@code true} + * + * <li>{@code residue($0 < 10, [$0 < 20, $0 > 0])} returns {@code $0 < 10} + * </ul> + */ + private <C extends Comparable<C>> Range<C> residue(RexNode ref, Range<C> r0, + List<RexNode> predicates, Class<C> clazz) { + for (RexNode predicate : predicates) { + switch (predicate.getKind()) { + case EQUALS: + case LESS_THAN: + case LESS_THAN_OR_EQUAL: + case GREATER_THAN: + case GREATER_THAN_OR_EQUAL: + final RexCall call = (RexCall) predicate; + if (call.operands.get(0).equals(ref) + && call.operands.get(1) instanceof RexLiteral) { + final RexLiteral literal = (RexLiteral) call.operands.get(1); + final C c1 = literal.getValueAs(clazz); + final Range<C> r1 = range(predicate.getKind(), c1); + if (r0.encloses(r1)) { + // Given these predicates, term is always satisfied. + // e.g. r0 is "$0 < 10", r1 is "$0 < 5" + return Range.all(); + } + if (r0.isConnected(r1)) { + return r0.intersection(r1); + } + // Ranges do not intersect. Return null meaning the empty range. + return null; + } + } + } + return r0; + } + /** Simplifies OR(x, x) into x, and similar. */ public RexNode simplifyOr(RexCall call) { assert call.getKind() == SqlKind.OR; @@ -837,39 +953,20 @@ public class RexSimplify { return e; } - private static RexNode processRange(RexBuilder rexBuilder, - List<RexNode> terms, Map<String, Pair<Range, List<RexNode>>> rangeTerms, - RexNode term, RexNode ref, RexLiteral constant, SqlKind comparison) { - final Comparable v0 = constant.getValueAs(Comparable.class); - Pair<Range, List<RexNode>> p = rangeTerms.get(ref.toString()); + private static <C extends Comparable<C>> RexNode processRange( + RexBuilder rexBuilder, List<RexNode> terms, + Map<String, Pair<Range<C>, List<RexNode>>> rangeTerms, RexNode term, + RexNode ref, C v0, SqlKind comparison) { + Pair<Range<C>, List<RexNode>> p = rangeTerms.get(ref.toString()); if (p == null) { - Range r; - switch (comparison) { - case EQUALS: - r = Range.singleton(v0); - break; - case LESS_THAN: - r = Range.lessThan(v0); - break; - case LESS_THAN_OR_EQUAL: - r = Range.atMost(v0); - break; - case GREATER_THAN: - r = Range.greaterThan(v0); - break; - case GREATER_THAN_OR_EQUAL: - r = Range.atLeast(v0); - break; - default: - throw new AssertionError(); - } rangeTerms.put(ref.toString(), - new Pair(r, ImmutableList.of(term))); + Pair.of(range(comparison, v0), + (List<RexNode>) ImmutableList.of(term))); } else { // Exists boolean removeUpperBound = false; boolean removeLowerBound = false; - Range r = p.left; + Range<C> r = p.left; switch (comparison) { case EQUALS: if (!r.contains(v0)) { @@ -877,10 +974,11 @@ public class RexSimplify { return rexBuilder.makeLiteral(false); } rangeTerms.put(ref.toString(), - new Pair(Range.singleton(v0), ImmutableList.of(term))); + Pair.of(Range.singleton(v0), + (List<RexNode>) ImmutableList.of(term))); // remove for (RexNode e : p.right) { - terms.set(terms.indexOf(e), rexBuilder.makeLiteral(true)); + Collections.replaceAll(terms, e, rexBuilder.makeLiteral(true)); } break; case LESS_THAN: { @@ -1027,31 +1125,99 @@ public class RexSimplify { if (removeUpperBound) { ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder(); for (RexNode e : p.right) { - if (e.isA(SqlKind.LESS_THAN) || e.isA(SqlKind.LESS_THAN_OR_EQUAL)) { - terms.set(terms.indexOf(e), rexBuilder.makeLiteral(true)); - } else { + switch (e.getKind()) { + case LESS_THAN: + case LESS_THAN_OR_EQUAL: + Collections.replaceAll(terms, e, rexBuilder.makeLiteral(true)); + break; + default: newBounds.add(e); } } newBounds.add(term); - rangeTerms.put(ref.toString(), new Pair(r, newBounds.build())); + rangeTerms.put(ref.toString(), + Pair.of(r, (List<RexNode>) newBounds.build())); } else if (removeLowerBound) { ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder(); for (RexNode e : p.right) { - if (e.isA(SqlKind.GREATER_THAN) || e.isA(SqlKind.GREATER_THAN_OR_EQUAL)) { - terms.set(terms.indexOf(e), rexBuilder.makeLiteral(true)); - } else { + switch (e.getKind()) { + case GREATER_THAN: + case GREATER_THAN_OR_EQUAL: + Collections.replaceAll(terms, e, rexBuilder.makeLiteral(true)); + break; + default: newBounds.add(e); } } newBounds.add(term); - rangeTerms.put(ref.toString(), new Pair(r, newBounds.build())); + rangeTerms.put(ref.toString(), + Pair.of(r, (List<RexNode>) newBounds.build())); } } // Default return null; } + private static <C extends Comparable<C>> Range<C> range(SqlKind comparison, + C c) { + switch (comparison) { + case EQUALS: + return Range.singleton(c); + case LESS_THAN: + return Range.lessThan(c); + case LESS_THAN_OR_EQUAL: + return Range.atMost(c); + case GREATER_THAN: + return Range.greaterThan(c); + case GREATER_THAN_OR_EQUAL: + return Range.atLeast(c); + default: + throw new AssertionError(); + } + } + + /** Comparison between a {@link RexInputRef} or {@link RexFieldAccess} and a + * literal. Literal may be on left or right side, and may be null. */ + private static class Comparison { + final RexNode ref; + final SqlKind kind; + final RexLiteral literal; + + private Comparison(RexNode ref, SqlKind kind, RexLiteral literal) { + this.ref = Preconditions.checkNotNull(ref); + this.kind = Preconditions.checkNotNull(kind); + this.literal = Preconditions.checkNotNull(literal); + } + + /** Creates a comparison, or returns null. */ + static Comparison of(RexNode e) { + switch (e.getKind()) { + case EQUALS: + case NOT_EQUALS: + case LESS_THAN: + case GREATER_THAN: + case LESS_THAN_OR_EQUAL: + case GREATER_THAN_OR_EQUAL: + final RexCall call = (RexCall) e; + final RexNode left = call.getOperands().get(0); + final RexNode right = call.getOperands().get(1); + switch (right.getKind()) { + case LITERAL: + if (RexUtil.isReferenceOrAccess(left, true)) { + return new Comparison(left, e.getKind(), (RexLiteral) right); + } + } + switch (left.getKind()) { + case LITERAL: + if (RexUtil.isReferenceOrAccess(right, true)) { + return new Comparison(right, e.getKind().reverse(), + (RexLiteral) left); + } + } + } + return null; + } + } } // End RexSimplify.java http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/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 b1e667e..216b27c 100644 --- a/core/src/main/java/org/apache/calcite/rex/RexUtil.java +++ b/core/src/main/java/org/apache/calcite/rex/RexUtil.java @@ -17,6 +17,7 @@ package org.apache.calcite.rex; import org.apache.calcite.linq4j.function.Predicate1; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.rel.RelCollation; import org.apache.calcite.rel.RelCollations; @@ -1673,8 +1674,8 @@ public class RexUtil { @Deprecated // to be removed before 2.0 public static RexNode simplifyPreservingType(RexBuilder rexBuilder, RexNode e) { - return new RexSimplify(rexBuilder, false, EXECUTOR) - .simplifyPreservingType(e); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + EXECUTOR).simplifyPreservingType(e); } /** @@ -1686,8 +1687,8 @@ public class RexUtil { */ @Deprecated // to be removed before 2.0 public static RexNode simplify(RexBuilder rexBuilder, RexNode e) { - return new RexSimplify(rexBuilder, false, EXECUTOR) - .simplify(e); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + EXECUTOR).simplify(e); } /** @@ -1717,8 +1718,8 @@ public class RexUtil { @Deprecated // to be removed before 2.0 public static RexNode simplify(RexBuilder rexBuilder, RexNode e, boolean unknownAsFalse) { - return new RexSimplify(rexBuilder, unknownAsFalse, EXECUTOR) - .simplify(e); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, + unknownAsFalse, EXECUTOR).simplify(e); } /** @@ -1730,15 +1731,15 @@ public class RexUtil { @Deprecated // to be removed before 2.0 public static RexNode simplifyAnds(RexBuilder rexBuilder, Iterable<? extends RexNode> nodes) { - return new RexSimplify(rexBuilder, false, EXECUTOR) - .simplifyAnds(nodes); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + EXECUTOR).simplifyAnds(nodes); } @Deprecated // to be removed before 2.0 public static RexNode simplifyAnds(RexBuilder rexBuilder, Iterable<? extends RexNode> nodes, boolean unknownAsFalse) { - return new RexSimplify(rexBuilder, unknownAsFalse, EXECUTOR) - .simplifyAnds(nodes); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, + unknownAsFalse, EXECUTOR).simplifyAnds(nodes); } /** Negates a logical expression by adding or removing a NOT. */ @@ -1788,22 +1789,23 @@ public class RexUtil { @Deprecated // to be removed before 2.0 public static RexNode simplifyAnd(RexBuilder rexBuilder, RexCall e, boolean unknownAsFalse) { - return new RexSimplify(rexBuilder, unknownAsFalse, EXECUTOR) - .simplifyAnd(e); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, + unknownAsFalse, EXECUTOR).simplifyAnd(e); } @Deprecated // to be removed before 2.0 public static RexNode simplifyAnd2(RexBuilder rexBuilder, List<RexNode> terms, List<RexNode> notTerms) { - return new RexSimplify(rexBuilder, false, EXECUTOR) - .simplifyAnd2(terms, notTerms); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + EXECUTOR).simplifyAnd2(terms, notTerms); } @Deprecated // to be removed before 2.0 public static RexNode simplifyAnd2ForUnknownAsFalse(RexBuilder rexBuilder, List<RexNode> terms, List<RexNode> notTerms) { - return new RexSimplify(rexBuilder, true, EXECUTOR) - .simplifyAnd2ForUnknownAsFalse(terms, notTerms); + final Class<Comparable> clazz = Comparable.class; + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, true, + EXECUTOR).simplifyAnd2ForUnknownAsFalse(terms, notTerms); } public static RexNode negate(RexBuilder rexBuilder, RexCall call) { @@ -1836,15 +1838,15 @@ public class RexUtil { @Deprecated // to be removed before 2.0 public static RexNode simplifyOr(RexBuilder rexBuilder, RexCall call) { - return new RexSimplify(rexBuilder, false, EXECUTOR) - .simplifyOr(call); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + EXECUTOR).simplifyOr(call); } @Deprecated // to be removed before 2.0 public static RexNode simplifyOrs(RexBuilder rexBuilder, List<RexNode> terms) { - return new RexSimplify(rexBuilder, false, EXECUTOR) - .simplifyOrs(terms); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + EXECUTOR).simplifyOrs(terms); } /** http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/main/java/org/apache/calcite/tools/RelBuilder.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java index e55015a..6499f42 100644 --- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java +++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java @@ -20,6 +20,7 @@ import org.apache.calcite.linq4j.Ord; import org.apache.calcite.plan.Context; import org.apache.calcite.plan.Contexts; import org.apache.calcite.plan.RelOptCluster; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.RelOptUtil; @@ -184,9 +185,11 @@ public class RelBuilder { final RexExecutor executor = Util.first(context.unwrap(RexExecutor.class), Util.first(cluster.getPlanner().getExecutor(), RexUtil.EXECUTOR)); - this.simplifier = new RexSimplify(cluster.getRexBuilder(), false, executor); + final RelOptPredicateList predicates = RelOptPredicateList.EMPTY; + this.simplifier = + new RexSimplify(cluster.getRexBuilder(), predicates, false, executor); this.simplifierUnknownAsFalse = - new RexSimplify(cluster.getRexBuilder(), true, executor); + new RexSimplify(cluster.getRexBuilder(), predicates, true, executor); } /** Creates a RelBuilder. */ http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/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 6a3bcc9..20093f1 100644 --- a/core/src/test/java/org/apache/calcite/test/MaterializationTest.java +++ b/core/src/test/java/org/apache/calcite/test/MaterializationTest.java @@ -20,6 +20,7 @@ import org.apache.calcite.adapter.java.ReflectiveSchema; import org.apache.calcite.jdbc.JavaTypeFactoryImpl; import org.apache.calcite.materialize.MaterializationService; import org.apache.calcite.plan.RelOptPlanner; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptRule; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.plan.SubstitutionVisitor; @@ -118,7 +119,8 @@ public class MaterializationTest { new JavaTypeFactoryImpl(RelDataTypeSystem.DEFAULT); private final RexBuilder rexBuilder = new RexBuilder(typeFactory); private final RexSimplify simplify = - new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR); + new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + RexUtil.EXECUTOR); @Test public void testScan() { CalciteAssert.that() http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java index d9aea0d..9b988ee 100644 --- a/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java +++ b/core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java @@ -2618,6 +2618,14 @@ public class RelOptRulesTest extends RelOptTestBase { transitiveInference(ReduceExpressionsRule.FILTER_INSTANCE); } + /** Test case for + * <a href="https://issues.apache.org/jira/browse/CALCITE-1995">[CALCITE-1995] + * Remove predicates from Filter if they can be proved to be always true or + * false</a>. */ + @Test public void testSimplifyFilter() throws Exception { + transitiveInference(ReduceExpressionsRule.FILTER_INSTANCE); + } + @Test public void testPullConstantIntoJoin() throws Exception { transitiveInference(ReduceExpressionsRule.JOIN_INSTANCE); } http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/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 4fb7369..074a0f8 100644 --- a/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexImplicationCheckerTest.java @@ -19,6 +19,7 @@ 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.RelOptPredicateList; import org.apache.calcite.plan.RelOptSchema; import org.apache.calcite.plan.RexImplicationChecker; import org.apache.calcite.rel.type.RelDataType; @@ -431,7 +432,9 @@ public class RexImplicationCheckerTest { }); executor = holder.get(); - simplify = new RexSimplify(rexBuilder, false, executor); + simplify = + new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + executor); checker = new RexImplicationChecker(rexBuilder, executor, rowType); } http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/test/java/org/apache/calcite/test/RexProgramTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java index b0467d6..d42cfbb 100644 --- a/core/src/test/java/org/apache/calcite/test/RexProgramTest.java +++ b/core/src/test/java/org/apache/calcite/test/RexProgramTest.java @@ -21,6 +21,7 @@ import org.apache.calcite.adapter.java.JavaTypeFactory; import org.apache.calcite.avatica.util.ByteString; import org.apache.calcite.jdbc.JavaTypeFactoryImpl; import org.apache.calcite.linq4j.QueryProvider; +import org.apache.calcite.plan.RelOptPredicateList; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.Strong; import org.apache.calcite.rel.type.RelDataType; @@ -109,7 +110,8 @@ public class RexProgramTest { rexBuilder = new RexBuilder(typeFactory); RexExecutor executor = new RexExecutorImpl(new DummyTestDataContext()); - simplify = new RexSimplify(rexBuilder, false, executor); + simplify = + new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, executor); trueLiteral = rexBuilder.makeLiteral(true); falseLiteral = rexBuilder.makeLiteral(false); final RelDataType intType = typeFactory.createSqlType(SqlTypeName.INTEGER); @@ -197,6 +199,13 @@ public class RexProgramTest { equalTo(expected)); } + private void checkSimplifyFilter(RexNode node, RelOptPredicateList predicates, + String expected) { + assertThat(simplify.withUnknownAsFalse(true).withPredicates(predicates) + .simplify(node).toString(), + equalTo(expected)); + } + /** Returns the number of nodes (including leaves) in a Rex tree. */ private static int nodeCount(RexNode node) { int n = 1; @@ -1278,9 +1287,9 @@ public class RexProgramTest { final RexNode cRef = rexBuilder.makeFieldAccess(range, 2); final RexNode dRef = rexBuilder.makeFieldAccess(range, 3); final RexLiteral literal1 = rexBuilder.makeExactLiteral(BigDecimal.ONE); + final RexLiteral literal5 = rexBuilder.makeExactLiteral(new BigDecimal(5)); final RexLiteral literal10 = rexBuilder.makeExactLiteral(BigDecimal.TEN); - // condition, and the inverse checkSimplifyFilter(and(le(aRef, literal1), gt(aRef, literal1)), "false"); @@ -1307,6 +1316,11 @@ public class RexProgramTest { checkSimplifyFilter(and(gt(aRef, literal10), ge(bRef, literal1), lt(aRef, literal10)), "false"); + // one "and" containing three "or"s + checkSimplifyFilter( + or(gt(aRef, literal10), gt(bRef, literal1), gt(aRef, literal10)), + "OR(>(?0.a, 10), >(?0.b, 1))"); + // case: trailing false and null, remove checkSimplifyFilter( case_(aRef, trueLiteral, bRef, trueLiteral, cRef, falseLiteral, dRef, falseLiteral, @@ -1314,6 +1328,65 @@ public class RexProgramTest { // condition with null value for range checkSimplifyFilter(and(gt(aRef, unknownLiteral), ge(bRef, literal1)), "false"); + + // range with no predicates; + // condition "a > 1 && a < 10 && a < 5" yields "a < 1 && a < 5" + checkSimplifyFilter( + and(gt(aRef, literal1), lt(aRef, literal10), lt(aRef, literal5)), + RelOptPredicateList.EMPTY, + "AND(>(?0.a, 1), <(?0.a, 5))"); + + // condition "a > 1 && a < 10 && a < 5" + // with pre-condition "a > 5" + // yields "false" + checkSimplifyFilter( + and(gt(aRef, literal1), lt(aRef, literal10), lt(aRef, literal5)), + RelOptPredicateList.of(rexBuilder, + ImmutableList.of(gt(aRef, literal5))), + "false"); + + // condition "a > 1 && a < 10 && a <= 5" + // with pre-condition "a >= 5" + // yields "a = 5" + // "a <= 5" would also be correct, just a little less concise. + checkSimplifyFilter( + and(gt(aRef, literal1), lt(aRef, literal10), le(aRef, literal5)), + RelOptPredicateList.of(rexBuilder, + ImmutableList.of(ge(aRef, literal5))), + "=(?0.a, 5)"); + + // condition "a > 1 && a < 10 && a < 5" + // with pre-condition "b < 10 && a > 5" + // yields "a > 1 and a < 5" + checkSimplifyFilter( + and(gt(aRef, literal1), lt(aRef, literal10), lt(aRef, literal5)), + RelOptPredicateList.of(rexBuilder, + ImmutableList.of(lt(bRef, literal10), ge(aRef, literal1))), + "AND(>(?0.a, 1), <(?0.a, 5))"); + + // condition "a > 1" + // with pre-condition "b < 10 && a > 5" + // yields "true" + checkSimplifyFilter(gt(aRef, literal1), + RelOptPredicateList.of(rexBuilder, + ImmutableList.of(lt(bRef, literal10), gt(aRef, literal5))), + "true"); + + // condition "a < 1" + // with pre-condition "b < 10 && a > 5" + // yields "false" + checkSimplifyFilter(lt(aRef, literal1), + RelOptPredicateList.of(rexBuilder, + ImmutableList.of(lt(bRef, literal10), gt(aRef, literal5))), + "false"); + + // condition "a > 5" + // with pre-condition "b < 10 && a >= 5" + // yields "a > 5" + checkSimplifyFilter(gt(aRef, literal5), + RelOptPredicateList.of(rexBuilder, + ImmutableList.of(lt(bRef, literal10), ge(aRef, literal5))), + ">(?0.a, 5)"); } /** Unit test for @@ -1794,7 +1867,8 @@ public class RexProgramTest { } private RexNode simplify(RexNode e) { - return new RexSimplify(rexBuilder, false, RexUtil.EXECUTOR).simplify(e); + return new RexSimplify(rexBuilder, RelOptPredicateList.EMPTY, false, + RexUtil.EXECUTOR).simplify(e); } } http://git-wip-us.apache.org/repos/asf/calcite/blob/a9ac3e48/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml ---------------------------------------------------------------------- diff --git a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml index ecf1e46..596f730 100644 --- a/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml +++ b/core/src/test/resources/org/apache/calcite/test/RelOptRulesTest.xml @@ -4673,6 +4673,31 @@ LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$ ]]> </Resource> </TestCase> + <TestCase name="testSimplifyFilter"> + <Resource name="sql"> + <![CDATA[select * +from (select * from sales.emp where deptno > 10) +where empno > 3 and deptno > 5]]> + </Resource> + <Resource name="planBefore"> + <![CDATA[ +LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8]) + LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8]) + LogicalFilter(condition=[AND(>($0, 3), >($7, 5))]) + LogicalFilter(condition=[>($7, 10)]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) +]]> + </Resource> + <Resource name="planAfter"> + <![CDATA[ +LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8]) + LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8]) + LogicalFilter(condition=[>($0, 3)]) + LogicalFilter(condition=[>($7, 10)]) + LogicalTableScan(table=[[CATALOG, SALES, EMP]]) +]]> + </Resource> + </TestCase> <TestCase name="testPullConstantIntoJoin"> <Resource name="sql"> <