This is an automated email from the ASF dual-hosted git repository.

rubenql pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/master by this push:
     new f3a9f6f  [CALCITE-4415] SqlStdOperatorTable.NOT_LIKE has a wrong 
implementor
f3a9f6f is described below

commit f3a9f6f34cd8a0fb1475810e9bf7fcba27d90e06
Author: rubenada <rube...@gmail.com>
AuthorDate: Tue Nov 24 09:39:51 2020 +0000

    [CALCITE-4415] SqlStdOperatorTable.NOT_LIKE has a wrong implementor
---
 .../calcite/adapter/enumerable/RexImpTable.java    |  4 -
 .../apache/calcite/rel/rel2sql/SqlImplementor.java | 86 +++++++++++-----------
 .../apache/calcite/sql/fun/SqlLikeOperator.java    |  8 ++
 .../java/org/apache/calcite/tools/RelBuilder.java  | 11 +++
 .../org/apache/calcite/test/RelBuilderTest.java    | 64 ++++++++++++++++
 .../adapter/elasticsearch/PredicateAnalyzer.java   | 26 -------
 6 files changed, 128 insertions(+), 71 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java 
b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
index bb65054..0c17078 100644
--- a/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
+++ b/core/src/main/java/org/apache/calcite/adapter/enumerable/RexImpTable.java
@@ -262,8 +262,6 @@ import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.MULTISET_UNION_DIST
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NEXT_VALUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_EQUALS;
-import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_LIKE;
-import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_SIMILAR_TO;
 import static 
org.apache.calcite.sql.fun.SqlStdOperatorTable.NOT_SUBMULTISET_OF;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NTH_VALUE;
 import static org.apache.calcite.sql.fun.SqlStdOperatorTable.NTILE;
