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> {