Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to you under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.calcite.rex; import org.apache.calcite.linq4j.Ord; import org.apache.calcite.plan.RelOptUtil; import org.apache.calcite.plan.Strong; import org.apache.calcite.rel.core.Project; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.fun.SqlStdOperatorTable; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.util.Pair; import org.apache.calcite.util.Util; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.BoundType; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Range; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** * Context required to simplify a row-expression. */ public class RexSimplify { public final RexBuilder rexBuilder; final boolean unknownAsFalse; private final RexExecutor executor; /** * Creates a RexSimplify. * * @param rexBuilder Rex builder * @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) { this.rexBuilder = Preconditions.checkNotNull(rexBuilder); this.unknownAsFalse = unknownAsFalse; this.executor = Preconditions.checkNotNull(executor); } //~ Methods ---------------------------------------------------------------- /** Returns a RexSimplify the same as this but with a specified * {@link #unknownAsFalse} value. */ public RexSimplify withUnknownAsFalse(boolean unknownAsFalse) { return unknownAsFalse == this.unknownAsFalse ? this : new RexSimplify(rexBuilder, unknownAsFalse, executor); } /** Simplifies a boolean expression, always preserving its type and its * nullability. * * <p>This is useful if you are simplifying expressions in a * {@link Project}. */ public RexNode simplifyPreservingType(RexNode e) { final RexNode e2 = simplify(e); if (e2.getType() == e.getType()) { return e2; } final RexNode e3 = rexBuilder.makeCast(e.getType(), e2, true); if (e3.equals(e)) { return e; } return e3; } /** * Simplifies a boolean expression. * * <p>In particular:</p> * <ul> * <li>{@code simplify(x = 1 AND y = 2 AND NOT x = 1)} * returns {@code y = 2}</li> * <li>{@code simplify(x = 1 AND FALSE)} * returns {@code FALSE}</li> * </ul> * * <p>If the expression is a predicate in a WHERE clause, UNKNOWN values have * the same effect as FALSE. In situations like this, specify * {@code unknownAsFalse = true}, so and we can switch from 3-valued logic to * simpler 2-valued logic and make more optimizations. * * @param e Expression to simplify */ public RexNode simplify(RexNode e) { switch (e.getKind()) { case AND: return simplifyAnd((RexCall) e); case OR: return simplifyOr((RexCall) e); case NOT: return simplifyNot((RexCall) e); case CASE: return simplifyCase((RexCall) e); case CAST: return simplifyCast((RexCall) e); case IS_NULL: case IS_NOT_NULL: case IS_TRUE: case IS_NOT_TRUE: case IS_FALSE: case IS_NOT_FALSE: assert e instanceof RexCall; return simplifyIs((RexCall) e); case EQUALS: case GREATER_THAN: case GREATER_THAN_OR_EQUAL: case LESS_THAN: case LESS_THAN_OR_EQUAL: case NOT_EQUALS: return simplifyComparison((RexCall) e); default: return e; } } // e must be a comparison (=, >, >=, <, <=, !=) private RexNode simplifyComparison(RexCall e) { final List<RexNode> operands = new ArrayList<>(e.operands); simplifyList(operands); // Simplify "x <op> x" final RexNode o0 = operands.get(0); final RexNode o1 = operands.get(1); if (RexUtil.eq(o0, o1) && (unknownAsFalse || (!o0.getType().isNullable() && !o1.getType().isNullable()))) { switch (e.getKind()) { case EQUALS: case GREATER_THAN_OR_EQUAL: case LESS_THAN_OR_EQUAL: // "x = x" simplifies to "x is not null" (similarly <= and >=) return simplify(rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, o0)); default: // "x != x" simplifies to "false" (similarly < and >) return rexBuilder.makeLiteral(false); } } // Simplify "<literal1> <op> <literal2>" // For example, "1 = 2" becomes FALSE; // "1 != 1" becomes FALSE; // "1 != NULL" becomes UNKNOWN (or FALSE if unknownAsFalse); // "1 != '1'" is unchanged because the types are not the same. if (o0.isA(SqlKind.LITERAL) && o1.isA(SqlKind.LITERAL) && o0.getType().equals(o1.getType())) { final Comparable v0 = ((RexLiteral) o0).getValue(); final Comparable v1 = ((RexLiteral) o1).getValue(); 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: return rexBuilder.makeLiteral(comparisonResult == 0); case GREATER_THAN: return rexBuilder.makeLiteral(comparisonResult > 0); case GREATER_THAN_OR_EQUAL: return rexBuilder.makeLiteral(comparisonResult >= 0); case LESS_THAN: return rexBuilder.makeLiteral(comparisonResult < 0); case LESS_THAN_OR_EQUAL: return rexBuilder.makeLiteral(comparisonResult <= 0); case NOT_EQUALS: return rexBuilder.makeLiteral(comparisonResult != 0); default: throw new AssertionError(); } } // If none of the arguments were simplified, return the call unchanged. if (operands.equals(e.operands)) { return e; } return rexBuilder.makeCall(e.op, operands); } /** * Simplifies a conjunction of boolean expressions. */ public RexNode simplifyAnds(Iterable<? extends RexNode> nodes) { final List<RexNode> terms = new ArrayList<>(); final List<RexNode> notTerms = new ArrayList<>(); for (RexNode e : nodes) { RelOptUtil.decomposeConjunction(e, terms, notTerms); } simplifyList(terms); simplifyList(notTerms); if (unknownAsFalse) { return simplifyAnd2ForUnknownAsFalse(terms, notTerms); } return simplifyAnd2(terms, notTerms); } private void simplifyList(List<RexNode> terms) { for (int i = 0; i < terms.size(); i++) { terms.set(i, withUnknownAsFalse(false).simplify(terms.get(i))); } } private RexNode simplifyNot(RexCall call) { final RexNode a = call.getOperands().get(0); switch (a.getKind()) { case NOT: // NOT NOT x ==> x return simplify(((RexCall) a).getOperands().get(0)); } final SqlKind negateKind = a.getKind().negate(); if (a.getKind() != negateKind) { return simplify(rexBuilder.makeCall(RexUtil.op(negateKind), ImmutableList.of(((RexCall) a).getOperands().get(0)))); } final SqlKind negateKind2 = a.getKind().negateNullSafe(); if (a.getKind() != negateKind2) { return simplify(rexBuilder.makeCall(RexUtil.op(negateKind2), ((RexCall) a).getOperands())); } if (a.getKind() == SqlKind.AND) { // NOT distributivity for AND final List<RexNode> newOperands = new ArrayList<>(); for (RexNode operand : ((RexCall) a).getOperands()) { newOperands.add(simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operand))); } return simplify(rexBuilder.makeCall(SqlStdOperatorTable.OR, newOperands)); } if (a.getKind() == SqlKind.OR) { // NOT distributivity for OR final List<RexNode> newOperands = new ArrayList<>(); for (RexNode operand : ((RexCall) a).getOperands()) { newOperands.add(simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operand))); } return simplify(rexBuilder.makeCall(SqlStdOperatorTable.AND, newOperands)); } return call; } private RexNode simplifyIs(RexCall call) { final SqlKind kind = call.getKind(); final RexNode a = call.getOperands().get(0); final RexNode simplified = simplifyIs2(kind, a); if (simplified != null) { return simplified; } return call; } private RexNode simplifyIs2(SqlKind kind, RexNode a) { switch (kind) { case IS_NULL: // x IS NULL ==> FALSE (if x is not nullable) if (!a.getType().isNullable()) { return rexBuilder.makeLiteral(false); } break; case IS_NOT_NULL: // x IS NOT NULL ==> TRUE (if x is not nullable) RexNode simplified = simplifyIsNotNull(a); if (simplified != null) { return simplified; } break; case IS_TRUE: case IS_NOT_FALSE: // x IS TRUE ==> x (if x is not nullable) // x IS NOT FALSE ==> x (if x is not nullable) if (!a.getType().isNullable()) { return simplify(a); } break; case IS_FALSE: case IS_NOT_TRUE: // x IS NOT TRUE ==> NOT x (if x is not nullable) // x IS FALSE ==> NOT x (if x is not nullable) if (!a.getType().isNullable()) { return simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, a)); } break; } switch (a.getKind()) { case NOT: // (NOT x) IS TRUE ==> x IS FALSE // Similarly for IS NOT TRUE, IS FALSE, etc. // // Note that // (NOT x) IS TRUE !=> x IS FALSE // because of null values. final SqlOperator notKind = RexUtil.op(kind.negate()); final RexNode arg = ((RexCall) a).operands.get(0); return simplify(rexBuilder.makeCall(notKind, arg)); } RexNode a2 = simplify(a); if (a != a2) { return rexBuilder.makeCall(RexUtil.op(kind), ImmutableList.of(a2)); } return null; // cannot be simplified } private RexNode simplifyIsNotNull(RexNode a) { if (!a.getType().isNullable()) { return rexBuilder.makeLiteral(true); } switch (Strong.policy(a.getKind())) { case ANY: final List<RexNode> operands = new ArrayList<>(); for (RexNode operand : ((RexCall) a).getOperands()) { final RexNode simplified = simplifyIsNotNull(operand); if (simplified == null) { operands.add(rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, operand)); } else if (simplified.isAlwaysFalse()) { return rexBuilder.makeLiteral(false); } else { operands.add(simplified); } } return RexUtil.composeConjunction(rexBuilder, operands, false); case CUSTOM: switch (a.getKind()) { case LITERAL: return rexBuilder.makeLiteral(((RexLiteral) a).getValue() != null); default: throw new AssertionError("every CUSTOM policy needs a handler, " + a.getKind()); } case AS_IS: default: return null; } } private RexNode simplifyCase(RexCall call) { final List<RexNode> operands = call.getOperands(); final List<RexNode> newOperands = new ArrayList<>(); final Set<String> values = new HashSet<>(); for (int i = 0; i < operands.size(); i++) { RexNode operand = operands.get(i); if (RexUtil.isCasePredicate(call, i)) { if (operand.isAlwaysTrue()) { // Predicate is always TRUE. Make value the ELSE and quit. newOperands.add(operands.get(++i)); if (unknownAsFalse && RexUtil.isNull(operands.get(i))) { values.add(rexBuilder.makeLiteral(false).toString()); } else { values.add(operands.get(i).toString()); } break; } else if (operand.isAlwaysFalse() || RexUtil.isNull(operand)) { // Predicate is always FALSE or NULL. Skip predicate and value. ++i; continue; } } else { if (unknownAsFalse && RexUtil.isNull(operand)) { values.add(rexBuilder.makeLiteral(false).toString()); } else { values.add(operand.toString()); } } newOperands.add(operand); } assert newOperands.size() % 2 == 1; if (newOperands.size() == 1 || values.size() == 1) { final RexNode last = Util.last(newOperands); if (!call.getType().equals(last.getType())) { return rexBuilder.makeAbstractCast(call.getType(), last); } return last; } trueFalse: if (call.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) { // Optimize CASE where every branch returns constant true or constant // false. final List<Pair<RexNode, RexNode>> pairs = casePairs(rexBuilder, newOperands); // 1) Possible simplification if unknown is treated as false: // CASE // WHEN p1 THEN TRUE // WHEN p2 THEN TRUE // ELSE FALSE // END // can be rewritten to: (p1 or p2) if (unknownAsFalse) { final List<RexNode> terms = new ArrayList<>(); int pos = 0; for (; pos < pairs.size(); pos++) { // True block Pair<RexNode, RexNode> pair = pairs.get(pos); if (!pair.getValue().isAlwaysTrue()) { break; } terms.add(pair.getKey()); } for (; pos < pairs.size(); pos++) { // False block Pair<RexNode, RexNode> pair = pairs.get(pos); if (!pair.getValue().isAlwaysFalse() && !RexUtil.isNull(pair.getValue())) { break; } } if (pos == pairs.size()) { final RexNode disjunction = RexUtil.composeDisjunction(rexBuilder, terms); if (!call.getType().equals(disjunction.getType())) { return rexBuilder.makeCast(call.getType(), disjunction); } return disjunction; } } // 2) Another simplification // CASE // WHEN p1 THEN TRUE // WHEN p2 THEN FALSE // WHEN p3 THEN TRUE // ELSE FALSE // END // if p1...pn cannot be nullable for (Ord<Pair<RexNode, RexNode>> pair : Ord.zip(pairs)) { if (pair.e.getKey().getType().isNullable()) { break trueFalse; } if (!pair.e.getValue().isAlwaysTrue() && !pair.e.getValue().isAlwaysFalse() && (!unknownAsFalse || !RexUtil.isNull(pair.e.getValue()))) { break trueFalse; } } final List<RexNode> terms = new ArrayList<>(); final List<RexNode> notTerms = new ArrayList<>(); for (Ord<Pair<RexNode, RexNode>> pair : Ord.zip(pairs)) { if (pair.e.getValue().isAlwaysTrue()) { terms.add(RexUtil.andNot(rexBuilder, pair.e.getKey(), notTerms)); } else { notTerms.add(pair.e.getKey()); } } final RexNode disjunction = RexUtil.composeDisjunction(rexBuilder, terms); if (!call.getType().equals(disjunction.getType())) { return rexBuilder.makeCast(call.getType(), disjunction); } return disjunction; } if (newOperands.equals(operands)) { return call; } return call.clone(call.getType(), newOperands); } /** Given "CASE WHEN p1 THEN v1 ... ELSE e END" * returns [(p1, v1), ..., (true, e)]. */ private static List<Pair<RexNode, RexNode>> casePairs(RexBuilder rexBuilder, List<RexNode> operands) { final ImmutableList.Builder<Pair<RexNode, RexNode>> builder = ImmutableList.builder(); for (int i = 0; i < operands.size() - 1; i += 2) { builder.add(Pair.of(operands.get(i), operands.get(i + 1))); } builder.add(Pair.of((RexNode) rexBuilder.makeLiteral(true), Util.last(operands))); return builder.build(); } public RexNode simplifyAnd(RexCall e) { final List<RexNode> terms = new ArrayList<>(); final List<RexNode> notTerms = new ArrayList<>(); RelOptUtil.decomposeConjunction(e, terms, notTerms); simplifyList(terms); simplifyList(notTerms); if (unknownAsFalse) { return simplifyAnd2ForUnknownAsFalse(terms, notTerms); } return simplifyAnd2(terms, notTerms); } RexNode simplifyAnd2(List<RexNode> terms, List<RexNode> notTerms) { for (RexNode term : terms) { if (term.isAlwaysFalse()) { return rexBuilder.makeLiteral(false); } } 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. // // 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 for (RexNode notDisjunction : notTerms) { final List<RexNode> terms2 = RelOptUtil.conjunctions(notDisjunction); if (terms.containsAll(terms2)) { return rexBuilder.makeLiteral(false); } } // Add the NOT disjunctions back in. for (RexNode notDisjunction : notTerms) { terms.add(simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction))); } return RexUtil.composeConjunction(rexBuilder, terms, false); } /** As {@link #simplifyAnd2(List, List)} but we assume that if the expression * returns UNKNOWN it will be interpreted as FALSE. */ RexNode simplifyAnd2ForUnknownAsFalse(List<RexNode> terms, List<RexNode> notTerms) { for (RexNode term : terms) { if (term.isAlwaysFalse()) { return rexBuilder.makeLiteral(false); } } 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)); } // 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, 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<>(); for (int i = 0; i < terms.size(); i++) { RexNode term = terms.get(i); if (!RexUtil.isDeterministic(term)) { continue; } // Simplify BOOLEAN expressions if possible while (term.getKind() == SqlKind.EQUALS) { RexCall call = (RexCall) term; if (call.getOperands().get(0).isAlwaysTrue()) { term = call.getOperands().get(1); terms.set(i, term); continue; } else if (call.getOperands().get(1).isAlwaysTrue()) { term = call.getOperands().get(0); terms.set(i, term); continue; } break; } switch (term.getKind()) { case EQUALS: case NOT_EQUALS: case LESS_THAN: case GREATER_THAN: case LESS_THAN_OR_EQUAL: case GREATER_THAN_OR_EQUAL: RexCall call = (RexCall) term; RexNode left = call.getOperands().get(0); comparedOperands.add(left.toString()); // if it is a cast, we include the inner reference if (left.getKind() == SqlKind.CAST) { RexCall leftCast = (RexCall) left; comparedOperands.add(leftCast.getOperands().get(0).toString()); } RexNode right = call.getOperands().get(1); comparedOperands.add(right.toString()); // if it is a cast, we include the inner reference if (right.getKind() == SqlKind.CAST) { RexCall rightCast = (RexCall) right; comparedOperands.add(rightCast.getOperands().get(0).toString()); } // Check for equality on different constants. If the same ref or CAST(ref) // is equal to different constants, this condition cannot be satisfied, // and hence it can be evaluated to FALSE final boolean leftRef = RexUtil.isReferenceOrAccess(left, true); final boolean rightRef = RexUtil.isReferenceOrAccess(right, true); final boolean leftConstant = left.isA(SqlKind.LITERAL); final boolean rightConstant = right.isA(SqlKind.LITERAL); if (term.getKind() == SqlKind.EQUALS) { if (leftRef && rightConstant) { final String literal = right.toString(); final String prevLiteral = equalityConstantTerms.put(left.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) { equalityTerms.put(left.toString(), Pair.of(right.toString(), term)); } } // Assume the expression a > 5 is part of a Filter condition. // Then we can derive the negated term: a <= 5. // But as the comparison is string based and thus operands order dependent, // we should also add the inverted negated term: 5 >= a. // Observe that for creating the inverted term we invert the list of operands. RexNode negatedTerm = RexUtil.negate(rexBuilder, call); if (negatedTerm != null) { negatedTerms.add(negatedTerm.toString()); RexNode invertNegatedTerm = RexUtil.invert(rexBuilder, (RexCall) negatedTerm); if (invertNegatedTerm != null) { negatedTerms.add(invertNegatedTerm.toString()); } } // Range SqlKind comparison = null; RexNode ref = null; RexLiteral constant = null; if (leftRef && rightConstant) { comparison = term.getKind(); ref = left; constant = (RexLiteral) right; } else if (leftConstant && rightRef) { comparison = term.getKind().reverse(); constant = (RexLiteral) left; ref = right; } if (comparison != null && comparison != SqlKind.NOT_EQUALS) { // NOT_EQUALS not supported final RexNode result = processRange(rexBuilder, terms, rangeTerms, term, ref, constant, comparison); if (result != null) { // Not satisfiable return result; } } break; case IN: comparedOperands.add(((RexCall) term).operands.get(0).toString()); break; case BETWEEN: comparedOperands.add(((RexCall) term).operands.get(1).toString()); break; case IS_NOT_NULL: notNullOperands.add(((RexCall) term).getOperands().get(0)); terms.remove(i); --i; break; case IS_NULL: nullOperands.add(((RexCall) term).getOperands().get(0).toString()); } } // If one column should be null and is in a comparison predicate, // it is not satisfiable. // Example. IS NULL(x) AND x < 5 - not satisfiable if (!Collections.disjoint(nullOperands, comparedOperands)) { return rexBuilder.makeLiteral(false); } // Check for equality of two refs wrt equality with constants // Example #1. x=5 AND y=5 AND x=y : x=5 AND y=5 // Example #2. x=5 AND y=6 AND x=y - not satisfiable for (String ref1 : equalityTerms.keySet()) { final String literal1 = equalityConstantTerms.get(ref1); if (literal1 == null) { continue; } Collection<Pair<String, RexNode>> references = equalityTerms.get(ref1); for (Pair<String, RexNode> ref2 : references) { final String literal2 = equalityConstantTerms.get(ref2.left); if (literal2 == null) { continue; } if (!literal1.equals(literal2)) { // If an expression is equal to two different constants, // it is not satisfiable return rexBuilder.makeLiteral(false); } // Otherwise we can remove the term, as we already know that // the expression is equal to two constants terms.remove(ref2.right); } } // Remove not necessary IS NOT NULL expressions. // // Example. IS NOT NULL(x) AND x < 5 : x < 5 for (RexNode operand : notNullOperands) { if (!comparedOperands.contains(operand.toString())) { terms.add(rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, operand)); } } // If one of the not-disjunctions is a disjunction that is wholly // contained in the disjunctions list, the expression is not // satisfiable. // // 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)); for (RexNode notDisjunction : notTerms) { if (!RexUtil.isDeterministic(notDisjunction)) { continue; } final List<String> terms2Set = RexUtil.strings(RelOptUtil.conjunctions(notDisjunction)); if (termsSet.containsAll(terms2Set)) { return rexBuilder.makeLiteral(false); } } // Add the NOT disjunctions back in. for (RexNode notDisjunction : notTerms) { final RexNode call = rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction); terms.add(simplify(call)); } // The negated terms: only deterministic expressions for (String negatedTerm : negatedTerms) { if (termsSet.contains(negatedTerm)) { return rexBuilder.makeLiteral(false); } } return RexUtil.composeConjunction(rexBuilder, terms, false); } /** Simplifies OR(x, x) into x, and similar. */ public RexNode simplifyOr(RexCall call) { assert call.getKind() == SqlKind.OR; final List<RexNode> terms = RelOptUtil.disjunctions(call); return simplifyOrs(terms); } /** Simplifies a list of terms and combines them into an OR. * Modifies the list in place. */ public RexNode simplifyOrs(List<RexNode> terms) { for (int i = 0; i < terms.size(); i++) { final RexNode term = simplify(terms.get(i)); switch (term.getKind()) { case LITERAL: if (!RexLiteral.isNullLiteral(term)) { if (RexLiteral.booleanValue(term)) { return term; // true } else { terms.remove(i); --i; continue; } } } terms.set(i, term); } return RexUtil.composeDisjunction(rexBuilder, terms); } private RexNode simplifyCast(RexCall e) { final RexNode operand = e.getOperands().get(0); switch (operand.getKind()) { case LITERAL: final RexLiteral literal = (RexLiteral) operand; final Comparable value = literal.getValue(); final SqlTypeName typeName = literal.getTypeName(); // First, try to remove the cast without changing the value. // makeCast and canRemoveCastFromLiteral have the same logic, so we are // sure to be able to remove the cast. if (rexBuilder.canRemoveCastFromLiteral(e.getType(), value, typeName)) { return rexBuilder.makeCast(e.getType(), operand); } // Next, try to convert the value to a different type, // e.g. CAST('123' as integer) switch (literal.getTypeName()) { case TIME: switch (e.getType().getSqlTypeName()) { case TIMESTAMP: return e; } } final List<RexNode> reducedValues = new ArrayList<>(); executor.reduce(rexBuilder, ImmutableList.<RexNode>of(e), reducedValues); return Preconditions.checkNotNull(Iterables.getOnlyElement(reducedValues)); default: 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.getValue(); Pair<Range, List<RexNode>> p = rangeTerms.get(ref.toString()); if (p == null) { Range r; switch (comparison) { case EQUALS: r = Range.singleton(v0); break; case LESS_THAN: r = Range.lessThan(v0); break; case LESS_THAN_OR_EQUAL: r = Range.atMost(v0); break; case GREATER_THAN: r = Range.greaterThan(v0); break; case GREATER_THAN_OR_EQUAL: r = Range.atLeast(v0); break; default: throw new AssertionError(); } rangeTerms.put(ref.toString(), new Pair(r, ImmutableList.of(term))); } else { // Exists boolean removeUpperBound = false; boolean removeLowerBound = false; Range r = p.left; switch (comparison) { case EQUALS: if (!r.contains(v0)) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } rangeTerms.put(ref.toString(), new Pair(Range.singleton(v0), ImmutableList.of(term))); // remove terms.removeAll(p.right); break; case LESS_THAN: { int comparisonResult = 0; if (r.hasUpperBound()) { comparisonResult = v0.compareTo(r.upperEndpoint()); } if (comparisonResult <= 0) { // 1) No upper bound, or // 2) We need to open the upper bound, or // 3) New upper bound is lower than old upper bound if (r.hasLowerBound()) { if (v0.compareTo(r.lowerEndpoint()) < 0) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // a <= x < b OR a < x < b r = Range.range(r.lowerEndpoint(), r.lowerBoundType(), v0, BoundType.OPEN); } else { // x < b r = Range.lessThan(v0); } if (r.isEmpty()) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // remove prev upper bound removeUpperBound = true; } else { // Remove this term as it is contained in current upper bound terms.remove(term); } break; } case LESS_THAN_OR_EQUAL: { int comparisonResult = -1; if (r.hasUpperBound()) { comparisonResult = v0.compareTo(r.upperEndpoint()); } if (comparisonResult < 0) { // 1) No upper bound, or // 2) New upper bound is lower than old upper bound if (r.hasLowerBound()) { if (v0.compareTo(r.lowerEndpoint()) < 0) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // a <= x <= b OR a < x <= b r = Range.range(r.lowerEndpoint(), r.lowerBoundType(), v0, BoundType.CLOSED); } else { // x <= b r = Range.atMost(v0); } if (r.isEmpty()) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // remove prev upper bound removeUpperBound = true; } else { // Remove this term as it is contained in current upper bound terms.remove(term); } break; } case GREATER_THAN: { int comparisonResult = 0; if (r.hasLowerBound()) { comparisonResult = v0.compareTo(r.lowerEndpoint()); } if (comparisonResult >= 0) { // 1) No lower bound, or // 2) We need to open the lower bound, or // 3) New lower bound is greater than old lower bound if (r.hasUpperBound()) { if (v0.compareTo(r.upperEndpoint()) > 0) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // a < x <= b OR a < x < b r = Range.range(v0, BoundType.OPEN, r.upperEndpoint(), r.upperBoundType()); } else { // x > a r = Range.greaterThan(v0); } if (r.isEmpty()) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // remove prev lower bound removeLowerBound = true; } else { // Remove this term as it is contained in current lower bound terms.remove(term); } break; } case GREATER_THAN_OR_EQUAL: { int comparisonResult = 1; if (r.hasLowerBound()) { comparisonResult = v0.compareTo(r.lowerEndpoint()); } if (comparisonResult > 0) { // 1) No lower bound, or // 2) New lower bound is greater than old lower bound if (r.hasUpperBound()) { if (v0.compareTo(r.upperEndpoint()) > 0) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // a <= x <= b OR a <= x < b r = Range.range(v0, BoundType.CLOSED, r.upperEndpoint(), r.upperBoundType()); } else { // x >= a r = Range.atLeast(v0); } if (r.isEmpty()) { // Range is empty, not satisfiable return rexBuilder.makeLiteral(false); } // remove prev lower bound removeLowerBound = true; } else { // Remove this term as it is contained in current lower bound terms.remove(term); } break; } default: throw new AssertionError(); } if (removeUpperBound) { ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder(); for (RexNode e : p.right) { if (e.isA(SqlKind.LESS_THAN) || e.isA(SqlKind.LESS_THAN_OR_EQUAL)) { terms.remove(e); } else { newBounds.add(e); } } newBounds.add(term); rangeTerms.put(ref.toString(), new Pair(r, newBounds.build())); } else if (removeLowerBound) { ImmutableList.Builder<RexNode> newBounds = ImmutableList.builder(); for (RexNode e : p.right) { if (e.isA(SqlKind.GREATER_THAN) || e.isA(SqlKind.GREATER_THAN_OR_EQUAL)) { terms.remove(e); } else { newBounds.add(e); } } newBounds.add(term); rangeTerms.put(ref.toString(), new Pair(r, newBounds.build())); } } // Default return null; } } // End RexSimplify.java