@@ -467,12 +465,10 @@ public class RexImpTable {
         new MethodImplementor(BuiltInMethod.LIKE.method, NullPolicy.STRICT,
             false);
     map.put(LIKE, likeImplementor);
-    map.put(NOT_LIKE, likeImplementor);
     final MethodImplementor similarImplementor =
         new MethodImplementor(BuiltInMethod.SIMILAR.method, NullPolicy.STRICT,
             false);
     map.put(SIMILAR_TO, similarImplementor);
-    map.put(NOT_SIMILAR_TO, NotImplementor.of(similarImplementor));
 
     // POSIX REGEX
     final MethodImplementor posixRegexImplementor =
diff --git 
a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java 
b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
index b61e4b8..582e60f 100644
--- a/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
+++ b/core/src/main/java/org/apache/calcite/rel/rel2sql/SqlImplementor.java
@@ -300,16 +300,6 @@ public abstract class SqlImplementor {
     final SqlOperator op;
     final Context joinContext;
     switch (node.getKind()) {
-    case NOT:
-      final RexNode operand0 = ((RexCall) node).getOperands().get(0);
-      final SqlOperator notOperator = 
NOT_KIND_OPERATORS.get(operand0.getKind());
-      if (notOperator != null) {
-        return convertConditionToSqlNode(
-            leftContext.implementor().rexBuilder.makeCall(notOperator,
-                ((RexCall) operand0).operands), leftContext, rightContext,
-            leftFieldCount, dialect);
-      }
-      // fall through
     case AND:
     case OR:
       operands = ((RexCall) node).getOperands();
@@ -331,6 +321,7 @@ public abstract class SqlImplementor {
     case LESS_THAN:
     case LESS_THAN_OR_EQUAL:
     case LIKE:
+    case NOT:
       node = stripCastFromString(node, dialect);
       operands = ((RexCall) node).getOperands();
       op = ((RexCall) node).getOperator();
@@ -820,40 +811,53 @@ public abstract class SqlImplementor {
           return toSql(program, (RexOver) rex);
         }
 
-        final RexCall call = (RexCall) stripCastFromString(rex, dialect);
-        SqlOperator op = call.getOperator();
-        switch (op.getKind()) {
-        case SUM0:
-          op = SqlStdOperatorTable.SUM;
-          break;
-        default:
-          break;
+        return callToSql(program, (RexCall) rex, false);
+      }
+    }
+
+    private SqlNode callToSql(RexProgram program, RexCall rex, boolean not) {
+      final RexCall call = (RexCall) stripCastFromString(rex, dialect);
+      SqlOperator op = call.getOperator();
+      switch (op.getKind()) {
+      case SUM0:
+        op = SqlStdOperatorTable.SUM;
+        break;
+      case NOT:
+        RexNode operand = call.operands.get(0);
+        if (NOT_KIND_OPERATORS.containsKey(operand.getKind())) {
+          return callToSql(program, (RexCall) operand, !not);
         }
-        final List<SqlNode> nodeList = toSql(program, call.getOperands());
-        switch (call.getKind()) {
-        case CAST:
-          // CURSOR is used inside CAST, like 'CAST ($0): CURSOR NOT NULL',
-          // convert it to sql call of {@link SqlStdOperatorTable#CURSOR}.
-          RelDataType dataType = rex.getType();
-          if (dataType.getSqlTypeName() == SqlTypeName.CURSOR) {
-            RexNode operand0 = ((RexCall) rex).operands.get(0);
-            assert operand0 instanceof RexInputRef;
-            int ordinal = ((RexInputRef) operand0).getIndex();
-            SqlNode fieldOperand = field(ordinal);
-            return SqlStdOperatorTable.CURSOR.createCall(SqlParserPos.ZERO, 
fieldOperand);
-          }
-          if (ignoreCast) {
-            assert nodeList.size() == 1;
-            return nodeList.get(0);
-          } else {
-            nodeList.add(dialect.getCastSpec(call.getType()));
-          }
-          break;
-        default:
-          break;
+        break;
+      default:
+        break;
+      }
+      if (not) {
+        op = NOT_KIND_OPERATORS.get(op.getKind());
+      }
+      final List<SqlNode> nodeList = toSql(program, call.getOperands());
+      switch (call.getKind()) {
+      case CAST:
+        // CURSOR is used inside CAST, like 'CAST ($0): CURSOR NOT NULL',
+        // convert it to sql call of {@link SqlStdOperatorTable#CURSOR}.
+        RelDataType dataType = rex.getType();
+        if (dataType.getSqlTypeName() == SqlTypeName.CURSOR) {
+          RexNode operand0 = ((RexCall) rex).operands.get(0);
+          assert operand0 instanceof RexInputRef;
+          int ordinal = ((RexInputRef) operand0).getIndex();
+          SqlNode fieldOperand = field(ordinal);
+          return SqlStdOperatorTable.CURSOR.createCall(SqlParserPos.ZERO, 
fieldOperand);
+        }
+        if (ignoreCast) {
+          assert nodeList.size() == 1;
+          return nodeList.get(0);
+        } else {
+          nodeList.add(dialect.getCastSpec(call.getType()));
         }
-        return SqlUtil.createCall(op, POS, nodeList);
+        break;
+      default:
+        break;
       }
+      return SqlUtil.createCall(op, POS, nodeList);
     }
 
     /** Converts a Sarg to SQL, generating "operand IN (c1, c2, ...)" if the
diff --git a/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java
index 89e889b..cf7f287 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLikeOperator.java
@@ -31,6 +31,7 @@ import org.apache.calcite.sql.type.OperandTypes;
 import org.apache.calcite.sql.type.ReturnTypes;
 import org.apache.calcite.sql.type.SqlOperandCountRanges;
 import org.apache.calcite.sql.type.SqlTypeUtil;
+import org.apache.calcite.util.Litmus;
 
 /**
  * An operator describing the <code>LIKE</code> and <code>SIMILAR</code>
@@ -128,6 +129,13 @@ public class SqlLikeOperator extends SqlSpecialOperator {
         throwOnFailure);
   }
 
+  @Override public boolean validRexOperands(int count, Litmus litmus) {
+    if (negated) {
+      litmus.fail("unsupported negated operator {}", this);
+    }
+    return super.validRexOperands(count, litmus);
+  }
+
   @Override public void unparse(
       SqlWriter writer,
       SqlCall call,
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java 
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index 24ac3be..e3d174a 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -82,6 +82,7 @@ import org.apache.calcite.schema.impl.ListTransientTable;
 import org.apache.calcite.sql.SqlAggFunction;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlOperator;
+import org.apache.calcite.sql.fun.SqlLikeOperator;
 import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
 import org.apache.calcite.sql.type.SqlTypeName;
@@ -627,6 +628,16 @@ public class RelBuilder {
   /** Creates a call to a scalar operator. */
   private @Nonnull RexCall call(SqlOperator operator, List<RexNode> 
operandList) {
     switch (operator.getKind()) {
+    case LIKE:
+      if (((SqlLikeOperator) operator).isNegated()) {
+        return (RexCall) not(call(SqlStdOperatorTable.LIKE, operandList));
+      }
+      break;
+    case SIMILAR:
+      if (((SqlLikeOperator) operator).isNegated()) {
+        return (RexCall) not(call(SqlStdOperatorTable.SIMILAR_TO, 
operandList));
+      }
+      break;
     case BETWEEN:
       assert operandList.size() == 3;
       return (RexCall) between(operandList.get(0), operandList.get(1),
diff --git a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java 
b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
index f1eaadb..2523627 100644
--- a/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
+++ b/core/src/test/java/org/apache/calcite/test/RelBuilderTest.java
@@ -17,6 +17,7 @@
 package org.apache.calcite.test;
 
 import org.apache.calcite.adapter.enumerable.EnumerableConvention;
+import org.apache.calcite.adapter.java.ReflectiveSchema;
 import org.apache.calcite.jdbc.CalciteConnection;
 import org.apache.calcite.plan.Contexts;
 import org.apache.calcite.plan.Convention;
@@ -3888,4 +3889,67 @@ public class RelBuilderTest {
       assertThat(result, is(expectedResult));
     }
   }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-4415";>[CALCITE-4415]
+   * SqlStdOperatorTable.NOT_LIKE has a wrong implementor</a>. */
+  @Test void testNotLike() {
+    final RelBuilder builder = RelBuilder.create(config().build());
+    RelNode root =
+        builder.scan("EMP")
+            .filter(
+                builder.call(
+                    SqlStdOperatorTable.NOT_LIKE,
+                    builder.field("ENAME"),
+                    builder.literal("a%b%c")))
+            .build();
+    final String expected = ""
+        + "LogicalFilter(condition=[NOT(LIKE($1, 'a%b%c'))])\n"
+        + "  LogicalTableScan(table=[[scott, EMP]])\n";
+    assertThat(root, hasTree(expected));
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-4415";>[CALCITE-4415]
+   * SqlStdOperatorTable.NOT_LIKE has a wrong implementor</a>. */
+  @Test void testNotSimilarTo() {
+    final RelBuilder builder = RelBuilder.create(config().build());
+    RelNode root =
+        builder.scan("EMP")
+            .filter(
+                builder.call(
+                    SqlStdOperatorTable.NOT_SIMILAR_TO,
+                    builder.field("ENAME"),
+                    builder.literal("a%b%c")))
+            .build();
+    final String expected = ""
+        + "LogicalFilter(condition=[NOT(SIMILAR TO($1, 'a%b%c'))])\n"
+        + "  LogicalTableScan(table=[[scott, EMP]])\n";
+    assertThat(root, hasTree(expected));
+  }
+
+  /** Test case for
+   * <a 
href="https://issues.apache.org/jira/browse/CALCITE-4415";>[CALCITE-4415]
+   * SqlStdOperatorTable.NOT_LIKE has a wrong implementor</a>. */
+  @Test void testExecuteNotLike() {
+    CalciteAssert.that()
+        .withSchema("s", new ReflectiveSchema(new JdbcTest.HrSchema()))
+        .query("?")
+        .withRel(
+            builder -> builder
+                .scan("s", "emps")
+                .filter(
+                    builder.call(
+                        SqlStdOperatorTable.NOT_LIKE,
+                        builder.field("name"),
+                        builder.literal("%r%c")))
+                .project(
+                    builder.field("empid"),
+                    builder.field("name"))
+                .build())
+        .returnsUnordered(
+            "empid=100; name=Bill",
+            "empid=110; name=Theodore",
+            "empid=150; name=Sebastian");
+  }
 }
diff --git 
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
 
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
index 5828a05..880d5b0 100644
--- 
a/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
+++ 
b/elasticsearch/src/main/java/org/apache/calcite/adapter/elasticsearch/PredicateAnalyzer.java
@@ -20,16 +20,13 @@ import 
org.apache.calcite.adapter.elasticsearch.QueryBuilders.BoolQueryBuilder;
 import org.apache.calcite.adapter.elasticsearch.QueryBuilders.QueryBuilder;
 import 
org.apache.calcite.adapter.elasticsearch.QueryBuilders.RangeQueryBuilder;
 import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.rex.RexCall;
 import org.apache.calcite.rex.RexInputRef;
 import org.apache.calcite.rex.RexLiteral;
 import org.apache.calcite.rex.RexNode;
-import org.apache.calcite.rex.RexShuttle;
 import org.apache.calcite.rex.RexVisitorImpl;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.calcite.sql.SqlSyntax;
-import org.apache.calcite.sql.fun.SqlStdOperatorTable;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
 
@@ -118,29 +115,6 @@ class PredicateAnalyzer {
   }
 
   /**
-   * Converts expressions of the form NOT(LIKE(...)) into NOT_LIKE(...)
-   */
-  @SuppressWarnings("unused")
-  private static class NotLikeConverter extends RexShuttle {
-    final RexBuilder rexBuilder;
-
-    NotLikeConverter(RexBuilder rexBuilder) {
-      this.rexBuilder = rexBuilder;
-    }
-
-    @Override public RexNode visitCall(RexCall call) {
-      if (call.getOperator().getKind() == SqlKind.NOT) {
-        RexNode child = call.getOperands().get(0);
-        if (child.getKind() == SqlKind.LIKE) {
-          return rexBuilder.makeCall(SqlStdOperatorTable.NOT_LIKE,
-              visitList(((RexCall) child).getOperands()));
-        }
-      }
-      return super.visitCall(call);
-    }
-  }
-
-  /**
    * Traverses {@link RexNode} tree and builds ES query.
    */
   private static class Visitor extends RexVisitorImpl<Expression> {

Reply via email to