All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.org.apache.calcite.rex.RexSimplify Maven / Gradle / Ivy

There is a newer version: 5.4.0
Show newest version
/*
 * 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 com.hazelcast.com.liance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.com.hazelcast.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.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.Util;

import com.hazelcast.com.google.com.hazelcast.com.on.collect.ArrayListMultimap;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.BoundType;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.ImmutableList;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Iterables;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Multimap;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Range;
import com.hazelcast.com.google.com.hazelcast.com.on.collect.Sets;

import java.util.ArrayList;
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.Objects;
import java.util.Set;

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;

/**
 * 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 final Strong 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 = Objects.requireNonNull(rexBuilder);
    this.predicates = Objects.requireNonNull(predicates);
    this.defaultUnknownAs = Objects.requireNonNull(defaultUnknownAs);
    this.predicateElimination = predicateElimination;
    this.paranoid = paranoid;
    this.executor = Objects.requireNonNull(executor);
    this.strong = new Strong();
  }

  @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 AND y = 2 AND NOT x = 1)} * returns {@code y = 2}
  • *
  • {@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()); } } 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 MINUS_PREFIX: return simplifyUnaryMinus((RexCall) e, unknownAs); case PLUS_PREFIX: return simplifyUnaryPlus((RexCall) e, unknownAs); default: if (e.getClass() == RexCall.class) { return simplifyGenericNode((RexCall) e); } else { return 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); } // e must be a com.hazelcast.com.arison (=, >, >=, <, <=, !=) private RexNode simplifyComparison(RexCall e, RexUnknownAs unknownAs) { //noinspection unchecked return simplifyComparison(e, unknownAs, Comparable.class); } // e must be a com.hazelcast.com.arison (=, >, >=, <, <=, !=) 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(rexBuilder.makeCall(SqlStdOperatorTable.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; } } if (cmp.literal.isAlwaysFalse()) { switch (cmp.kind) { case EQUALS: case LESS_THAN_OR_EQUAL: return simplify(rexBuilder.makeCall(SqlStdOperatorTable.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; } } } } // 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 com.hazelcast.com.arisonResult = v0.com.hazelcast.com.areTo(v1); switch (e.getKind()) { case EQUALS: return rexBuilder.makeLiteral(com.hazelcast.com.arisonResult == 0); case GREATER_THAN: return rexBuilder.makeLiteral(com.hazelcast.com.arisonResult > 0); case GREATER_THAN_OR_EQUAL: return rexBuilder.makeLiteral(com.hazelcast.com.arisonResult >= 0); case LESS_THAN: return rexBuilder.makeLiteral(com.hazelcast.com.arisonResult < 0); case LESS_THAN_OR_EQUAL: return rexBuilder.makeLiteral(com.hazelcast.com.arisonResult <= 0); case NOT_EQUALS: return rexBuilder.makeLiteral(com.hazelcast.com.arisonResult != 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 nodes) { ensureParanoidOff(); return simplifyAnds(nodes, defaultUnknownAs); } // package-protected only for a deprecated method; treat as private RexNode simplifyAnds(Iterable 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; for (int i = 0; i < terms.size(); i++) { final RexNode t = terms.get(i); if (!allowedAsPredicateDuringOrSimplification(t)) { continue; } final RexNode t2 = simplify.simplify(t, unknownAs); terms.set(i, t2); final RexNode inverse = simplify.simplify(rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_TRUE, 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 (allowedAsPredicateDuringOrSimplification(t)) { continue; } 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); if (predicate == null) { return false; } SqlKind kind = t.getKind(); if (SqlKind.COMPARISON.contains(kind) && t.getType().isNullable()) { return false; } return true; } private RexNode simplifyNot(RexCall call, RexUnknownAs unknownAs) { final RexNode a = call.getOperands().get(0); switch (a.getKind()) { case NOT: // NOT NOT x ==> x return simplify(((RexCall) a).getOperands().get(0), unknownAs); case LITERAL: if (a.getType().getSqlTypeName() == SqlTypeName.BOOLEAN && !RexLiteral.isNullLiteral(a)) { return rexBuilder.makeLiteral(!RexLiteral.booleanValue(a)); } } 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); } 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), unknownAs)); } return simplify( rexBuilder.makeCall(SqlStdOperatorTable.OR, newOperands), unknownAs); } 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), unknownAs)); } return simplify( rexBuilder.makeCall(SqlStdOperatorTable.AND, newOperands), unknownAs); } if (a.getKind() == SqlKind.CASE) { final List newOperands = new ArrayList<>(); List operands = ((RexCall) a).getOperands(); for (int i = 0; i < operands.size(); i += 2) { if (i + 1 == operands.size()) { newOperands.add(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operands.get(i))); } else { newOperands.add(operands.get(i)); newOperands.add(rexBuilder.makeCall(SqlStdOperatorTable.NOT, operands.get(i + 1))); } } return simplify( rexBuilder.makeCall(SqlStdOperatorTable.CASE, newOperands), unknownAs); } RexNode a2 = simplify(a, unknownAs.negate()); if (a == a2) { return call; } if (a2.isAlwaysTrue()) { return rexBuilder.makeLiteral(false); } if (a2.isAlwaysFalse()) { return rexBuilder.makeLiteral(true); } return rexBuilder.makeCall(SqlStdOperatorTable.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); // 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) similarily 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(rexBuilder.makeCall(SqlStdOperatorTable.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(rexBuilder.makeCall(SqlStdOperatorTable.NOT, a), unknownAs); } final RexNode pred = simplifyIsPredicate(kind, a); if (pred != null) { return pred; } final RexNode simplified = simplifyIs2(kind, a, unknownAs); 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.equals(pred.ref)) { continue; } if (kind == pred.kind) { return rexBuilder.makeLiteral(true); } } return null; } private 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: 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, unknownAs); } else { RexNode newSub = simplify(a, kind == SqlKind.IS_TRUE ? RexUnknownAs.FALSE : RexUnknownAs.TRUE); if (newSub == a) { return null; } return rexBuilder.makeCall(RexUtil.op(kind), ImmutableList.of(newSub)); } 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), unknownAs); } 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); } final RexNode a2 = simplify(a, UNKNOWN); if (a != a2) { return rexBuilder.makeCall(RexUtil.op(kind), ImmutableList.of(a2)); } return null; // cannot be simplified } private 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.getKind())) { 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.com.hazelcast.com.oseConjunction(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 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.getKind())) { 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.com.hazelcast.com.oseDisjunction(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 void validateStrongPolicy(RexNode rexNode) { if (hasCustomNullabilityRules(rexNode.getKind())) { return; } switch (Strong.policy(rexNode.getKind())) { 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); } } } /** * 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 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 com.hazelcast.com.osite 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 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 AND/ORs */ enum SafeRexVisitor implements RexVisitor { INSTANCE; 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.NOT_IN); 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; } for (RexNode o : call.getOperands()) { if (!o.accept(this)) { return false; } } return true; } @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 com.hazelcast.com.ination 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 static RexNode simplifyBooleanCase(RexBuilder rexBuilder, List inputBranches, 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 simplifcation recursion List branches = new ArrayList<>(); for (CaseBranch branch : inputBranches) { if ((branches.size() > 0 && !isSafeExpression(branch.cond)) || !isSafeExpression(branch.value)) { return null; } RexNode cond; RexNode value; if (branch.cond.getType().isNullable()) { cond = rexBuilder.makeCall(SqlStdOperatorTable.IS_TRUE, branch.cond); } else { cond = branch.cond; } 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.com.hazelcast.com.oseDisjunction(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<>(); for (RexNode o : operands) { RelOptUtil.decomposeConjunction(o, terms, notTerms); } switch (unknownAs) { case FALSE: return simplifyAnd2ForUnknownAsFalse(terms, notTerms, Comparable.class); } 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( rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction), UNKNOWN)); } return RexUtil.com.hazelcast.com.oseConjunction(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 com.hazelcast.com.aredOperands = new HashSet<>(); // Add the predicates from the source to the range terms. for (RexNode predicate : predicates.pulledUpPredicates) { final Comparison com.hazelcast.com.arison = Comparison.of(predicate); if (com.hazelcast.com.arison != null && com.hazelcast.com.arison.kind != SqlKind.NOT_EQUALS) { // not supported yet final C v0 = com.hazelcast.com.arison.literal.getValueAs(clazz); if (v0 != null) { final RexNode result = processRange(rexBuilder, terms, rangeTerms, predicate, com.hazelcast.com.arison.ref, v0, com.hazelcast.com.arison.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); com.hazelcast.com.aredOperands.add(left); // if it is a cast, we include the inner reference if (left.getKind() == SqlKind.CAST) { RexCall leftCast = (RexCall) left; com.hazelcast.com.aredOperands.add(leftCast.getOperands().get(0)); } final RexNode right = call.getOperands().get(1); com.hazelcast.com.aredOperands.add(right); // if it is a cast, we include the inner reference if (right.getKind() == SqlKind.CAST) { RexCall rightCast = (RexCall) right; com.hazelcast.com.aredOperands.add(rightCast.getOperands().get(0)); } final Comparison com.hazelcast.com.arison = Comparison.of(term); // Check for com.hazelcast.com.arison with null values if (com.hazelcast.com.arison != null && com.hazelcast.com.arison.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 (com.hazelcast.com.arison != null) { final RexLiteral literal = com.hazelcast.com.arison.literal; final RexLiteral prevLiteral = equalityConstantTerms.put(com.hazelcast.com.arison.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 com.hazelcast.com.arison 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 (com.hazelcast.com.arison != null && com.hazelcast.com.arison.kind != SqlKind.NOT_EQUALS) { // not supported yet final C constant = com.hazelcast.com.arison.literal.getValueAs(clazz); final RexNode result = processRange(rexBuilder, terms, rangeTerms, term, com.hazelcast.com.arison.ref, constant, com.hazelcast.com.arison.kind); if (result != null) { // Not satisfiable return result; } } break; case IN: com.hazelcast.com.aredOperands.add(((RexCall) term).operands.get(0)); break; case BETWEEN: com.hazelcast.com.aredOperands.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)); } } // If one column should be null and is in a com.hazelcast.com.arison predicate, // it is not satisfiable. // Example. IS NULL(x) AND x < 5 - not satisfiable if (!Collections.disjoint(nullOperands, com.hazelcast.com.aredOperands)) { 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 (!com.hazelcast.com.aredOperands.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(rexBuilder.makeCall(SqlStdOperatorTable.NOT, notDisjunction)); } // The negated terms: only deterministic expressions for (RexNode negatedTerm : negatedTerms) { if (termsSet.contains(negatedTerm)) { return rexBuilder.makeLiteral(false); } } return RexUtil.com.hazelcast.com.oseConjunction(rexBuilder, terms); } private RexNode simplifyNotEqual(RexNode e) { final Comparison com.hazelcast.com.arison = Comparison.of(e); if (com.hazelcast.com.arison == null) { return e; } for (RexNode node: predicates.pulledUpPredicates) { final Comparison predicate = Comparison.of(node); if (predicate == null || predicate.kind != SqlKind.EQUALS || !predicate.ref.equals(com.hazelcast.com.arison.ref)) { continue; } // Given x=5, x!=5 can be simplified to 'null and x is null' and x!=3 can // be simplified to 'null or x is not null'. RexNode simplified; if (predicate.literal.equals(com.hazelcast.com.arison.literal)) { simplified = rexBuilder.makeCall(SqlStdOperatorTable.AND, rexBuilder.makeNullLiteral(e.getType()), rexBuilder.makeCall(SqlStdOperatorTable.IS_NULL, com.hazelcast.com.arison.ref)); } else { simplified = rexBuilder.makeCall(SqlStdOperatorTable.OR, rexBuilder.makeNullLiteral(e.getType()), rexBuilder.makeCall(SqlStdOperatorTable.IS_NOT_NULL, com.hazelcast.com.arison.ref)); } return simplify(simplified); } return e; } private > RexNode simplifyUsingPredicates(RexNode e, Class clazz) { if (predicates.pulledUpPredicates.isEmpty()) { return e; } if (e.getKind() == SqlKind.NOT_EQUALS) { return simplifyNotEqual(e); } final Comparison com.hazelcast.com.arison = Comparison.of(e); // Check for com.hazelcast.com.arison with null values if (com.hazelcast.com.arison == null || com.hazelcast.com.arison.literal.getValue() == null) { return e; } final C v0 = com.hazelcast.com.arison.literal.getValueAs(clazz); final Range range = range(com.hazelcast.com.arison.kind, v0); final Range range2 = residue(com.hazelcast.com.arison.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, com.hazelcast.com.arison.ref), RexUnknownAs.UNKNOWN); } 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, com.hazelcast.com.arison.ref, rexBuilder.makeLiteral(range2.lowerEndpoint(), com.hazelcast.com.arison.literal.getType(), com.hazelcast.com.arison.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 com.hazelcast.com.arison 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) { Range result = r0; 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 (result.encloses(r1)) { // Given these predicates, term is always satisfied. // e.g. r0 is "$0 < 10", r1 is "$0 < 5" result = Range.all(); continue; } if (result.isConnected(r1)) { result = result.intersection(r1); continue; } // Ranges do not intersect. Return null meaning the empty range. return null; } } } 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 terms = RelOptUtil.disjunctions(call); if (predicateElimination) { simplifyOrTerms(terms, unknownAs); } else { simplifyList(terms, unknownAs); } return simplifyOrs(terms, unknownAs); } /** Simplifies a list of terms and com.hazelcast.com.ines 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 com.hazelcast.com.ines them into an OR. * Modifies the list in place. */ private RexNode simplifyOrs(List terms, RexUnknownAs 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 com.hazelcast.com.arison values if (prevNotEquals.getKind() != SqlKind.IS_NOT_NULL) { final Comparable com.hazelcast.com.arable1 = notEqualsComparison.literal.getValue(); final Comparable com.hazelcast.com.arable2 = Comparison.of(prevNotEquals).literal.getValue(); //noinspection unchecked if (com.hazelcast.com.arable1.com.hazelcast.com.areTo(com.hazelcast.com.arable2) != 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; } terms.set(i, term); } return RexUtil.com.hazelcast.com.oseDisjunction(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 (!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 (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(); } } } if (!v0.equals(v1)) { throw new AssertionError("result mismatch: when applied to " + map + ", " + before + " yielded " + v0 + ", and " + simplified + " yielded " + v1); } } } 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-specifiic 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)) { 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; } break; } final List reducedValues = new ArrayList<>(); executor.reduce(rexBuilder, ImmutableList.of(e), reducedValues); return Objects.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(); } 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 com.hazelcast.com.arison) { Pair, List> p = rangeTerms.get(ref); if (p == null) { rangeTerms.put(ref, Pair.of(range(com.hazelcast.com.arison, v0), ImmutableList.of(term))); } else { // Exists boolean removeUpperBound = false; boolean removeLowerBound = false; Range r = p.left; final RexLiteral trueLiteral = rexBuilder.makeLiteral(true); switch (com.hazelcast.com.arison) { 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 com.hazelcast.com.arisonResult = 0; if (r.hasUpperBound()) { com.hazelcast.com.arisonResult = v0.com.hazelcast.com.areTo(r.upperEndpoint()); } if (com.hazelcast.com.arisonResult <= 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.com.hazelcast.com.areTo(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 com.hazelcast.com.arisonResult = -1; if (r.hasUpperBound()) { com.hazelcast.com.arisonResult = v0.com.hazelcast.com.areTo(r.upperEndpoint()); } if (com.hazelcast.com.arisonResult < 0) { // 1) No upper bound, or // 2) New upper bound is lower than old upper bound if (r.hasLowerBound()) { if (v0.com.hazelcast.com.areTo(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 com.hazelcast.com.arisonResult = 0; if (r.hasLowerBound()) { com.hazelcast.com.arisonResult = v0.com.hazelcast.com.areTo(r.lowerEndpoint()); } if (com.hazelcast.com.arisonResult >= 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.com.hazelcast.com.areTo(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 com.hazelcast.com.arisonResult = 1; if (r.hasLowerBound()) { com.hazelcast.com.arisonResult = v0.com.hazelcast.com.areTo(r.lowerEndpoint()); } if (com.hazelcast.com.arisonResult > 0) { // 1) No lower bound, or // 2) New lower bound is greater than old lower bound if (r.hasUpperBound()) { if (v0.com.hazelcast.com.areTo(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 com.hazelcast.com.arison, C c) { switch (com.hazelcast.com.arison) { 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); } } /** 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 = Objects.requireNonNull(ref); this.kind = Objects.requireNonNull(kind); this.literal = Objects.requireNonNull(literal); } /** Creates a com.hazelcast.com.arison, between a {@link RexInputRef} or {@link RexFieldAccess} or * deterministic {@link RexCall} and a literal. */ static Comparison of(RexNode e) { return of(e, node -> RexUtil.isReferenceOrAccess(node, true) || RexUtil.isDeterministic(node)); } /** Creates a com.hazelcast.com.arison, or returns null. */ static 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); } } switch (left.getKind()) { case LITERAL: if (nodePredicate.test(right)) { 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. * *

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 RexNode simplifyFilterPredicates(Iterable predicates) { final RexNode simplifiedAnds = withPredicateElimination(Bug.CALCITE_2401_FIXED) .simplifyUnknownAsFalse( RexUtil.com.hazelcast.com.oseConjunction(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) { final int index = list.lastIndexOf(oldVal); if (index < 0) { return false; } list.set(index, newVal); return true; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy