com.hazelcast.org.apache.calcite.rex.RexSimplify Maven / Gradle / Ivy
/*
* 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 com.hazelcast.org.apache.calcite.rex;
import com.hazelcast.org.apache.calcite.avatica.util.TimeUnit;
import com.hazelcast.org.apache.calcite.avatica.util.TimeUnitRange;
import com.hazelcast.org.apache.calcite.plan.RelOptPredicateList;
import com.hazelcast.org.apache.calcite.plan.RelOptUtil;
import com.hazelcast.org.apache.calcite.plan.Strong;
import com.hazelcast.org.apache.calcite.rel.core.Project;
import com.hazelcast.org.apache.calcite.rel.metadata.NullSentinel;
import com.hazelcast.org.apache.calcite.rel.type.RelDataType;
import com.hazelcast.org.apache.calcite.sql.SqlKind;
import com.hazelcast.org.apache.calcite.sql.SqlOperator;
import com.hazelcast.org.apache.calcite.sql.fun.SqlStdOperatorTable;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeCoercionRule;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeFamily;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeName;
import com.hazelcast.org.apache.calcite.sql.type.SqlTypeUtil;
import com.hazelcast.org.apache.calcite.util.Bug;
import com.hazelcast.org.apache.calcite.util.Pair;
import com.hazelcast.org.apache.calcite.util.RangeSets;
import com.hazelcast.org.apache.calcite.util.Sarg;
import com.hazelcast.org.apache.calcite.util.Util;
import com.hazelcast.com.google.common.collect.ArrayListMultimap;
import com.hazelcast.com.google.common.collect.BoundType;
import com.hazelcast.com.google.common.collect.ImmutableList;
import com.hazelcast.com.google.common.collect.ImmutableRangeSet;
import com.hazelcast.com.google.common.collect.Iterables;
import com.hazelcast.com.google.common.collect.Multimap;
import com.hazelcast.com.google.common.collect.Range;
import com.hazelcast.com.google.common.collect.RangeSet;
import com.hazelcast.com.google.common.collect.Sets;
import com.hazelcast.com.google.common.collect.TreeRangeSet;
import com.hazelcast.org.checkerframework.checker.nullness.qual.Nullable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.hazelcast.org.apache.calcite.linq4j.Nullness.castNonNull;
import static com.hazelcast.org.apache.calcite.rex.RexUnknownAs.FALSE;
import static com.hazelcast.org.apache.calcite.rex.RexUnknownAs.TRUE;
import static com.hazelcast.org.apache.calcite.rex.RexUnknownAs.UNKNOWN;
import static java.util.Objects.requireNonNull;
/**
* Context required to simplify a row-expression.
*/
public class RexSimplify {
private final boolean paranoid;
public final RexBuilder rexBuilder;
private final RelOptPredicateList predicates;
/** How to treat UNKNOWN values, if one of the deprecated {@code
* simplify} methods without an {@code unknownAs} argument is called. */
final RexUnknownAs defaultUnknownAs;
final boolean predicateElimination;
private final RexExecutor executor;
private static final Strong STRONG = new Strong();
/**
* Creates a RexSimplify.
*
* @param rexBuilder Rex builder
* @param predicates Predicates known to hold on input fields
* @param executor Executor for constant reduction, not null
*/
public RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates,
RexExecutor executor) {
this(rexBuilder, predicates, UNKNOWN, true, false, executor);
}
/** Internal constructor. */
private RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates,
RexUnknownAs defaultUnknownAs, boolean predicateElimination,
boolean paranoid, RexExecutor executor) {
this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder");
this.predicates = requireNonNull(predicates, "predicates");
this.defaultUnknownAs = requireNonNull(defaultUnknownAs, "defaultUnknownAs");
this.predicateElimination = predicateElimination;
this.paranoid = paranoid;
this.executor = requireNonNull(executor, "executor");
}
@Deprecated // to be removed before 2.0
public RexSimplify(RexBuilder rexBuilder, boolean unknownAsFalse,
RexExecutor executor) {
this(rexBuilder, RelOptPredicateList.EMPTY,
RexUnknownAs.falseIf(unknownAsFalse), true, false, executor);
}
@Deprecated // to be removed before 2.0
public RexSimplify(RexBuilder rexBuilder, RelOptPredicateList predicates,
boolean unknownAsFalse, RexExecutor executor) {
this(rexBuilder, predicates, RexUnknownAs.falseIf(unknownAsFalse), true,
false, executor);
}
//~ Methods ----------------------------------------------------------------
/** Returns a RexSimplify the same as this but with a specified
* {@link #defaultUnknownAs} value.
*
* @deprecated Use methods with a {@link RexUnknownAs} argument, such as
* {@link #simplify(RexNode, RexUnknownAs)}. */
@Deprecated // to be removed before 2.0
public RexSimplify withUnknownAsFalse(boolean unknownAsFalse) {
final RexUnknownAs defaultUnknownAs = RexUnknownAs.falseIf(unknownAsFalse);
return defaultUnknownAs == this.defaultUnknownAs
? this
: new RexSimplify(rexBuilder, predicates, defaultUnknownAs,
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, defaultUnknownAs,
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, defaultUnknownAs,
predicateElimination, paranoid, executor);
}
/** Returns a RexSimplify the same as this but with a specified
* {@link #predicateElimination} value.
*
* This is introduced temporarily, until
* {@link Bug#CALCITE_2401_FIXED [CALCITE-2401] is fixed}.
*/
private RexSimplify withPredicateElimination(boolean predicateElimination) {
return predicateElimination == this.predicateElimination
? this
: new RexSimplify(rexBuilder, predicates, defaultUnknownAs,
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) {
return simplifyPreservingType(e, defaultUnknownAs, true);
}
public RexNode simplifyPreservingType(RexNode e, RexUnknownAs unknownAs,
boolean matchNullability) {
final RexNode e2 = simplifyUnknownAs(e, unknownAs);
if (e2.getType() == e.getType()) {
return e2;
}
if (!matchNullability
&& SqlTypeUtil.equalSansNullability(rexBuilder.typeFactory, e2.getType(), e.getType())) {
return e2;
}
final RexNode e3 = rexBuilder.makeCast(e.getType(), e2, matchNullability);
if (e3.equals(e)) {
return e;
}
return e3;
}
/**
* Simplifies a boolean expression.
*
*
In particular:
*
* - {@code simplify(x = 1 OR NOT x = 1 OR x IS NULL)}
* returns {@code TRUE}
* - {@code simplify(x = 1 AND FALSE)}
* returns {@code FALSE}
*
*
* Handles UNKNOWN values using the policy specified when you created this
* {@code RexSimplify}. Unless you used a deprecated constructor, that policy
* is {@link RexUnknownAs#UNKNOWN}.
*
*
If the expression is a predicate in a WHERE clause, consider instead
* using {@link #simplifyUnknownAsFalse(RexNode)}.
*
* @param e Expression to simplify
*/
public RexNode simplify(RexNode e) {
return simplifyUnknownAs(e, defaultUnknownAs);
}
/** As {@link #simplify(RexNode)}, but for a boolean expression
* for which a result of UNKNOWN will be treated as FALSE.
*
*
Use this form for expressions on a WHERE, ON, HAVING or FILTER(WHERE)
* clause.
*
*
This may allow certain additional simplifications. A result of UNKNOWN
* may yield FALSE, however it may still yield UNKNOWN. (If the simplified
* expression has type BOOLEAN NOT NULL, then of course it can only return
* FALSE.) */
public final RexNode simplifyUnknownAsFalse(RexNode e) {
return simplifyUnknownAs(e, FALSE);
}
/** As {@link #simplify(RexNode)}, but specifying how UNKNOWN values are to be
* treated.
*
*
If UNKNOWN is treated as FALSE, this may allow certain additional
* simplifications. A result of UNKNOWN may yield FALSE, however it may still
* yield UNKNOWN. (If the simplified expression has type BOOLEAN NOT NULL,
* then of course it can only return FALSE.) */
public RexNode simplifyUnknownAs(RexNode e, RexUnknownAs unknownAs) {
final RexNode simplified = withParanoid(false).simplify(e, unknownAs);
if (paranoid) {
verify(e, simplified, unknownAs);
}
return simplified;
}
/** Internal method to simplify an expression.
*
*
Unlike the public {@link #simplify(RexNode)}
* and {@link #simplifyUnknownAsFalse(RexNode)} methods,
* never calls {@link #verify(RexNode, RexNode, RexUnknownAs)}.
* Verify adds an overhead that is only acceptable for a top-level call.
*/
RexNode simplify(RexNode e, RexUnknownAs unknownAs) {
if (STRONG.isNull(e)) {
// Only boolean NULL (aka UNKNOWN) can be converted to FALSE. Even in
// unknownAs=FALSE mode, we must not convert a NULL integer (say) to FALSE
if (e.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
switch (unknownAs) {
case FALSE:
case TRUE:
return rexBuilder.makeLiteral(unknownAs.toBoolean());
default:
break;
}
}
return rexBuilder.makeNullLiteral(e.getType());
}
switch (e.getKind()) {
case AND:
return simplifyAnd((RexCall) e, unknownAs);
case OR:
return simplifyOr((RexCall) e, unknownAs);
case NOT:
return simplifyNot((RexCall) e, unknownAs);
case CASE:
return simplifyCase((RexCall) e, unknownAs);
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, unknownAs);
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, unknownAs);
case SEARCH:
return simplifySearch((RexCall) e, unknownAs);
case LIKE:
return simplifyLike((RexCall) e, unknownAs);
case MINUS_PREFIX:
return simplifyUnaryMinus((RexCall) e, unknownAs);
case PLUS_PREFIX:
return simplifyUnaryPlus((RexCall) e, unknownAs);
case PLUS:
case MINUS:
case TIMES:
case DIVIDE:
return simplifyArithmetic((RexCall) e);
default:
if (e.getClass() == RexCall.class) {
return simplifyGenericNode((RexCall) e);
} else {
return e;
}
}
}
/** Applies NOT to an expression. */
RexNode not(RexNode e) {
return RexUtil.not(rexBuilder, e);
}
/** Applies IS NOT FALSE to an expression. */
RexNode isNotFalse(RexNode e) {
return e.isAlwaysTrue()
? rexBuilder.makeLiteral(true)
: e.isAlwaysFalse()
? rexBuilder.makeLiteral(false)
: e.getKind() == SqlKind.NOT
? isNotTrue(((RexCall) e).operands.get(0))
: predicates.isEffectivelyNotNull(e)
? e // would "CAST(e AS BOOLEAN NOT NULL)" better?
: rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_FALSE, e);
}
/** Applies IS NOT TRUE to an expression. */
RexNode isNotTrue(RexNode e) {
return e.isAlwaysTrue()
? rexBuilder.makeLiteral(false)
: e.isAlwaysFalse()
? rexBuilder.makeLiteral(true)
: e.getKind() == SqlKind.NOT
? isNotFalse(((RexCall) e).operands.get(0))
: predicates.isEffectivelyNotNull(e)
? not(e)
: rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE, e);
}
/** Applies IS TRUE to an expression. */
RexNode isTrue(RexNode e) {
return e.isAlwaysTrue()
? rexBuilder.makeLiteral(true)
: e.isAlwaysFalse()
? rexBuilder.makeLiteral(false)
: e.getKind() == SqlKind.NOT
? isFalse(((RexCall) e).operands.get(0))
: predicates.isEffectivelyNotNull(e)
? e // would "CAST(e AS BOOLEAN NOT NULL)" better?
: rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, e);
}
/** Applies IS FALSE to an expression. */
RexNode isFalse(RexNode e) {
return e.isAlwaysTrue()
? rexBuilder.makeLiteral(false)
: e.isAlwaysFalse()
? rexBuilder.makeLiteral(true)
: e.getKind() == SqlKind.NOT
? isTrue(((RexCall) e).operands.get(0))
: predicates.isEffectivelyNotNull(e)
? not(e)
: rexBuilder.makeCall(SqlStdOperatorTable.IS_FALSE, e);
}
/**
* Runs simplification inside a non-specialized node.
*/
private RexNode simplifyGenericNode(RexCall e) {
final List operands = new ArrayList<>(e.operands);
simplifyList(operands, UNKNOWN);
if (e.operands.equals(operands)) {
return e;
}
return rexBuilder.makeCall(e.getType(), e.getOperator(), operands);
}
/**
* Try to find a literal with the given value in the input list.
* The type of the literal must be one of the numeric types.
*/
private static int findLiteralIndex(List operands, BigDecimal value) {
for (int i = 0; i < operands.size(); i++) {
if (operands.get(i).isA(SqlKind.LITERAL)) {
Comparable comparable = ((RexLiteral) operands.get(i)).getValue();
if (comparable instanceof BigDecimal
&& value.compareTo((BigDecimal) comparable) == 0) {
return i;
}
}
}
return -1;
}
private RexNode simplifyArithmetic(RexCall e) {
if (e.getType().getSqlTypeName().getFamily() != SqlTypeFamily.NUMERIC
|| e.getOperands().stream().anyMatch(
o -> e.getType().getSqlTypeName().getFamily() != SqlTypeFamily.NUMERIC)) {
// we only support simplifying numeric types.
return simplifyGenericNode(e);
}
assert e.getOperands().size() == 2;
switch (e.getKind()) {
case PLUS:
return simplifyPlus(e);
case MINUS:
return simplifyMinus(e);
case TIMES:
return simplifyMultiply(e);
case DIVIDE:
return simplifyDivide(e);
default:
throw new IllegalArgumentException("Unsupported arithmeitc operation " + e.getKind());
}
}
private RexNode simplifyPlus(RexCall e) {
final int zeroIndex = findLiteralIndex(e.operands, BigDecimal.ZERO);
if (zeroIndex >= 0) {
// return the other operand.
RexNode other = e.getOperands().get((zeroIndex + 1) % 2);
return other.getType().equals(e.getType())
? other : rexBuilder.makeCast(e.getType(), other);
}
return simplifyGenericNode(e);
}
private RexNode simplifyMinus(RexCall e) {
final int zeroIndex = findLiteralIndex(e.operands, BigDecimal.ZERO);
if (zeroIndex == 1) {
RexNode leftOperand = e.getOperands().get(0);
return leftOperand.getType().equals(e.getType())
? leftOperand : rexBuilder.makeCast(e.getType(), leftOperand);
}
return simplifyGenericNode(e);
}
private RexNode simplifyMultiply(RexCall e) {
final int oneIndex = findLiteralIndex(e.operands, BigDecimal.ONE);
if (oneIndex >= 0) {
// return the other operand.
RexNode other = e.getOperands().get((oneIndex + 1) % 2);
return other.getType().equals(e.getType())
? other : rexBuilder.makeCast(e.getType(), other);
}
return simplifyGenericNode(e);
}
private RexNode simplifyDivide(RexCall e) {
final int oneIndex = findLiteralIndex(e.operands, BigDecimal.ONE);
if (oneIndex == 1) {
RexNode leftOperand = e.getOperands().get(0);
return leftOperand.getType().equals(e.getType())
? leftOperand : rexBuilder.makeCast(e.getType(), leftOperand);
}
return simplifyGenericNode(e);
}
private RexNode simplifyLike(RexCall e, RexUnknownAs unknownAs) {
if (e.operands.get(1) instanceof RexLiteral) {
final RexLiteral literal = (RexLiteral) e.operands.get(1);
if ("%".equals(literal.getValueAs(String.class))) {
// "x LIKE '%'" simplifies to "x = x"
final RexNode x = e.operands.get(0);
return simplify(rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, x, x),
unknownAs);
}
}
return simplifyGenericNode(e);
}
// e must be a comparison (=, >, >=, <, <=, !=)
private RexNode simplifyComparison(RexCall e, RexUnknownAs unknownAs) {
//noinspection unchecked
return simplifyComparison(e, unknownAs, Comparable.class);
}
// e must be a comparison (=, >, >=, <, <=, !=)
private > RexNode simplifyComparison(RexCall e,
RexUnknownAs unknownAs, Class clazz) {
final List operands = new ArrayList<>(e.operands);
// UNKNOWN mode is warranted: false = null
simplifyList(operands, UNKNOWN);
// Simplify "x x"
final RexNode o0 = operands.get(0);
final RexNode o1 = operands.get(1);
if (o0.equals(o1) && RexUtil.isDeterministic(o0)) {
RexNode newExpr;
switch (e.getKind()) {
case EQUALS:
case GREATER_THAN_OR_EQUAL:
case LESS_THAN_OR_EQUAL:
// "x = x" simplifies to "null or x is not null" (similarly <= and >=)
newExpr = rexBuilder.makeCall(SqlStdOperatorTable.OR,
rexBuilder.makeNullLiteral(e.getType()),
rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, o0));
return simplify(newExpr, unknownAs);
case NOT_EQUALS:
case LESS_THAN:
case GREATER_THAN:
// "x != x" simplifies to "null and x is null" (similarly < and >)
newExpr = rexBuilder.makeCall(SqlStdOperatorTable.AND,
rexBuilder.makeNullLiteral(e.getType()),
rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, o0));
return simplify(newExpr, unknownAs);
default:
// unknown kind
}
}
if (o0.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
Comparison cmp = Comparison.of(rexBuilder.makeCall(e.getOperator(), o0, o1), node -> true);
if (cmp != null) {
if (cmp.literal.isAlwaysTrue()) {
switch (cmp.kind) {
case GREATER_THAN_OR_EQUAL:
case EQUALS: // x=true
return cmp.ref;
case LESS_THAN:
case NOT_EQUALS: // x!=true
return simplify(not(cmp.ref), unknownAs);
case GREATER_THAN:
/* this is false, but could be null if x is null */
if (!cmp.ref.getType().isNullable()) {
return rexBuilder.makeLiteral(false);
}
break;
case LESS_THAN_OR_EQUAL:
/* this is true, but could be null if x is null */
if (!cmp.ref.getType().isNullable()) {
return rexBuilder.makeLiteral(true);
}
break;
default:
break;
}
}
if (cmp.literal.isAlwaysFalse()) {
switch (cmp.kind) {
case EQUALS:
case LESS_THAN_OR_EQUAL:
return simplify(not(cmp.ref), unknownAs);
case NOT_EQUALS:
case GREATER_THAN:
return cmp.ref;
case GREATER_THAN_OR_EQUAL:
/* this is true, but could be null if x is null */
if (!cmp.ref.getType().isNullable()) {
return rexBuilder.makeLiteral(true);
}
break;
case LESS_THAN:
/* this is false, but could be null if x is null */
if (!cmp.ref.getType().isNullable()) {
return rexBuilder.makeLiteral(false);
}
break;
default:
break;
}
}
}
}
// 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)
&& SqlTypeUtil.equalSansNullability(rexBuilder.getTypeFactory(),
o0.getType(), o1.getType())) {
final C v0 = ((RexLiteral) o0).getValueAs(clazz);
final C v1 = ((RexLiteral) o1).getValueAs(clazz);
if (v0 == null || v1 == null) {
return unknownAs == FALSE
? 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.
*/
@Deprecated // to be removed before 2.0
public RexNode simplifyAnds(Iterable extends RexNode> nodes) {
ensureParanoidOff();
return simplifyAnds(nodes, defaultUnknownAs);
}
// package-protected only for a deprecated method; treat as private
RexNode simplifyAnds(Iterable extends RexNode> nodes,
RexUnknownAs unknownAs) {
final List terms = new ArrayList<>();
final List notTerms = new ArrayList<>();
for (RexNode e : nodes) {
RelOptUtil.decomposeConjunction(e, terms, notTerms);
}
simplifyList(terms, UNKNOWN);
simplifyList(notTerms, UNKNOWN);
if (unknownAs == FALSE) {
return simplifyAnd2ForUnknownAsFalse(terms, notTerms);
}
return simplifyAnd2(terms, notTerms);
}
private void simplifyList(List terms, RexUnknownAs unknownAs) {
for (int i = 0; i < terms.size(); i++) {
terms.set(i, simplify(terms.get(i), unknownAs));
}
}
private void simplifyAndTerms(List terms, RexUnknownAs unknownAs) {
RexSimplify simplify = this;
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, unknownAs));
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, unknownAs));
}
}
private void simplifyOrTerms(List terms, RexUnknownAs unknownAs) {
// 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;
// 'doneTerms' prevents us from visiting a term in both first and second
// loops. If we did this, the second visit would have a predicate saying
// that 'term' is false. Effectively, we sort terms: visiting
// 'allowedAsPredicate' terms in the first loop, and
// non-'allowedAsPredicate' in the second. Each term is visited once.
final BitSet doneTerms = new BitSet();
for (int i = 0; i < terms.size(); i++) {
final RexNode t = terms.get(i);
if (!simplify.allowedAsPredicateDuringOrSimplification(t)) {
continue;
}
doneTerms.set(i);
final RexNode t2 = simplify.simplify(t, unknownAs);
terms.set(i, t2);
final RexNode inverse =
simplify.simplify(isNotTrue(t2), RexUnknownAs.UNKNOWN);
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 (doneTerms.get(i)) {
continue; // we visited this term in the first loop
}
terms.set(i, simplify.simplify(t, unknownAs));
}
}
/**
* Decides whether the given node could be used as a predicate during the simplification
* of other OR operands.
*/
private boolean allowedAsPredicateDuringOrSimplification(final RexNode t) {
Predicate predicate = Predicate.of(t);
return predicate != null && predicate.allowedInOr(predicates);
}
private RexNode simplifyNot(RexCall call, RexUnknownAs unknownAs) {
final RexNode a = call.getOperands().get(0);
final List newOperands;
switch (a.getKind()) {
case NOT:
// NOT NOT x ==> x
return simplify(((RexCall) a).getOperands().get(0), unknownAs);
case SEARCH:
// NOT SEARCH(x, Sarg[(-inf, 10) OR NULL) ==> SEARCH(x, Sarg[[10, +inf)])
final RexCall call2 = (RexCall) a;
final RexNode ref = call2.operands.get(0);
final RexLiteral literal = (RexLiteral) call2.operands.get(1);
final Sarg sarg = literal.getValueAs(Sarg.class);
return simplifySearch(
call2.clone(call2.type,
ImmutableList.of(ref,
rexBuilder.makeLiteral(requireNonNull(sarg, "sarg").negate(), literal.getType(),
literal.getTypeName()))),
unknownAs.negate());
case LITERAL:
if (a.getType().getSqlTypeName() == SqlTypeName.BOOLEAN
&& !RexLiteral.isNullLiteral(a)) {
return rexBuilder.makeLiteral(!RexLiteral.booleanValue(a));
}
break;
case AND:
// NOT distributivity for AND
newOperands = new ArrayList<>();
for (RexNode operand : ((RexCall) a).getOperands()) {
newOperands.add(simplify(not(operand), unknownAs));
}
return simplify(
rexBuilder.makeCall(SqlStdOperatorTable.OR, newOperands), unknownAs);
case OR:
// NOT distributivity for OR
newOperands = new ArrayList<>();
for (RexNode operand : ((RexCall) a).getOperands()) {
newOperands.add(simplify(not(operand), unknownAs));
}
return simplify(
rexBuilder.makeCall(SqlStdOperatorTable.AND, newOperands), unknownAs);
case CASE:
newOperands = new ArrayList<>();
List operands = ((RexCall) a).getOperands();
for (int i = 0; i < operands.size(); i += 2) {
if (i + 1 == operands.size()) {
newOperands.add(not(operands.get(i)));
} else {
newOperands.add(operands.get(i));
newOperands.add(not(operands.get(i + 1)));
}
}
return simplify(
rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands), unknownAs);
case IN:
case NOT_IN:
// do not try to negate
break;
default:
final SqlKind negateKind = a.getKind().negate();
if (a.getKind() != negateKind) {
return simplify(
rexBuilder.makeCall(RexUtil.op(negateKind),
((RexCall) a).getOperands()), unknownAs);
}
final SqlKind negateKind2 = a.getKind().negateNullSafe();
if (a.getKind() != negateKind2) {
return simplify(
rexBuilder.makeCall(RexUtil.op(negateKind2),
((RexCall) a).getOperands()), unknownAs);
}
}
RexNode a2 = simplify(a, unknownAs.negate());
if (a == a2) {
return call;
}
return not(a2);
}
private RexNode simplifyUnaryMinus(RexCall call, RexUnknownAs unknownAs) {
final RexNode a = call.getOperands().get(0);
if (a.getKind() == SqlKind.MINUS_PREFIX) {
// -(-(x)) ==> x
return simplify(((RexCall) a).getOperands().get(0), unknownAs);
}
return simplifyGenericNode(call);
}
private RexNode simplifyUnaryPlus(RexCall call, RexUnknownAs unknownAs) {
return simplify(call.getOperands().get(0), unknownAs);
}
private RexNode simplifyIs(RexCall call, RexUnknownAs unknownAs) {
final SqlKind kind = call.getKind();
final RexNode a = call.getOperands().get(0);
final RexNode simplified = simplifyIs1(kind, a, unknownAs);
return simplified == null ? call : simplified;
}
private @Nullable RexNode simplifyIs1(SqlKind kind, RexNode a, RexUnknownAs unknownAs) {
// UnknownAs.FALSE corresponds to x IS TRUE evaluation
// UnknownAs.TRUE to x IS NOT FALSE
// Note that both UnknownAs.TRUE and UnknownAs.FALSE only changes the meaning of Unknown
// (1) if we are already in UnknownAs.FALSE mode; x IS TRUE can be simplified to x
// (2) similarly in UnknownAs.TRUE mode; x IS NOT FALSE can be simplified to x
// (3) x IS FALSE could be rewritten to (NOT x) IS TRUE and from there the 1. rule applies
// (4) x IS NOT TRUE can be rewritten to (NOT x) IS NOT FALSE and from there the 2. rule applies
if (kind == SqlKind.IS_TRUE && unknownAs == RexUnknownAs.FALSE) {
return simplify(a, unknownAs);
}
if (kind == SqlKind.IS_FALSE && unknownAs == RexUnknownAs.FALSE) {
return simplify(not(a), unknownAs);
}
if (kind == SqlKind.IS_NOT_FALSE && unknownAs == RexUnknownAs.TRUE) {
return simplify(a, unknownAs);
}
if (kind == SqlKind.IS_NOT_TRUE && unknownAs == RexUnknownAs.TRUE) {
return simplify(not(a), unknownAs);
}
final RexNode pred = simplifyIsPredicate(kind, a);
if (pred != null) {
return pred;
}
return simplifyIs2(kind, a, unknownAs);
}
private @Nullable RexNode simplifyIsPredicate(SqlKind kind, RexNode a) {
if (!(RexUtil.isReferenceOrAccess(a, true) || RexUtil.isDeterministic(a))) {
return null;
}
for (RexNode p : predicates.pulledUpPredicates) {
IsPredicate pred = IsPredicate.of(p);
if (pred == null || !a.equals(pred.ref)) {
continue;
}
if (kind == pred.kind) {
return rexBuilder.makeLiteral(true);
}
}
return null;
}
private @Nullable RexNode simplifyIs2(SqlKind kind, RexNode a, RexUnknownAs unknownAs) {
final RexNode simplified;
switch (kind) {
case IS_NULL:
// x IS NULL ==> FALSE (if x is not nullable)
validateStrongPolicy(a);
simplified = simplifyIsNull(a);
if (simplified != null) {
return simplified;
}
break;
case IS_NOT_NULL:
// x IS NOT NULL ==> TRUE (if x is not nullable)
validateStrongPolicy(a);
simplified = simplifyIsNotNull(a);
if (simplified != null) {
return simplified;
}
break;
case IS_TRUE:
// x IS TRUE ==> x (if x is not nullable)
if (predicates.isEffectivelyNotNull(a)) {
return simplify(a, unknownAs);
}
simplified = simplify(a, RexUnknownAs.FALSE);
if (simplified == a) {
return null;
}
return isTrue(simplified);
case IS_NOT_FALSE:
// x IS NOT FALSE ==> x (if x is not nullable)
if (predicates.isEffectivelyNotNull(a)) {
return simplify(a, unknownAs);
}
simplified = simplify(a, RexUnknownAs.TRUE);
if (simplified == a) {
return null;
}
return isNotFalse(simplified);
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 (predicates.isEffectivelyNotNull(a)) {
return simplify(not(a), unknownAs);
}
break;
default:
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.negateNullSafe());
final RexNode arg = ((RexCall) a).operands.get(0);
return simplify(rexBuilder.makeCall(notKind, arg), UNKNOWN);
default:
break;
}
final RexNode a2 = simplify(a, UNKNOWN);
if (a != a2) {
return rexBuilder.makeCall(RexUtil.op(kind), ImmutableList.of(a2));
}
return null; // cannot be simplified
}
private @Nullable RexNode simplifyIsNotNull(RexNode a) {
// Simplify the argument first,
// call ourselves recursively to see whether we can make more progress.
// For example, given
// "(CASE WHEN FALSE THEN 1 ELSE 2) IS NOT NULL" we first simplify the
// argument to "2", and only then we can simplify "2 IS NOT NULL" to "TRUE".
a = simplify(a, UNKNOWN);
if (!a.getType().isNullable() && isSafeExpression(a)) {
return rexBuilder.makeLiteral(true);
}
if (predicates.pulledUpPredicates.contains(a)) {
return rexBuilder.makeLiteral(true);
}
if (hasCustomNullabilityRules(a.getKind())) {
return null;
}
switch (Strong.policy(a)) {
case NOT_NULL:
return rexBuilder.makeLiteral(true);
case ANY:
// "f" is a strong operator, so "f(operand0, operand1) IS NOT NULL"
// simplifies to "operand0 IS NOT NULL AND operand1 IS NOT NULL"
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);
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 @Nullable RexNode simplifyIsNull(RexNode a) {
// Simplify the argument first,
// call ourselves recursively to see whether we can make more progress.
// For example, given
// "(CASE WHEN FALSE THEN 1 ELSE 2) IS NULL" we first simplify the
// argument to "2", and only then we can simplify "2 IS NULL" to "FALSE".
a = simplify(a, UNKNOWN);
if (!a.getType().isNullable() && isSafeExpression(a)) {
return rexBuilder.makeLiteral(false);
}
if (RexUtil.isNull(a)) {
return rexBuilder.makeLiteral(true);
}
if (hasCustomNullabilityRules(a.getKind())) {
return null;
}
switch (Strong.policy(a)) {
case NOT_NULL:
return rexBuilder.makeLiteral(false);
case ANY:
// "f" is a strong operator, so "f(operand0, operand1) IS NULL" simplifies
// to "operand0 IS NULL OR operand1 IS NULL"
final List operands = new ArrayList<>();
for (RexNode operand : ((RexCall) a).getOperands()) {
final RexNode simplified = simplifyIsNull(operand);
if (simplified == null) {
operands.add(
rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, operand));
} else {
operands.add(simplified);
}
}
return RexUtil.composeDisjunction(rexBuilder, operands, false);
case AS_IS:
default:
return null;
}
}
/**
* Validates strong policy for specified {@link RexNode}.
*
* @param rexNode Rex node to validate the strong policy
* @throws AssertionError If the validation fails
*/
private static void validateStrongPolicy(RexNode rexNode) {
if (hasCustomNullabilityRules(rexNode.getKind())) {
return;
}
switch (Strong.policy(rexNode)) {
case NOT_NULL:
assert !rexNode.getType().isNullable();
break;
case ANY:
List operands = ((RexCall) rexNode).getOperands();
if (rexNode.getType().isNullable()) {
assert operands.stream()
.map(RexNode::getType)
.anyMatch(RelDataType::isNullable);
} else {
assert operands.stream()
.map(RexNode::getType)
.noneMatch(RelDataType::isNullable);
}
break;
default:
break;
}
}
/**
* Returns {@code true} if specified {@link SqlKind} has custom nullability rules which
* depend not only on the nullability of input operands.
*
* For example, CAST may be used to change the nullability of its operand type,
* so it may be nullable, though the argument type was non-nullable.
*
* @param sqlKind Sql kind to check
* @return {@code true} if specified {@link SqlKind} has custom nullability rules
*/
private static boolean hasCustomNullabilityRules(SqlKind sqlKind) {
switch (sqlKind) {
case CAST:
case ITEM:
return true;
default:
return false;
}
}
private RexNode simplifyCoalesce(RexCall call) {
final Set operandSet = new HashSet<>();
final List operands = new ArrayList<>();
for (RexNode operand : call.getOperands()) {
operand = simplify(operand, UNKNOWN);
if (!RexUtil.isNull(operand)
&& operandSet.add(operand)) {
operands.add(operand);
}
if (!operand.getType().isNullable()) {
break;
}
}
switch (operands.size()) {
case 0:
return rexBuilder.makeNullLiteral(call.type);
case 1:
return operands.get(0);
default:
if (operands.equals(call.operands)) {
return call;
}
return call.clone(call.type, operands);
}
}
private RexNode simplifyCase(RexCall call, RexUnknownAs unknownAs) {
List inputBranches =
CaseBranch.fromCaseOperands(rexBuilder, new ArrayList<>(call.getOperands()));
// run simplification on all operands
RexSimplify condSimplifier = this.withPredicates(RelOptPredicateList.EMPTY);
RexSimplify valueSimplifier = this;
RelDataType caseType = call.getType();
boolean conditionNeedsSimplify = false;
CaseBranch lastBranch = null;
List branches = new ArrayList<>();
for (CaseBranch inputBranch : inputBranches) {
// simplify the condition
RexNode newCond = condSimplifier.simplify(inputBranch.cond, RexUnknownAs.FALSE);
if (newCond.isAlwaysFalse()) {
// If the condition is false, we do not need to add it
continue;
}
// simplify the value
RexNode newValue = valueSimplifier.simplify(inputBranch.value, unknownAs);
// create new branch
if (lastBranch != null) {
if (lastBranch.value.equals(newValue)
&& isSafeExpression(newCond)) {
// in this case, last branch and new branch have the same conclusion,
// hence we create a new composite condition and we do not add it to
// the final branches for the time being
newCond = rexBuilder.makeCall(SqlStdOperatorTable.OR, lastBranch.cond, newCond);
conditionNeedsSimplify = true;
} else {
// if we reach here, the new branch is not mergeable with the last one,
// hence we are going to add the last branch to the final branches.
// if the last branch was merged, then we will simplify it first.
// otherwise, we just add it
CaseBranch branch = generateBranch(conditionNeedsSimplify, condSimplifier, lastBranch);
if (!branch.cond.isAlwaysFalse()) {
// If the condition is not false, we add it to the final result
branches.add(branch);
if (branch.cond.isAlwaysTrue()) {
// If the condition is always true, we are done
lastBranch = null;
break;
}
}
conditionNeedsSimplify = false;
}
}
lastBranch = new CaseBranch(newCond, newValue);
if (newCond.isAlwaysTrue()) {
// If the condition is always true, we are done (useful in first loop iteration)
break;
}
}
if (lastBranch != null) {
// we need to add the last pending branch once we have finished
// with the for loop
CaseBranch branch = generateBranch(conditionNeedsSimplify, condSimplifier, lastBranch);
if (!branch.cond.isAlwaysFalse()) {
branches.add(branch);
}
}
if (branches.size() == 1) {
// we can just return the value in this case (matching the case type)
final RexNode value = branches.get(0).value;
if (sameTypeOrNarrowsNullability(caseType, value.getType())) {
return value;
} else {
return rexBuilder.makeAbstractCast(caseType, value);
}
}
if (call.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
final RexNode result = simplifyBooleanCase(rexBuilder, branches, unknownAs, caseType);
if (result != null) {
if (sameTypeOrNarrowsNullability(caseType, result.getType())) {
return simplify(result, unknownAs);
} else {
// If the simplification would widen the nullability
RexNode simplified = simplify(result, UNKNOWN);
if (!simplified.getType().isNullable()) {
return simplified;
} else {
return rexBuilder.makeCast(call.getType(), simplified);
}
}
}
}
List newOperands = CaseBranch.toCaseOperands(branches);
if (newOperands.equals(call.getOperands())) {
return call;
}
return rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands);
}
/**
* If boolean is true, simplify cond in input branch and return new branch.
* Otherwise, simply return input branch.
*/
private static CaseBranch generateBranch(boolean simplifyCond, RexSimplify simplifier,
CaseBranch branch) {
if (simplifyCond) {
// the previous branch was merged, time to simplify it and
// add it to the final result
return new CaseBranch(
simplifier.simplify(branch.cond, RexUnknownAs.FALSE), branch.value);
}
return branch;
}
/**
* Return if the new type is the same and at most narrows the nullability.
*/
private boolean sameTypeOrNarrowsNullability(RelDataType oldType, RelDataType newType) {
return oldType.equals(newType)
|| (SqlTypeUtil.equalSansNullability(rexBuilder.typeFactory, oldType, newType)
&& oldType.isNullable());
}
/** Object to describe a CASE branch. */
static final class CaseBranch {
private final RexNode cond;
private final RexNode value;
CaseBranch(RexNode cond, RexNode value) {
this.cond = cond;
this.value = value;
}
@Override public String toString() {
return cond + " => " + value;
}
/** Given "CASE WHEN p1 THEN v1 ... ELSE e END"
* returns [(p1, v1), ..., (true, e)]. */
private static List fromCaseOperands(RexBuilder rexBuilder,
List operands) {
List ret = new ArrayList<>();
for (int i = 0; i < operands.size() - 1; i += 2) {
ret.add(new CaseBranch(operands.get(i), operands.get(i + 1)));
}
ret.add(new CaseBranch(rexBuilder.makeLiteral(true), Util.last(operands)));
return ret;
}
private static List toCaseOperands(List branches) {
List ret = new ArrayList<>();
for (int i = 0; i < branches.size() - 1; i++) {
CaseBranch branch = branches.get(i);
ret.add(branch.cond);
ret.add(branch.value);
}
CaseBranch lastBranch = Util.last(branches);
assert lastBranch.cond.isAlwaysTrue();
ret.add(lastBranch.value);
return ret;
}
}
/**
* Decides whether it is safe to flatten the given CASE part into ANDs/ORs.
*/
enum SafeRexVisitor implements RexVisitor {
INSTANCE;
@SuppressWarnings("ImmutableEnumChecker")
private final Set safeOps;
SafeRexVisitor() {
Set safeOps = EnumSet.noneOf(SqlKind.class);
safeOps.addAll(SqlKind.COMPARISON);
safeOps.add(SqlKind.PLUS_PREFIX);
safeOps.add(SqlKind.MINUS_PREFIX);
safeOps.add(SqlKind.PLUS);
safeOps.add(SqlKind.MINUS);
safeOps.add(SqlKind.TIMES);
safeOps.add(SqlKind.IS_FALSE);
safeOps.add(SqlKind.IS_NOT_FALSE);
safeOps.add(SqlKind.IS_TRUE);
safeOps.add(SqlKind.IS_NOT_TRUE);
safeOps.add(SqlKind.IS_NULL);
safeOps.add(SqlKind.IS_NOT_NULL);
safeOps.add(SqlKind.IS_DISTINCT_FROM);
safeOps.add(SqlKind.IS_NOT_DISTINCT_FROM);
safeOps.add(SqlKind.IN);
safeOps.add(SqlKind.SEARCH);
safeOps.add(SqlKind.OR);
safeOps.add(SqlKind.AND);
safeOps.add(SqlKind.NOT);
safeOps.add(SqlKind.CASE);
safeOps.add(SqlKind.LIKE);
safeOps.add(SqlKind.COALESCE);
safeOps.add(SqlKind.TRIM);
safeOps.add(SqlKind.LTRIM);
safeOps.add(SqlKind.RTRIM);
safeOps.add(SqlKind.BETWEEN);
safeOps.add(SqlKind.CEIL);
safeOps.add(SqlKind.FLOOR);
safeOps.add(SqlKind.REVERSE);
safeOps.add(SqlKind.TIMESTAMP_ADD);
safeOps.add(SqlKind.TIMESTAMP_DIFF);
this.safeOps = Sets.immutableEnumSet(safeOps);
}
@Override public Boolean visitInputRef(RexInputRef inputRef) {
return true;
}
@Override public Boolean visitLocalRef(RexLocalRef localRef) {
return false;
}
@Override public Boolean visitLiteral(RexLiteral literal) {
return true;
}
@Override public Boolean visitCall(RexCall call) {
if (!safeOps.contains(call.getKind())) {
return false;
}
return RexVisitorImpl.visitArrayAnd(this, call.operands);
}
@Override public Boolean visitOver(RexOver over) {
return false;
}
@Override public Boolean visitCorrelVariable(RexCorrelVariable correlVariable) {
return false;
}
@Override public Boolean visitDynamicParam(RexDynamicParam dynamicParam) {
return false;
}
@Override public Boolean visitRangeRef(RexRangeRef rangeRef) {
return false;
}
@Override public Boolean visitFieldAccess(RexFieldAccess fieldAccess) {
return true;
}
@Override public Boolean visitSubQuery(RexSubQuery subQuery) {
return false;
}
@Override public Boolean visitTableInputRef(RexTableInputRef fieldRef) {
return false;
}
@Override public Boolean visitPatternFieldRef(RexPatternFieldRef fieldRef) {
return false;
}
}
/** Analyzes a given {@link RexNode} and decides whenever it is safe to
* unwind.
*
* "Safe" means that it only contains a combination of known good operators.
*
*
Division is an unsafe operator; consider the following:
*
case when a > 0 then 1 / a else null end
*/
static boolean isSafeExpression(RexNode r) {
return r.accept(SafeRexVisitor.INSTANCE);
}
private @Nullable RexNode simplifyBooleanCase(RexBuilder rexBuilder,
List inputBranches, @SuppressWarnings("unused") RexUnknownAs unknownAs,
RelDataType branchType) {
RexNode result;
// prepare all condition/branches for boolean interpretation
// It's done here make these interpretation changes available to case2or simplifications
// but not interfere with the normal simplification recursion
List branches = new ArrayList<>();
for (CaseBranch branch : inputBranches) {
if ((branches.size() > 0 && !isSafeExpression(branch.cond))
|| !isSafeExpression(branch.value)) {
return null;
}
final RexNode cond = isTrue(branch.cond);
final RexNode value;
if (!branchType.equals(branch.value.getType())) {
value = rexBuilder.makeAbstractCast(branchType, branch.value);
} else {
value = branch.value;
}
branches.add(new CaseBranch(cond, value));
}
result = simplifyBooleanCaseGeneric(rexBuilder, branches);
return result;
}
/**
* Generic boolean case simplification.
*
* Rewrites:
*
* CASE
* WHEN p1 THEN x
* WHEN p2 THEN y
* ELSE z
* END
*
* to
* (p1 and x) or (p2 and y and not(p1)) or (true and z and not(p1) and not(p2))
*/
private static RexNode simplifyBooleanCaseGeneric(RexBuilder rexBuilder,
List branches) {
boolean booleanBranches = branches.stream()
.allMatch(branch -> branch.value.isAlwaysTrue() || branch.value.isAlwaysFalse());
final List terms = new ArrayList<>();
final List notTerms = new ArrayList<>();
for (CaseBranch branch : branches) {
boolean useBranch = !branch.value.isAlwaysFalse();
if (useBranch) {
final RexNode branchTerm;
if (branch.value.isAlwaysTrue()) {
branchTerm = branch.cond;
} else {
branchTerm = rexBuilder.makeCall(SqlStdOperatorTable.AND, branch.cond, branch.value);
}
terms.add(RexUtil.andNot(rexBuilder, branchTerm, notTerms));
}
if (booleanBranches && useBranch) {
// we are safe to ignore this branch because for boolean true branches:
// a || (b && !a) === a || b
} else {
notTerms.add(branch.cond);
}
}
return RexUtil.composeDisjunction(rexBuilder, terms);
}
@Deprecated // to be removed before 2.0
public RexNode simplifyAnd(RexCall e) {
ensureParanoidOff();
return simplifyAnd(e, defaultUnknownAs);
}
RexNode simplifyAnd(RexCall e, RexUnknownAs unknownAs) {
List operands = RelOptUtil.conjunctions(e);
if (unknownAs == FALSE && predicateElimination) {
simplifyAndTerms(operands, FALSE);
} else {
simplifyList(operands, unknownAs);
}
final List terms = new ArrayList<>();
final List notTerms = new ArrayList<>();
final SargCollector sargCollector = new SargCollector(rexBuilder, true);
operands.forEach(t -> sargCollector.accept(t, terms));
if (sargCollector.needToFix()) {
operands.clear();
terms.forEach(t ->
operands.add(SargCollector.fix(rexBuilder, t, unknownAs)));
}
terms.clear();
for (RexNode o : operands) {
RelOptUtil.decomposeConjunction(o, terms, notTerms);
}
switch (unknownAs) {
case FALSE:
return simplifyAnd2ForUnknownAsFalse(terms, notTerms, Comparable.class);
default:
break;
}
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
List notSatisfiableNullables = null;
for (RexNode notDisjunction : notTerms) {
final List terms2 = RelOptUtil.conjunctions(notDisjunction);
if (!terms.containsAll(terms2)) {
// may be satisfiable ==> check other terms
continue;
}
if (!notDisjunction.getType().isNullable()) {
// x is NOT nullable, then x AND NOT(x) ==> FALSE
return rexBuilder.makeLiteral(false);
}
// x AND NOT(x) is UNKNOWN for NULL input
// So we search for the shortest notDisjunction then convert
// original expression to NULL and x IS NULL
if (notSatisfiableNullables == null) {
notSatisfiableNullables = new ArrayList<>();
}
notSatisfiableNullables.add(notDisjunction);
}
if (notSatisfiableNullables != null) {
// Remove the intersection of "terms" and "notTerms"
terms.removeAll(notSatisfiableNullables);
notTerms.removeAll(notSatisfiableNullables);
// The intersection simplify to "null and x1 is null and x2 is null..."
terms.add(rexBuilder.makeNullLiteral(notSatisfiableNullables.get(0).getType()));
for (RexNode notSatisfiableNullable : notSatisfiableNullables) {
terms.add(
simplifyIs((RexCall)
rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, notSatisfiableNullable), UNKNOWN));
}
}
// Add the NOT disjunctions back in.
for (RexNode notDisjunction : notTerms) {
terms.add(simplify(not(notDisjunction), UNKNOWN));
}
return RexUtil.composeConjunction(rexBuilder, terms);
}
/** 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), FALSE);
}
// 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;
final RexNode left = call.getOperands().get(0);
comparedOperands.add(left);
// 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));
}
final RexNode right = call.getOperands().get(1);
comparedOperands.add(right);
// 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));
}
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 RexLiteral literal = comparison.literal;
final RexLiteral prevLiteral =
equalityConstantTerms.put(comparison.ref, literal);
if (prevLiteral != null && !literal.equals(prevLiteral)) {
return rexBuilder.makeLiteral(false);
}
} else if (RexUtil.isReferenceOrAccess(left, true)
&& RexUtil.isReferenceOrAccess(right, true)) {
equalityTerms.put(left, Pair.of(right, 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);
RexNode invertNegatedTerm = RexUtil.invert(rexBuilder, (RexCall) negatedTerm);
if (invertNegatedTerm != null) {
negatedTerms.add(invertNegatedTerm);
}
}
// 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);
if (constant == null) {
break;
}
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));
break;
case BETWEEN:
comparedOperands.add(((RexCall) term).operands.get(1));
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));
break;
default:
break;
}
}
// 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 (RexNode ref1 : equalityTerms.keySet()) {
final RexLiteral literal1 = equalityConstantTerms.get(ref1);
if (literal1 == null) {
continue;
}
Collection> references = equalityTerms.get(ref1);
for (Pair ref2 : references) {
final RexLiteral 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)) {
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<>(terms);
for (RexNode notDisjunction : notTerms) {
if (!RexUtil.isDeterministic(notDisjunction)) {
continue;
}
final List terms2Set = RelOptUtil.conjunctions(notDisjunction);
if (termsSet.containsAll(terms2Set)) {
return rexBuilder.makeLiteral(false);
}
}
// Add the NOT disjunctions back in.
for (RexNode notDisjunction : notTerms) {
terms.add(not(notDisjunction));
}
// The negated terms: only deterministic expressions
for (RexNode negatedTerm : negatedTerms) {
if (termsSet.contains(negatedTerm)) {
return rexBuilder.makeLiteral(false);
}
}
return RexUtil.composeConjunction(rexBuilder, terms);
}
@SuppressWarnings("BetaApi")
private > RexNode simplifyUsingPredicates(RexNode e,
Class clazz) {
if (predicates.pulledUpPredicates.isEmpty()) {
return e;
}
final Comparison comparison = Comparison.of(e);
// Check for comparison with null values
if (comparison == null
|| comparison.literal.getValue() == null) {
return e;
}
final C v0 = comparison.literal.getValueAs(clazz);
if (v0 == null) {
return e;
}
final RangeSet rangeSet = rangeSet(comparison.kind, v0);
final RangeSet rangeSet2 =
residue(comparison.ref, rangeSet, predicates.pulledUpPredicates,
clazz);
if (rangeSet2.isEmpty()) {
// Term is impossible to satisfy given these predicates
return rexBuilder.makeLiteral(false);
} else if (rangeSet2.equals(rangeSet)) {
// no change
return e;
} else if (rangeSet2.equals(RangeSets.rangeSetAll())) {
// Range is always satisfied given these predicates; but nullability might
// be problematic
return simplify(
rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, comparison.ref),
RexUnknownAs.UNKNOWN);
} else if (rangeSet2.asRanges().size() == 1
&& Iterables.getOnlyElement(rangeSet2.asRanges()).hasLowerBound()
&& Iterables.getOnlyElement(rangeSet2.asRanges()).hasUpperBound()
&& Iterables.getOnlyElement(rangeSet2.asRanges()).lowerEndpoint()
.equals(Iterables.getOnlyElement(rangeSet2.asRanges()).upperEndpoint())) {
final Range r = Iterables.getOnlyElement(rangeSet2.asRanges());
// range is now a point; it's worth simplifying
return rexBuilder.makeCall(SqlStdOperatorTable.EQUALS, comparison.ref,
rexBuilder.makeLiteral(r.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}
*
*/
@SuppressWarnings("BetaApi")
private static > RangeSet residue(RexNode ref,
RangeSet r0, List predicates, Class clazz) {
RangeSet result = r0;
for (RexNode predicate : predicates) {
switch (predicate.getKind()) {
case EQUALS:
case NOT_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);
assert c1 != null : "value must not be null in " + literal;
switch (predicate.getKind()) {
case NOT_EQUALS:
// We want to intersect result with the range set of everything but
// c1. We subtract the point c1 from result, which is equivalent.
final Range pointRange = range(SqlKind.EQUALS, c1);
final RangeSet notEqualsRangeSet =
ImmutableRangeSet.of(pointRange).complement();
if (result.enclosesAll(notEqualsRangeSet)) {
result = RangeSets.rangeSetAll();
continue;
}
result = RangeSets.minus(result, pointRange);
break;
default:
final Range r1 = range(predicate.getKind(), c1);
if (result.encloses(r1)) {
// Given these predicates, term is always satisfied.
// e.g. r0 is "$0 < 10", r1 is "$0 < 5"
result = RangeSets.rangeSetAll();
continue;
}
result = result.subRangeSet(r1);
}
if (result.isEmpty()) {
break; // short-cut
}
}
break;
default:
break;
}
}
return result;
}
/** Simplifies OR(x, x) into x, and similar.
* The simplified expression returns UNKNOWN values as is (not as FALSE). */
@Deprecated // to be removed before 2.0
public RexNode simplifyOr(RexCall call) {
ensureParanoidOff();
return simplifyOr(call, UNKNOWN);
}
private RexNode simplifyOr(RexCall call, RexUnknownAs unknownAs) {
assert call.getKind() == SqlKind.OR;
final List terms0 = RelOptUtil.disjunctions(call);
final List terms;
if (predicateElimination) {
terms = Util.moveToHead(terms0, e -> e.getKind() == SqlKind.IS_NULL);
simplifyOrTerms(terms, unknownAs);
} else {
terms = terms0;
simplifyList(terms, unknownAs);
}
return simplifyOrs(terms, unknownAs);
}
/** Simplifies a list of terms and combines them into an OR.
* Modifies the list in place.
* The simplified expression returns UNKNOWN values as is (not as FALSE). */
@Deprecated // to be removed before 2.0
public RexNode simplifyOrs(List terms) {
ensureParanoidOff();
return simplifyOrs(terms, UNKNOWN);
}
private void ensureParanoidOff() {
if (paranoid) {
throw new UnsupportedOperationException("Paranoid is not supported for this method");
}
}
/** Simplifies a list of terms and combines them into an OR.
* Modifies the list in place. */
private RexNode simplifyOrs(List terms, RexUnknownAs unknownAs) {
final SargCollector sargCollector = new SargCollector(rexBuilder, false);
final List newTerms = new ArrayList<>();
terms.forEach(t -> sargCollector.accept(t, newTerms));
if (sargCollector.needToFix()) {
terms.clear();
newTerms.forEach(t ->
terms.add(SargCollector.fix(rexBuilder, t, unknownAs)));
}
// CALCITE-3198 Auxiliary map to simplify cases like:
// X <> A OR X <> B => X IS NOT NULL or NULL
// The map key will be the 'X'; and the value the first call 'X<>A' that is found,
// or 'X IS NOT NULL' if a simplification takes place (because another 'X<>B' is found)
final Map notEqualsComparisonMap = new HashMap<>();
final RexLiteral trueLiteral = rexBuilder.makeLiteral(true);
for (int i = 0; i < terms.size(); i++) {
final RexNode term = terms.get(i);
switch (term.getKind()) {
case LITERAL:
if (RexLiteral.isNullLiteral(term)) {
if (unknownAs == FALSE) {
terms.remove(i);
--i;
continue;
} else if (unknownAs == TRUE) {
return trueLiteral;
}
} else {
if (RexLiteral.booleanValue(term)) {
return term; // true
} else {
terms.remove(i);
--i;
continue;
}
}
break;
case NOT_EQUALS:
final Comparison notEqualsComparison = Comparison.of(term);
if (notEqualsComparison != null) {
// We are dealing with a X<>A term, check if we saw before another NOT_EQUALS involving X
final RexNode prevNotEquals = notEqualsComparisonMap.get(notEqualsComparison.ref);
if (prevNotEquals == null) {
// This is the first NOT_EQUALS involving X, put it in the map
notEqualsComparisonMap.put(notEqualsComparison.ref, term);
} else {
// There is already in the map another NOT_EQUALS involving X:
// - if it is already an IS_NOT_NULL: it was already simplified, ignore this term
// - if it is not an IS_NOT_NULL (i.e. it is a NOT_EQUALS): check comparison values
if (prevNotEquals.getKind() != SqlKind.IS_NOT_NULL) {
final Comparable comparable1 = notEqualsComparison.literal.getValue();
final Comparable comparable2 =
castNonNull(Comparison.of(prevNotEquals)).literal.getValue();
//noinspection unchecked
if (comparable1 != null
&& comparable2 != null
&& comparable1.compareTo(comparable2) != 0) {
// X <> A OR X <> B => X IS NOT NULL OR NULL
final RexNode isNotNull =
rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, notEqualsComparison.ref);
final RexNode constantNull =
rexBuilder.makeNullLiteral(trueLiteral.getType());
final RexNode newCondition = simplify(
rexBuilder.makeCall(SqlStdOperatorTable.OR, isNotNull, constantNull),
unknownAs);
if (newCondition.isAlwaysTrue()) {
return trueLiteral;
}
notEqualsComparisonMap.put(notEqualsComparison.ref, isNotNull);
final int pos = terms.indexOf(prevNotEquals);
terms.set(pos, newCondition);
}
}
terms.remove(i);
--i;
continue;
}
}
break;
default:
break;
}
}
return RexUtil.composeDisjunction(rexBuilder, terms);
}
private void verify(RexNode before, RexNode simplified, RexUnknownAs unknownAs) {
if (simplified.isAlwaysFalse()
&& before.isAlwaysTrue()) {
throw new AssertionError("always true [" + before
+ "] simplified to always false [" + simplified + "]");
}
if (simplified.isAlwaysTrue()
&& before.isAlwaysFalse()) {
throw new AssertionError("always false [" + before
+ "] simplified to always true [" + 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;
}
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 (!Boolean.TRUE.equals(v)) {
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 (before.getType().getSqlTypeName() == SqlTypeName.BOOLEAN) {
switch (unknownAs) {
case FALSE:
case TRUE:
if (v0 == NullSentinel.INSTANCE) {
v0 = unknownAs.toBoolean();
}
if (v1 == NullSentinel.INSTANCE) {
v1 = unknownAs.toBoolean();
}
break;
default:
break;
}
}
if (!v0.equals(v1)) {
throw new AssertionError("result mismatch (unknown as "
+ unknownAs + "): when applied to " + map + ",\n"
+ before + " yielded " + v0 + ";\n"
+ simplified + " yielded " + v1);
}
}
}
private RexNode simplifySearch(RexCall call, RexUnknownAs unknownAs) {
assert call.getKind() == SqlKind.SEARCH;
final RexNode a = call.getOperands().get(0);
if (call.getOperands().get(1) instanceof RexLiteral) {
RexLiteral literal = (RexLiteral) call.getOperands().get(1);
final Sarg sarg = castNonNull(literal.getValueAs(Sarg.class));
if (sarg.isAll() || sarg.isNone()) {
return RexUtil.simpleSarg(rexBuilder, a, sarg, unknownAs);
}
// Remove null from sarg if the left-hand side is never null
if (sarg.nullAs != UNKNOWN) {
final RexNode simplified = simplifyIs1(SqlKind.IS_NULL, a, unknownAs);
if (simplified != null
&& simplified.isAlwaysFalse()) {
final Sarg sarg2 = Sarg.of(UNKNOWN, sarg.rangeSet);
final RexLiteral literal2 =
rexBuilder.makeLiteral(sarg2, literal.getType(),
literal.getTypeName());
// Now we've strengthened the Sarg, try to simplify again
return simplifySearch(
call.clone(call.type, ImmutableList.of(a, literal2)),
unknownAs);
}
} else if (sarg.isPoints() && sarg.pointCount <= 1) {
// Expand "SEARCH(x, Sarg([point])" to "x = point"
// and "SEARCH(x, Sarg([])" to "false"
return RexUtil.expandSearch(rexBuilder, null, call);
}
}
return call;
}
private RexNode simplifyCast(RexCall e) {
RexNode operand = e.getOperands().get(0);
operand = simplify(operand, UNKNOWN);
if (sameTypeOrNarrowsNullability(e.getType(), operand.getType())) {
return operand;
}
if (RexUtil.isLosslessCast(operand)) {
// x :: y below means cast(x as y) (which is PostgreSQL-specific cast by the way)
// A) Remove lossless casts:
// A.1) intExpr :: bigint :: int => intExpr
// A.2) char2Expr :: char(5) :: char(2) => char2Expr
// B) There are cases when we can't remove two casts, but we could probably remove inner one
// B.1) char2expression :: char(4) :: char(5) -> char2expression :: char(5)
// B.2) char2expression :: char(10) :: char(5) -> char2expression :: char(5)
// B.3) char2expression :: varchar(10) :: char(5) -> char2expression :: char(5)
// B.4) char6expression :: varchar(10) :: char(5) -> char6expression :: char(5)
// C) Simplification is not possible:
// C.1) char6expression :: char(3) :: char(5) -> must not be changed
// the input is truncated to 3 chars, so we can't use char6expression :: char(5)
// C.2) varchar2Expr :: char(5) :: varchar(2) -> must not be changed
// the input have to be padded with spaces (up to 2 chars)
// C.3) char2expression :: char(4) :: varchar(5) -> must not be changed
// would not have the padding
// The approach seems to be:
// 1) Ensure inner cast is lossless (see if above)
// 2) If operand of the inner cast has the same type as the outer cast,
// remove two casts except C.2 or C.3-like pattern (== inner cast is CHAR)
// 3) If outer cast is lossless, remove inner cast (B-like cases)
// Here we try to remove two casts in one go (A-like cases)
RexNode intExpr = ((RexCall) operand).operands.get(0);
// intExpr == CHAR detects A.1
// operand != CHAR detects C.2
if ((intExpr.getType().getSqlTypeName() == SqlTypeName.CHAR
|| operand.getType().getSqlTypeName() != SqlTypeName.CHAR)
&& sameTypeOrNarrowsNullability(e.getType(), intExpr.getType())) {
return intExpr;
}
// Here we try to remove inner cast (B-like cases)
if (RexUtil.isLosslessCast(intExpr.getType(), operand.getType())
&& (e.getType().getSqlTypeName() == operand.getType().getSqlTypeName()
|| e.getType().getSqlTypeName() == SqlTypeName.CHAR
|| operand.getType().getSqlTypeName() != SqlTypeName.CHAR)
&& SqlTypeCoercionRule.instance()
.canApplyFrom(intExpr.getType().getSqlTypeName(), e.getType().getSqlTypeName())) {
return rexBuilder.makeCast(e.getType(), intExpr);
}
}
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;
default:
break;
}
break;
default:
break;
}
final List reducedValues = new ArrayList<>();
final RexNode simplifiedExpr = rexBuilder.makeCast(e.getType(), operand);
executor.reduce(rexBuilder, ImmutableList.of(simplifiedExpr), reducedValues);
return requireNonNull(
Iterables.getOnlyElement(reducedValues));
default:
if (operand == e.getOperands().get(0)) {
return e;
} else {
return rexBuilder.makeCast(e.getType(), operand);
}
}
}
/** 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), UNKNOWN);
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;
}
return outer.ordinal() <= inner.ordinal();
default:
break;
}
break;
case QUARTER:
switch (inner) {
case QUARTER:
case MONTH:
case DAY:
case HOUR:
case MINUTE:
case SECOND:
case MILLISECOND:
case MICROSECOND:
return true;
default:
break;
}
break;
default:
break;
}
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 > @Nullable RexNode processRange(
RexBuilder rexBuilder, List terms,
Map, List>> rangeTerms, RexNode term,
RexNode ref, C v0, SqlKind comparison) {
Pair, List> p = rangeTerms.get(ref);
if (p == null) {
rangeTerms.put(ref,
Pair.of(range(comparison, v0),
ImmutableList.of(term)));
} else {
// Exists
boolean removeUpperBound = false;
boolean removeLowerBound = false;
Range r = p.left;
final RexLiteral trueLiteral = rexBuilder.makeLiteral(true);
switch (comparison) {
case EQUALS:
if (!r.contains(v0)) {
// Range is empty, not satisfiable
return rexBuilder.makeLiteral(false);
}
rangeTerms.put(ref,
Pair.of(Range.singleton(v0),
ImmutableList.of(term)));
// remove
for (RexNode e : p.right) {
replaceLast(terms, e, trueLiteral);
}
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
replaceLast(terms, term, trueLiteral);
}
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
replaceLast(terms, term, trueLiteral);
}
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
replaceLast(terms, term, trueLiteral);
}
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
replaceLast(terms, term, trueLiteral);
}
break;
}
default:
throw new AssertionError();
}
if (removeUpperBound) {
ImmutableList.Builder newBounds = ImmutableList.builder();
for (RexNode e : p.right) {
if (isUpperBound(e)) {
replaceLast(terms, e, trueLiteral);
} else {
newBounds.add(e);
}
}
newBounds.add(term);
rangeTerms.put(ref,
Pair.of(r, newBounds.build()));
} else if (removeLowerBound) {
ImmutableList.Builder newBounds = ImmutableList.builder();
for (RexNode e : p.right) {
if (isLowerBound(e)) {
replaceLast(terms, e, trueLiteral);
} else {
newBounds.add(e);
}
}
newBounds.add(term);
rangeTerms.put(ref,
Pair.of(r, 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();
}
}
@SuppressWarnings("BetaApi")
private static > RangeSet rangeSet(SqlKind comparison,
C c) {
switch (comparison) {
case EQUALS:
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
return ImmutableRangeSet.of(range(comparison, c));
case NOT_EQUALS:
return ImmutableRangeSet.builder()
.add(range(SqlKind.LESS_THAN, c))
.add(range(SqlKind.GREATER_THAN, c))
.build();
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 @Nullable Predicate of(RexNode t) {
final Predicate p = Comparison.of(t);
if (p != null) {
return p;
}
return IsPredicate.of(t);
}
/** Returns whether this predicate can be used while simplifying other OR
* operands. */
default boolean allowedInOr(RelOptPredicateList predicates) {
return true;
}
}
/** Represents a simple Comparison.
*
* Left hand side is a {@link RexNode}, right hand side is a literal.
*/
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 = requireNonNull(ref, "ref");
this.kind = requireNonNull(kind, "kind");
this.literal = requireNonNull(literal, "literal");
}
/** Creates a comparison, between a {@link RexInputRef} or {@link RexFieldAccess} or
* deterministic {@link RexCall} and a literal. */
static @Nullable Comparison of(RexNode e) {
return of(e, node -> RexUtil.isReferenceOrAccess(node, true)
|| RexUtil.isDeterministic(node));
}
/** Creates a comparison, or returns null. */
static @Nullable Comparison of(RexNode e, java.util.function.Predicate nodePredicate) {
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 (nodePredicate.test(left)) {
return new Comparison(left, e.getKind(), (RexLiteral) right);
}
break;
default:
break;
}
switch (left.getKind()) {
case LITERAL:
if (nodePredicate.test(right)) {
return new Comparison(right, e.getKind().reverse(), (RexLiteral) left);
}
break;
default:
break;
}
break;
default:
break;
}
return null;
}
@Override public boolean allowedInOr(RelOptPredicateList predicates) {
return !ref.getType().isNullable()
|| predicates.isEffectivelyNotNull(ref);
}
}
/** Represents an IS Predicate. */
private static class IsPredicate implements Predicate {
final RexNode ref;
final SqlKind kind;
private IsPredicate(RexNode ref, SqlKind kind) {
this.ref = requireNonNull(ref, "ref");
this.kind = requireNonNull(kind, "kind");
}
/** Creates an IS predicate, or returns null. */
static @Nullable 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) || RexUtil.isDeterministic(pA))) {
return null;
}
return new IsPredicate(pA, e.getKind());
default:
break;
}
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.
*
* The expression is simplified on the assumption that an UNKNOWN value
* is always treated as FALSE. Therefore the simplified expression may
* sometimes evaluate to FALSE where the original expression would evaluate to
* UNKNOWN.
*
* @param predicates Filter condition predicates
* @return simplified conjunction of predicates for the filter, null if always false
*/
public @Nullable RexNode simplifyFilterPredicates(Iterable extends RexNode> predicates) {
final RexNode simplifiedAnds =
withPredicateElimination(Bug.CALCITE_2401_FIXED)
.simplifyUnknownAsFalse(
RexUtil.composeConjunction(rexBuilder, 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);
}
/**
* Replaces the last occurrence of one specified value in a list with
* another.
*
*
Does not change the size of the list.
*
*
Returns whether the value was found.
*/
private static boolean replaceLast(List list, E oldVal, E newVal) {
@SuppressWarnings("argument.type.incompatible")
final int index = list.lastIndexOf(oldVal);
if (index < 0) {
return false;
}
list.set(index, newVal);
return true;
}
/** Gathers expressions that can be converted into
* {@link Sarg search arguments}. */
static class SargCollector {
final Map map = new HashMap<>();
private final RexBuilder rexBuilder;
private final boolean negate;
/**
* Count of the new terms after converting all the operands to
* {@code SEARCH} on a {@link Sarg}. It is used to decide whether
* the new terms are simpler.
*/
private int newTermsCount;
SargCollector(RexBuilder rexBuilder, boolean negate) {
this.rexBuilder = rexBuilder;
this.negate = negate;
}
private void accept(RexNode term, List newTerms) {
if (!accept_(term, newTerms)) {
newTerms.add(term);
}
newTermsCount = newTerms.size();
}
private boolean accept_(RexNode e, List newTerms) {
switch (e.getKind()) {
case LESS_THAN:
case LESS_THAN_OR_EQUAL:
case GREATER_THAN:
case GREATER_THAN_OR_EQUAL:
case EQUALS:
case NOT_EQUALS:
case SEARCH:
return accept2(((RexCall) e).operands.get(0),
((RexCall) e).operands.get(1), e.getKind(), newTerms);
case IS_NULL:
case IS_NOT_NULL:
final RexNode arg = ((RexCall) e).operands.get(0);
return accept1(arg, e.getKind(), newTerms);
default:
return false;
}
}
private boolean accept2(RexNode left, RexNode right, SqlKind kind,
List newTerms) {
switch (left.getKind()) {
case INPUT_REF:
case FIELD_ACCESS:
case CAST:
switch (right.getKind()) {
case LITERAL:
return accept2b(left, kind, (RexLiteral) right, newTerms);
default:
break;
}
return false;
case LITERAL:
switch (right.getKind()) {
case INPUT_REF:
case FIELD_ACCESS:
case CAST:
return accept2b(right, kind.reverse(), (RexLiteral) left, newTerms);
default:
break;
}
return false;
default:
break;
}
return false;
}
private static E addFluent(List super E> list, E e) {
list.add(e);
return e;
}
// always returns true
private boolean accept1(RexNode e, SqlKind kind, List newTerms) {
final RexSargBuilder b =
map.computeIfAbsent(e, e2 ->
addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate)));
switch (negate ? kind.negate() : kind) {
case IS_NULL:
b.nullAs = b.nullAs.or(TRUE);
return true;
case IS_NOT_NULL:
b.nullAs = b.nullAs.or(FALSE);
b.addAll();
return true;
default:
throw new AssertionError("unexpected " + kind);
}
}
private boolean accept2b(RexNode e, SqlKind kind,
RexLiteral literal, List newTerms) {
if (literal.getValue() == null) {
// Cannot include expressions 'x > NULL' in a Sarg. Comparing to a NULL
// literal is a daft thing to do, because it always returns UNKNOWN. It
// is better handled by other simplifications.
return false;
}
final RexSargBuilder b =
map.computeIfAbsent(e, e2 ->
addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate)));
if (negate) {
kind = kind.negateNullSafe();
}
final Comparable value =
requireNonNull(literal.getValueAs(Comparable.class), "value");
switch (kind) {
case LESS_THAN:
b.addRange(Range.lessThan(value), literal.getType());
return true;
case LESS_THAN_OR_EQUAL:
b.addRange(Range.atMost(value), literal.getType());
return true;
case GREATER_THAN:
b.addRange(Range.greaterThan(value), literal.getType());
return true;
case GREATER_THAN_OR_EQUAL:
b.addRange(Range.atLeast(value), literal.getType());
return true;
case EQUALS:
b.addRange(Range.singleton(value), literal.getType());
return true;
case NOT_EQUALS:
b.addRange(Range.lessThan(value), literal.getType());
b.addRange(Range.greaterThan(value), literal.getType());
return true;
case SEARCH:
final Sarg sarg = (Sarg) value;
b.addSarg(sarg, negate, literal.getType());
return true;
default:
throw new AssertionError("unexpected " + kind);
}
}
/** Returns whether it is worth to fix and convert to {@code SEARCH} calls. */
boolean needToFix() {
// Fix and converts to SEARCH if:
// 1. A Sarg has complexity greater than 1;
// 2. The terms are reduced as simpler Sarg points;
// 3. The terms are reduced as simpler Sarg comparison.
// Ignore 'negate' just to be compatible with previous versions of this
// method. "build().complexity()" would be a better estimate, if we could
// switch to it breaking lots of plans.
final Collection builders = map.values();
return builders.stream().anyMatch(b -> b.build(false).complexity() > 1)
|| newTermsCount == 1
&& builders.stream().allMatch(b -> simpleSarg(b.build()));
}
/**
* Returns whether this Sarg can be expanded to more simple form, e.g.
* the IN call or single comparison.
*/
private static boolean simpleSarg(Sarg sarg) {
return sarg.isPoints()
|| RangeSets.isOpenInterval(sarg.rangeSet)
|| sarg.isComplementedPoints();
}
/** If a term is a call to {@code SEARCH} on a {@link RexSargBuilder},
* converts it to a {@code SEARCH} on a {@link Sarg}. */
static RexNode fix(RexBuilder rexBuilder, RexNode term,
RexUnknownAs unknownAs) {
if (term instanceof RexSargBuilder) {
final RexSargBuilder sargBuilder = (RexSargBuilder) term;
final Sarg sarg = sargBuilder.build();
if (sarg.complexity() <= 1 && simpleSarg(sarg)) {
// Expand small sargs into comparisons in order to avoid plan changes
// and better readability.
return RexUtil.sargRef(rexBuilder, sargBuilder.ref, sarg,
term.getType(), unknownAs);
}
return rexBuilder.makeCall(SqlStdOperatorTable.SEARCH, sargBuilder.ref,
rexBuilder.makeSearchArgumentLiteral(sarg, term.getType()));
}
return term;
}
}
/** Equivalent to a {@link RexLiteral} whose value is a {@link Sarg},
* but mutable, so that the Sarg can be expanded as {@link SargCollector}
* traverses a list of OR or AND terms.
*
* The {@link SargCollector#fix} method converts it to an immutable
* literal.
*
*
The {@link #nullAs} field will become {@link Sarg#nullAs}, as follows:
*
*
* - If there is at least one term that returns TRUE when the argument
* is NULL, then the overall value will be TRUE; failing that,
*
- if there is at least one term that returns UNKNOWN when the argument
* is NULL, then the overall value will be UNKNOWN; failing that,
*
- the value will be FALSE.
*
*
* This is analogous to the behavior of OR in three-valued logic:
* {@code TRUE OR UNKNOWN OR FALSE} returns {@code TRUE};
* {@code UNKNOWN OR FALSE OR UNKNOWN} returns {@code UNKNOWN};
* {@code FALSE OR FALSE} returns {@code FALSE}. */
@SuppressWarnings("BetaApi")
private static class RexSargBuilder extends RexNode {
final RexNode ref;
final RexBuilder rexBuilder;
final boolean negate;
final List types = new ArrayList<>();
final RangeSet rangeSet = TreeRangeSet.create();
RexUnknownAs nullAs = FALSE;
RexSargBuilder(RexNode ref, RexBuilder rexBuilder, boolean negate) {
this.ref = requireNonNull(ref, "ref");
this.rexBuilder = requireNonNull(rexBuilder, "rexBuilder");
this.negate = negate;
}
@Override public String toString() {
return "SEARCH(" + ref + ", " + (negate ? "NOT " : "") + rangeSet
+ "; NULL AS " + nullAs + ")";
}
> Sarg build() {
return build(negate);
}
@SuppressWarnings({"rawtypes", "unchecked", "UnstableApiUsage"})
> Sarg build(boolean negate) {
final RangeSet r = (RangeSet) this.rangeSet;
if (negate) {
return Sarg.of(nullAs.negate(), r.complement());
} else {
return Sarg.of(nullAs, r);
}
}
@Override public RelDataType getType() {
if (this.types.isEmpty()) {
// Expression is "x IS NULL"
return ref.getType();
}
final List distinctTypes = Util.distinctList(this.types);
return requireNonNull(
rexBuilder.typeFactory.leastRestrictive(distinctTypes),
() -> "Can't find leastRestrictive type among " + distinctTypes);
}
@Override public R accept(RexVisitor visitor) {
throw new UnsupportedOperationException();
}
@Override public R accept(RexBiVisitor visitor, P arg) {
throw new UnsupportedOperationException();
}
@Override public boolean equals(@Nullable Object obj) {
throw new UnsupportedOperationException();
}
@Override public int hashCode() {
throw new UnsupportedOperationException();
}
void addAll() {
rangeSet.add(Range.all());
}
void addRange(Range range, RelDataType type) {
types.add(type);
rangeSet.add(range);
nullAs = nullAs.or(UNKNOWN);
}
@SuppressWarnings({"UnstableApiUsage", "rawtypes", "unchecked"})
void addSarg(Sarg sarg, boolean negate, RelDataType type) {
final RangeSet r;
final RexUnknownAs nullAs;
if (negate) {
r = sarg.rangeSet.complement();
nullAs = sarg.nullAs.negate();
} else {
r = sarg.rangeSet;
nullAs = sarg.nullAs;
}
types.add(type);
rangeSet.addAll(r);
switch (nullAs) {
case TRUE:
this.nullAs = this.nullAs.or(TRUE);
break;
case FALSE:
this.nullAs = this.nullAs.or(FALSE);
break;
case UNKNOWN:
this.nullAs = this.nullAs.or(UNKNOWN);
break;
}
}
}
}