You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by ru...@apache.org on 2020/11/29 10:21:37 UTC
[calcite] branch master updated: [CALCITE-4415]
SqlStdOperatorTable.NOT_LIKE has a wrong implementor
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 <ru...@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> {