org.apache.calcite.rex.RexSimplify Maven / Gradle / Ivy
Show all versions of calcite-core Show documentation
/*
* 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.avatica.util.TimeUnit;
import org.apache.calcite.avatica.util.TimeUnitRange;
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;
import org.apache.calcite.rel.metadata.NullSentinel;
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.Bug;
import org.apache.calcite.util.Pair;
import org.apache.calcite.util.Util;
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.Objects;
import java.util.Set;
import java.util.function.Function;
/**
* Context required to simplify a row-expression.
*/
public class RexSimplify {
private final boolean paranoid;
public final RexBuilder rexBuilder;
private final RelOptPredicateList predicates;
final boolean unknownAsFalse;
final boolean predicateElimination;
private final RexExecutor executor;
/**
* 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, RelOptPredicateList predicates,
boolean unknownAsFalse, RexExecutor executor) {
this(rexBuilder, predicates, unknownAsFalse, true, false, executor);
}
/** Internal constructor. */
private RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates,
boolean unknownAsFalse, boolean predicateElimination, boolean paranoid,
RexExecutor executor) {
this.rexBuilder = Objects.requireNonNull(rexBuilder);
this.predicates = Objects.requireNonNull(predicates);
this.unknownAsFalse = unknownAsFalse;
this.predicateElimination = predicateElimination;
this.paranoid = paranoid;
this.executor = Objects.requireNonNull(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
* {@link #unknownAsFalse} value. */
public RexSimplify withUnknownAsFalse(boolean unknownAsFalse) {
return unknownAsFalse == this.unknownAsFalse
? this
: new RexSimplify(rexBuilder, predicates, unknownAsFalse, predicateElimination, paranoid,
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, predicateElimination, paranoid,
executor);
}
/** Returns a RexSimplify the same as this but which verifies that
* the expression before and after simplification are equivalent.
*
* @see #verify
*/
public RexSimplify withParanoid(boolean paranoid) {
return paranoid == this.paranoid
? this
: new RexSimplify(rexBuilder, predicates, unknownAsFalse, predicateElimination, paranoid,
executor);
}
/** Returns a RexSimplify the same as this but with a specified {@link #predicateElimination}
* value.
* This is introduced temporarily; until CALCITE-2401 is fixed
*/
private RexSimplify withPredicateElimination(boolean predicateElimination) {
return predicateElimination == this.predicateElimination
? this
: new RexSimplify(rexBuilder, predicates, unknownAsFalse, predicateElimination, paranoid,
executor);
}
/** Simplifies a boolean expression, always preserving its type and its
* nullability.
*
* 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.
*
*
In particular:
*
* - {@code simplify(x = 1 AND y = 2 AND NOT x = 1)}
* returns {@code y = 2}
* - {@code simplify(x = 1 AND FALSE)}
* returns {@code FALSE}
*
*
* 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) {
return verify(e, simplifier -> simplifier.simplify_(e));
}
private 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 COALESCE:
return simplifyCoalesce((RexCall) e);
case CAST:
return simplifyCast((RexCall) e);
case CEIL:
case FLOOR:
return simplifyCeilFloor((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) {
//noinspection unchecked
return simplifyComparison(e, Comparable.class);
}
// e must be a comparison (=, >, >=, <, <=, !=)
private > RexNode simplifyComparison(RexCall e,
Class clazz) {
final List operands = new ArrayList<>(e.operands);
simplifyList(operands);
// Simplify "x 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 " "
// 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 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());
}
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.
final RexNode e2;
if (operands.equals(e.operands)) {
e2 = e;
} else {
e2 = rexBuilder.makeCall(e.op, operands);
}
return simplifyUsingPredicates(e2, clazz);
}
/**
* Simplifies a conjunction of boolean expressions.
*/
public RexNode simplifyAnds(Iterable extends RexNode> nodes) {
final List terms = new ArrayList<>();
final List 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 terms) {
for (int i = 0; i < terms.size(); i++) {
terms.set(i, withUnknownAsFalse(false).simplify_(terms.get(i)));
}
}
private void simplifyAndTerms(List terms) {
RexSimplify simplify = withUnknownAsFalse(false);
for (int i = 0; i < terms.size(); i++) {
RexNode t = terms.get(i);
if (Predicate.of(t) == null) {
continue;
}
terms.set(i, simplify.simplify(t));
RelOptPredicateList newPredicates = simplify.predicates.union(rexBuilder,
RelOptPredicateList.of(rexBuilder, terms.subList(i, i + 1)));
simplify = simplify.withPredicates(newPredicates);
}
for (int i = 0; i < terms.size(); i++) {
RexNode t = terms.get(i);
if (Predicate.of(t) != null) {
continue;
}
terms.set(i, simplify.simplify(t));
}
}
private void simplifyOrTerms(List terms) {
// Suppose we are processing "e1(x) OR e2(x) OR e3(x)". When we are
// visiting "e3(x)" we know both "e1(x)" and "e2(x)" are not true (they
// may be unknown), because if either of them were true we would have
// stopped.
RexSimplify simplify = this;
for (int i = 0; i < terms.size(); i++) {
final RexNode t = terms.get(i);
if (Predicate.of(t) == null) {
continue;
}
final RexNode t2 = simplify.simplify(t);
terms.set(i, t2);
final RexNode inverse =
simplify.simplify(rexBuilder.makeCall(SqlStdOperatorTable.NOT, t2));
final RelOptPredicateList newPredicates = simplify.predicates.union(rexBuilder,
RelOptPredicateList.of(rexBuilder, ImmutableList.of(inverse)));
simplify = simplify.withPredicates(newPredicates);
}
for (int i = 0; i < terms.size(); i++) {
final RexNode t = terms.get(i);
if (Predicate.of(t) != null) {
continue;
}
terms.set(i, simplify.simplify(t));
}
}
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 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 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 pred = simplifyIsPredicate(kind, a);
if (pred != null) {
return pred;
}
final RexNode simplified = simplifyIs2(kind, a);
if (simplified != null) {
return simplified;
}
return call;
}
private RexNode simplifyIsPredicate(SqlKind kind, RexNode a) {
if (!RexUtil.isReferenceOrAccess(a, true)) {
return null;
}
for (RexNode p : predicates.pulledUpPredicates) {
IsPredicate pred = IsPredicate.of(p);
if (pred == null || !a.toString().equals(pred.ref.toString())) {
continue;
}
if (kind == pred.kind) {
return rexBuilder.makeLiteral(true);
} else {
return rexBuilder.makeLiteral(false);
}
}
return null;
}
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 NUll !=> x IS NOT NULL
if (kind != SqlKind.IS_NULL) {
// (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.negateNullSafe());
final RexNode arg = ((RexCall) a).operands.get(0);
return simplify_(rexBuilder.makeCall(notKind, arg));
}
break;
}
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);
}
if (predicates.pulledUpPredicates.contains(a)) {
return rexBuilder.makeLiteral(true);
}
switch (Strong.policy(a.getKind())) {
case ANY:
final List 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).isNull());
default:
throw new AssertionError("every CUSTOM policy needs a handler, "
+ a.getKind());
}
case AS_IS:
default:
return null;
}
}
private RexNode simplifyCoalesce(RexCall call) {
final Set digests = new HashSet<>();
final List operands = new ArrayList<>();
for (RexNode operand : call.getOperands()) {
operand = simplify_(operand);
if (digests.add(operand.digest)) {
operands.add(operand);
}
if (!operand.getType().isNullable()) {
break;
}
}
switch (operands.size()) {
case 0:
return rexBuilder.makeNullLiteral(call.type);
case 1:
// ensure type consistency, especially nullability.
if (!call.getType().equals(operands.get(0).getType())) {
return rexBuilder.makeAbstractCast(call.getType(), operands.get(0));
}
return operands.get(0);
default:
if (operands.equals(call.operands)) {
return call;
}
return call.clone(call.type, operands);
}
}
private RexNode simplifyCase(RexCall call) {
final List operands = call.getOperands();
final List newOperands = new ArrayList<>();
final Set 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> 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 terms = new ArrayList<>();
int pos = 0;
for (; pos < pairs.size(); pos++) {
// True block
Pair pair = pairs.get(pos);
if (!pair.getValue().isAlwaysTrue()) {
break;
}
terms.add(pair.getKey());
}
for (; pos < pairs.size(); pos++) {
// False block
Pair 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 : 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 terms = new ArrayList<>();
final List notTerms = new ArrayList<>();
for (Ord> 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> casePairs(RexBuilder rexBuilder,
List operands) {
final ImmutableList.Builder> 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 only to support a deprecated method; treat as private
public RexNode simplifyAnd(RexCall e) {
final List terms = new ArrayList<>();
final List notTerms = new ArrayList<>();
RelOptUtil.decomposeConjunction(e, terms, notTerms);
if (unknownAsFalse && predicateElimination) {
simplifyAndTerms(terms);
} else {
simplifyList(terms);
}
simplifyList(notTerms);
if (unknownAsFalse) {
return simplifyAnd2ForUnknownAsFalse(terms, notTerms);
}
return simplifyAnd2(terms, notTerms);
}
// package-protected only to support a deprecated method; treat as private
RexNode simplifyAnd2(List terms, List notTerms) {
for (RexNode term : terms) {
if (term.isAlwaysFalse()) {
return rexBuilder.makeLiteral(false);
}
}
if (terms.isEmpty() && notTerms.isEmpty()) {
return rexBuilder.makeLiteral(true);
}
// 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 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 terms,
List notTerms) {
//noinspection unchecked
return simplifyAnd2ForUnknownAsFalse(terms, notTerms, Comparable.class);
}
private > RexNode simplifyAnd2ForUnknownAsFalse(
List terms, List notTerms, Class clazz) {
for (RexNode term : terms) {
if (term.isAlwaysFalse() || RexLiteral.isNullLiteral(term)) {
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> equalityTerms = ArrayListMultimap.create();
final Map, List>> rangeTerms =
new HashMap<>();
final Map equalityConstantTerms = new HashMap<>();
final Set negatedTerms = new HashSet<>();
final Set nullOperands = new HashSet<>();
final Set notNullOperands = new LinkedHashSet<>();
final Set 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)) {
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());
}
final Comparison comparison = Comparison.of(term);
// Check for comparison with null values
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 (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 (RexUtil.isReferenceOrAccess(left, true)
&& RexUtil.isReferenceOrAccess(right, true)) {
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());
}
}
// 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.kind != SqlKind.NOT_EQUALS) { // not supported yet
final C constant = comparison.literal.getValueAs(clazz);
final RexNode result = processRange(rexBuilder, terms, rangeTerms,
term, comparison.ref, constant, comparison.kind);
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> references = equalityTerms.get(ref1);
for (Pair 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 termsSet = new HashSet<>(RexUtil.strings(terms));
for (RexNode notDisjunction : notTerms) {
if (!RexUtil.isDeterministic(notDisjunction)) {
continue;
}
final List 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);
}
private > RexNode simplifyUsingPredicates(RexNode e,
Class 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 range = range(comparison.kind, v0);
final Range 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())) {
// Range is always satisfied given these predicates; but nullability might
// be problematic
return simplify(
rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, comparison.ref));
} else if (range2.lowerEndpoint().equals(range2.upperEndpoint())) {
if (range2.lowerBoundType() == BoundType.OPEN
|| range2.upperBoundType() == BoundType.OPEN) {
// range is a point, but does not include its endpoint, therefore is
// effectively empty
return rexBuilder.makeLiteral(false);
}
// 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.
*
* The term is broken into "ref comparison constant",
* for example "$0 < 5".
*
*
Examples:
*
*
* - {@code residue($0 < 10, [$0 < 5])} returns {@code true}
*
*
- {@code residue($0 < 10, [$0 < 20, $0 > 0])} returns {@code $0 < 10}
*
*/
private > Range residue(RexNode ref, Range r0,
List predicates, Class 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 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;
final List terms = RelOptUtil.disjunctions(call);
if (predicateElimination) {
simplifyOrTerms(terms);
}
return simplifyOrs(terms);
}
/** Simplifies a list of terms and combines them into an OR.
* Modifies the list in place. */
public RexNode simplifyOrs(List terms) {
if (paranoid) {
final RexNode before = RexUtil.composeDisjunction(rexBuilder, terms);
return verify(before, simplifier -> simplifier.simplifyOrs(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 (unknownAsFalse) {
terms.remove(i);
--i;
continue;
}
} else {
if (RexLiteral.booleanValue(term)) {
return term; // true
} else {
terms.remove(i);
--i;
continue;
}
}
}
terms.set(i, term);
}
return RexUtil.composeDisjunction(rexBuilder, terms);
}
private RexNode verify(RexNode before,
Function simplifier) {
final RexNode simplified = simplifier.apply(withParanoid(false));
if (!paranoid) {
return simplified;
}
final RexAnalyzer foo0 = new RexAnalyzer(before, predicates);
final RexAnalyzer foo1 = new RexAnalyzer(simplified, predicates);
if (foo0.unsupportedCount > 0 || foo1.unsupportedCount > 0) {
// Analyzer cannot handle this expression currently
return simplified;
}
if (!foo0.variables.containsAll(foo1.variables)) {
throw new AssertionError("variable mismatch: "
+ before + " has " + foo0.variables + ", "
+ simplified + " has " + foo1.variables);
}
assignment_loop:
for (Map map : foo0.assignments()) {
for (RexNode predicate : predicates.pulledUpPredicates) {
final Comparable v = RexInterpreter.evaluate(predicate, map);
if (!v.equals(true)) {
continue assignment_loop;
}
}
Comparable v0 = RexInterpreter.evaluate(foo0.e, map);
if (v0 == null) {
throw new AssertionError("interpreter returned null for " + foo0.e);
}
Comparable v1 = RexInterpreter.evaluate(foo1.e, map);
if (v1 == null) {
throw new AssertionError("interpreter returned null for " + foo1.e);
}
if (unknownAsFalse
&& before.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
if (v0 == NullSentinel.INSTANCE) {
v0 = false;
}
if (v1 == NullSentinel.INSTANCE) {
v1 = false;
}
}
if (!v0.equals(v1)) {
throw new AssertionError("result mismatch: when applied to " + map
+ ", " + before + " yielded " + v0
+ ", and " + simplified + " yielded " + v1);
}
}
return simplified;
}
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.getValueAs(Comparable.class);
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;
}
break;
}
final List reducedValues = new ArrayList<>();
executor.reduce(rexBuilder, ImmutableList.of(e), reducedValues);
return Objects.requireNonNull(
Iterables.getOnlyElement(reducedValues));
default:
return e;
}
}
/** Tries to simplify CEIL/FLOOR function on top of CEIL/FLOOR.
*
* Examples:
*
*
* - {@code floor(floor($0, flag(hour)), flag(day))} returns {@code floor($0, flag(day))}
*
*
- {@code ceil(ceil($0, flag(second)), flag(day))} returns {@code ceil($0, flag(day))}
*
*
- {@code floor(floor($0, flag(day)), flag(second))} does not change
*
*
*/
private RexNode simplifyCeilFloor(RexCall e) {
if (e.getOperands().size() != 2) {
// Bail out since we only simplify floor
return e;
}
final RexNode operand = simplify_(e.getOperands().get(0));
if (e.getKind() == operand.getKind()) {
assert e.getKind() == SqlKind.CEIL || e.getKind() == SqlKind.FLOOR;
// CEIL/FLOOR on top of CEIL/FLOOR
final RexCall child = (RexCall) operand;
if (child.getOperands().size() != 2) {
// Bail out since we only simplify ceil/floor
return e;
}
final RexLiteral parentFlag = (RexLiteral) e.operands.get(1);
final TimeUnitRange parentFlagValue = (TimeUnitRange) parentFlag.getValue();
final RexLiteral childFlag = (RexLiteral) child.operands.get(1);
final TimeUnitRange childFlagValue = (TimeUnitRange) childFlag.getValue();
if (parentFlagValue != null && childFlagValue != null) {
if (canRollUp(parentFlagValue.startUnit, childFlagValue.startUnit)) {
return e.clone(e.getType(),
ImmutableList.of(child.getOperands().get(0), parentFlag));
}
}
}
return e.clone(e.getType(),
ImmutableList.of(operand, e.getOperands().get(1)));
}
/** Method that returns whether we can rollup from inner time unit
* to outer time unit. */
private static boolean canRollUp(TimeUnit outer, TimeUnit inner) {
// Special handling for QUARTER as it is not in the expected
// order in TimeUnit
switch (outer) {
case YEAR:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
case MILLISECOND:
case MICROSECOND:
switch (inner) {
case YEAR:
case QUARTER:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
case MILLISECOND:
case MICROSECOND:
if (inner == TimeUnit.QUARTER) {
return outer == TimeUnit.YEAR || outer == TimeUnit.QUARTER;
}
return outer.ordinal() <= inner.ordinal();
}
break;
case QUARTER:
switch (inner) {
case QUARTER:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
case MILLISECOND:
case MICROSECOND:
return true;
}
}
return false;
}
/** Removes any casts that change nullability but not type.
*
* For example, {@code CAST(1 = 0 AS BOOLEAN)} becomes {@code 1 = 0}. */
public RexNode removeNullabilityCast(RexNode e) {
return RexUtil.removeNullabilityCast(rexBuilder.getTypeFactory(), e);
}
private static > RexNode processRange(
RexBuilder rexBuilder, List terms,
Map, List>> rangeTerms, RexNode term,
RexNode ref, C v0, SqlKind comparison) {
Pair, List> p = rangeTerms.get(ref.toString());
if (p == null) {
rangeTerms.put(ref.toString(),
Pair.of(range(comparison, v0),
(List) 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(),
Pair.of(Range.singleton(v0),
(List) ImmutableList.of(term)));
// remove
for (RexNode e : p.right) {
Collections.replaceAll(terms, e, rexBuilder.makeLiteral(true));
}
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
final int index = terms.indexOf(term);
if (index >= 0) {
terms.set(index, rexBuilder.makeLiteral(true));
}
}
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
final int index = terms.indexOf(term);
if (index >= 0) {
terms.set(index, rexBuilder.makeLiteral(true));
}
}
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
final int index = terms.indexOf(term);
if (index >= 0) {
terms.set(index, rexBuilder.makeLiteral(true));
}
}
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
final int index = terms.indexOf(term);
if (index >= 0) {
terms.set(index, rexBuilder.makeLiteral(true));
}
}
break;
}
default:
throw new AssertionError();
}
if (removeUpperBound) {
ImmutableList.Builder newBounds = ImmutableList.builder();
for (RexNode e : p.right) {
if (isUpperBound(e)) {
Collections.replaceAll(terms, e, rexBuilder.makeLiteral(true));
} else {
newBounds.add(e);
}
}
newBounds.add(term);
rangeTerms.put(ref.toString(),
Pair.of(r, (List) newBounds.build()));
} else if (removeLowerBound) {
ImmutableList.Builder newBounds = ImmutableList.builder();
for (RexNode e : p.right) {
if (isLowerBound(e)) {
Collections.replaceAll(terms, e, rexBuilder.makeLiteral(true));
} else {
newBounds.add(e);
}
}
newBounds.add(term);
rangeTerms.put(ref.toString(),
Pair.of(r, (List) newBounds.build()));
}
}
// Default
return null;
}
private static > Range 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();
}
}
/** Marker interface for predicates (expressions that evaluate to BOOLEAN). */
private interface Predicate {
/** Wraps an expression in a Predicate or returns null. */
static Predicate of(RexNode t) {
final Predicate p = Comparison.of(t);
if (p != null) {
return p;
}
return IsPredicate.of(t);
}
}
/** 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 implements Predicate {
final RexNode ref;
final SqlKind kind;
final RexLiteral literal;
private Comparison(RexNode ref, SqlKind kind, RexLiteral literal) {
this.ref = Objects.requireNonNull(ref);
this.kind = Objects.requireNonNull(kind);
this.literal = Objects.requireNonNull(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;
}
}
/** Represents an IS Predicate. */
private static class IsPredicate implements Predicate {
final RexNode ref;
final SqlKind kind;
private IsPredicate(RexNode ref, SqlKind kind) {
this.ref = Objects.requireNonNull(ref);
this.kind = Objects.requireNonNull(kind);
}
/** Creates an IS predicate, or returns null. */
static IsPredicate of(RexNode e) {
switch (e.getKind()) {
case IS_NULL:
case IS_NOT_NULL:
RexNode pA = ((RexCall) e).getOperands().get(0);
if (!RexUtil.isReferenceOrAccess(pA, true)) {
return null;
}
return new IsPredicate(pA, e.getKind());
}
return null;
}
}
private static boolean isUpperBound(final RexNode e) {
final List operands;
switch (e.getKind()) {
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
operands = ((RexCall) e).getOperands();
return RexUtil.isReferenceOrAccess(operands.get(0), true)
&& operands.get(1).isA(SqlKind.LITERAL);
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
operands = ((RexCall) e).getOperands();
return RexUtil.isReferenceOrAccess(operands.get(1), true)
&& operands.get(0).isA(SqlKind.LITERAL);
default:
return false;
}
}
private static boolean isLowerBound(final RexNode e) {
final List operands;
switch (e.getKind()) {
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
operands = ((RexCall) e).getOperands();
return RexUtil.isReferenceOrAccess(operands.get(1), true)
&& operands.get(0).isA(SqlKind.LITERAL);
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
operands = ((RexCall) e).getOperands();
return RexUtil.isReferenceOrAccess(operands.get(0), true)
&& operands.get(1).isA(SqlKind.LITERAL);
default:
return false;
}
}
/**
* Combines predicates AND, optimizes, and returns null if the result is
* always false.
*
* @param predicates Filter condition predicates
* @return simplified conjunction of predicates for the filter, null if always false
*/
public RexNode simplifyFilterPredicates(Iterable extends RexNode> predicates) {
final RexNode simplifiedAnds = withPredicateElimination(Bug.CALCITE_2401_FIXED)
.simplifyAnds(predicates);
if (simplifiedAnds.isAlwaysFalse()) {
return null;
}
// Remove cast of BOOLEAN NOT NULL to BOOLEAN or vice versa. Filter accepts
// nullable and not-nullable conditions, but a CAST might get in the way of
// other rewrites.
return removeNullabilityCast(simplifiedAnds);
}
}
// End RexSimplify.java