liyafan82 commented on a change in pull request #2124:
URL: https://github.com/apache/calcite/pull/2124#discussion_r480047756
##########
File path: core/src/main/java/org/apache/calcite/rex/RexSimplify.java
##########
@@ -2515,4 +2510,214 @@ public RexNode simplifyFilterPredicates(Iterable<?
extends RexNode> predicates)
return true;
}
+ /** Gathers expressions that can be converted into
+ * {@link Sarg search arguments}. */
+ static class SargCollector {
+ final Map<RexNode, RexSargBuilder> map = new HashMap<>();
+ private final RexBuilder rexBuilder;
+ private final boolean negate;
+
+ SargCollector(RexBuilder rexBuilder, boolean negate) {
+ this.rexBuilder = rexBuilder;
+ this.negate = negate;
+ }
+
+ private void accept(RexNode term, List<RexNode> newTerms) {
+ if (!accept_(term, newTerms)) {
+ newTerms.add(term);
+ }
+ }
+
+ private boolean accept_(RexNode e, List<RexNode> 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:
+ if (negate) {
+ return false;
+ }
+ return accept1(((RexCall) e).operands.get(0), e.getKind(),
+ rexBuilder.makeNullLiteral(e.getType()), newTerms);
+ default:
+ return false;
+ }
+ }
+
+ private boolean accept2(RexNode left, RexNode right, SqlKind kind,
+ List<RexNode> newTerms) {
+ switch (left.getKind()) {
+ case INPUT_REF:
+ case FIELD_ACCESS:
+ switch (right.getKind()) {
+ case LITERAL:
+ return accept1(left, kind, (RexLiteral) right, newTerms);
+ }
+ return false;
+ case LITERAL:
+ switch (right.getKind()) {
+ case INPUT_REF:
+ case FIELD_ACCESS:
+ return accept1(right, kind.reverse(), (RexLiteral) left, newTerms);
+ }
+ return false;
+ }
+ return false;
+ }
+
+ private static <E> E addFluent(List<? super E> list, E e) {
+ list.add(e);
+ return e;
+ }
+
+ // always returns true
+ private boolean accept1(RexNode e, SqlKind kind,
+ @Nonnull RexLiteral literal, List<RexNode> newTerms) {
+ final RexSargBuilder b =
+ map.computeIfAbsent(e, e2 ->
+ addFluent(newTerms, new RexSargBuilder(e2, rexBuilder, negate)));
+ if (negate) {
+ kind = kind.negateNullSafe();
+ }
+ final Comparable value = literal.getValueAs(Comparable.class);
+ 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 = literal.getValueAs(Sarg.class);
+ b.addSarg(sarg, negate, literal.getType());
+ return true;
+ case IS_NULL:
+ if (negate) {
+ throw new AssertionError();
+ }
+ b.containsNull = true;
+ return true;
+ default:
+ throw new AssertionError("unexpected " + kind);
+ }
+ }
+
+ /** If a term is a call to {@code SEARCH} on a {@link RexSargBuilder},
+ * converts it to a {@code SEARCH} on a {@link Sarg}. */
+ RexNode fix(RexBuilder rexBuilder, RexNode term) {
+ if (term instanceof RexSargBuilder) {
+ RexSargBuilder sargBuilder = (RexSargBuilder) term;
+ return rexBuilder.makeCall(SqlStdOperatorTable.SEARCH, sargBuilder.ref,
+ rexBuilder.makeSearchArgumentLiteral(sargBuilder.build(negate),
+ 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.
+ *
+ * <p>The {@link SargCollector#fix} method converts it to an immutable
+ * literal. */
+ static class RexSargBuilder extends RexNode {
+ final RexNode ref;
+ private final RexBuilder rexBuilder;
+ private List<RelDataType> types = new ArrayList<>();
+ final RangeSet<Comparable> rangeSet = TreeRangeSet.create();
+ boolean containsNull;
+
+ RexSargBuilder(RexNode ref, RexBuilder rexBuilder, boolean negate) {
+ this.ref = Objects.requireNonNull(ref);
+ this.rexBuilder = Objects.requireNonNull(rexBuilder);
+ this.containsNull = false;
+ }
+
+ @Override public String toString() {
+ return "SEARCH(" + ref + ", " + rangeSet
+ + (containsNull ? " + null)" : ")");
+ }
+
+ /** Returns a rough estimate of whether it is worth converting to a Sarg.
+ *
+ * <p>Examples:
+ * <ul>
+ * <li>{@code x = 1}, {@code x <> 1}, {@code x > 1} have complexity 1
+ * <li>{@code x > 1 or x is null} has complexity 2
+ * <li>{@code x in (2, 4, 6) or x > 20} has complexity 4
Review comment:
Should we take into account the cost of the logical operations?
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]