This is an automated email from the ASF dual-hosted git repository. mhubail pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/asterixdb.git
The following commit(s) were added to refs/heads/master by this push: new 4d5476e [ASTERIXDB-2951][COMP] Support IS DISTINCT FROM operator 4d5476e is described below commit 4d5476e6672e6bb749cc2a0de69f4887ff5dce9b Author: Dmitry Lychagin <dmitry.lycha...@couchbase.com> AuthorDate: Wed Aug 18 19:43:22 2021 -0700 [ASTERIXDB-2951][COMP] Support IS DISTINCT FROM operator - user model changes: no - storage format changes: no - interface changes: no Details: - Add support for IS [NOT] DISTINCT FROM comparison operator - Add testcases and update documentation Change-Id: Ifad404fa7c613771f60bd12f3aa2bd5fea77ba34 Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/12884 Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu> Reviewed-by: Dmitry Lychagin <dmitry.lycha...@couchbase.com> Reviewed-by: Ali Alsuliman <ali.al.solai...@gmail.com> --- .../is_distinct_01/is_distinct_01.1.query.sqlpp | 39 +++++++++ .../is_distinct_01/is_distinct_01.2.query.sqlpp | 42 +++++++++ .../comparison/is_distinct_01/is_distinct_01.1.adm | 81 ++++++++++++++++++ .../comparison/is_distinct_01/is_distinct_01.2.adm | 1 + .../test/resources/runtimets/testsuite_sqlpp.xml | 5 ++ .../asterix-doc/src/main/markdown/sqlpp/2_expr.md | 4 +- .../asterix/lang/common/struct/OperatorType.java | 4 +- .../visitor/OperatorExpressionVisitor.java | 99 ++++++++++++++++++---- .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj | 13 ++- 9 files changed, 268 insertions(+), 20 deletions(-) diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/comparison/is_distinct_01/is_distinct_01.1.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/comparison/is_distinct_01/is_distinct_01.1.query.sqlpp new file mode 100644 index 0000000..c0ed568 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/comparison/is_distinct_01/is_distinct_01.1.query.sqlpp @@ -0,0 +1,39 @@ +/* + * 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 compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.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. + */ + +from + [ int64(2), float(2.0), double(2.5), "str1", "str2", true, false, null, missing ] arg1, + [ int32(2), double(2.0), float(2.5), "str1", "str2", true, false, null, missing ] arg2 +let + is_distinct = arg1 is distinct from arg2, + is_not_distinct = arg1 is not distinct from arg2 +select + case + when arg1 is missing then "MISSING" + when arg1 is null then "NULL" + else arg1 + end as arg1, + case + when arg2 is missing then "MISSING" + when arg2 is null then "NULL" + else arg2 + end as arg2, + is_distinct as `!!==`, + is_not_distinct as `==` +order by is_distinct, arg1, arg2 diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/comparison/is_distinct_01/is_distinct_01.2.query.sqlpp b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/comparison/is_distinct_01/is_distinct_01.2.query.sqlpp new file mode 100644 index 0000000..fcc1dc2 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/queries_sqlpp/comparison/is_distinct_01/is_distinct_01.2.query.sqlpp @@ -0,0 +1,42 @@ +/* + * 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 compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.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. + */ + +let + x = 2, y = 3 +select + /* test expression */ + x between 1 and 3 is not distinct from x between 0 and 4 as t1, + /* expected operator precedence */ + (x between 1 and 3) is not distinct from (x between 0 and 4) as t1_expected, + /* unexpected operator precedence */ + (((x between 1 and 3) is not distinct from x) between 0 and 4) as t1_unexpected, + + /* test expression */ + x is unknown is not distinct from y is unknown as t2, + /* expected operator precedence */ + (x is unknown) is not distinct from (y is unknown) as t2_expected, + /* unexpected operator precedence */ + (((x is unknown) is not distinct from y) is unknown) as t2_unexpected, + + /* test expression */ + x is not unknown is distinct from y is unknown as t3, + /* expected operator precedence */ + (x is not unknown) is distinct from (y is unknown) as t3_expected, + /* unexpected operator precedence */ + (((x is not unknown) is distinct from y) is unknown) as t3_unexpected; diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/comparison/is_distinct_01/is_distinct_01.1.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/comparison/is_distinct_01/is_distinct_01.1.adm new file mode 100644 index 0000000..6b0764d --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/comparison/is_distinct_01/is_distinct_01.1.adm @@ -0,0 +1,81 @@ +{ "!!==": false, "==": true, "arg1": "MISSING", "arg2": "MISSING" } +{ "!!==": false, "==": true, "arg1": "NULL", "arg2": "NULL" } +{ "!!==": false, "==": true, "arg1": 2, "arg2": 2 } +{ "!!==": false, "==": true, "arg1": 2, "arg2": 2.0 } +{ "!!==": false, "==": true, "arg1": 2.0, "arg2": 2 } +{ "!!==": false, "==": true, "arg1": 2.0, "arg2": 2.0 } +{ "!!==": false, "==": true, "arg1": 2.5, "arg2": 2.5 } +{ "!!==": false, "==": true, "arg1": "str1", "arg2": "str1" } +{ "!!==": false, "==": true, "arg1": "str2", "arg2": "str2" } +{ "!!==": false, "==": true, "arg1": false, "arg2": false } +{ "!!==": false, "==": true, "arg1": true, "arg2": true } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": 2 } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": false } +{ "!!==": true, "==": false, "arg1": "MISSING", "arg2": true } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": 2 } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": false } +{ "!!==": true, "==": false, "arg1": "NULL", "arg2": true } +{ "!!==": true, "==": false, "arg1": 2, "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": 2, "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": 2, "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": 2, "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": 2, "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": 2, "arg2": false } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": false } +{ "!!==": true, "==": false, "arg1": 2, "arg2": true } +{ "!!==": true, "==": false, "arg1": 2.0, "arg2": true } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": 2 } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": false } +{ "!!==": true, "==": false, "arg1": 2.5, "arg2": true } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": 2 } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": false } +{ "!!==": true, "==": false, "arg1": "str1", "arg2": true } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": 2 } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": false } +{ "!!==": true, "==": false, "arg1": "str2", "arg2": true } +{ "!!==": true, "==": false, "arg1": false, "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": false, "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": false, "arg2": 2 } +{ "!!==": true, "==": false, "arg1": false, "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": false, "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": false, "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": false, "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": false, "arg2": true } +{ "!!==": true, "==": false, "arg1": true, "arg2": "MISSING" } +{ "!!==": true, "==": false, "arg1": true, "arg2": "NULL" } +{ "!!==": true, "==": false, "arg1": true, "arg2": 2 } +{ "!!==": true, "==": false, "arg1": true, "arg2": 2.0 } +{ "!!==": true, "==": false, "arg1": true, "arg2": 2.5 } +{ "!!==": true, "==": false, "arg1": true, "arg2": "str1" } +{ "!!==": true, "==": false, "arg1": true, "arg2": "str2" } +{ "!!==": true, "==": false, "arg1": true, "arg2": false } diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/results/comparison/is_distinct_01/is_distinct_01.2.adm b/asterixdb/asterix-app/src/test/resources/runtimets/results/comparison/is_distinct_01/is_distinct_01.2.adm new file mode 100644 index 0000000..bd83c66 --- /dev/null +++ b/asterixdb/asterix-app/src/test/resources/runtimets/results/comparison/is_distinct_01/is_distinct_01.2.adm @@ -0,0 +1 @@ +{ "t1": true, "t1_expected": true, "t1_unexpected": null, "t2": true, "t2_expected": true, "t2_unexpected": false, "t3": true, "t3_expected": true, "t3_unexpected": false } diff --git a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml index 5937278..cb4c5ad 100644 --- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml +++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml @@ -3431,6 +3431,11 @@ </compilation-unit> </test-case> <test-case FilePath="comparison"> + <compilation-unit name="is_distinct_01"> + <output-dir compare="Text">is_distinct_01</output-dir> + </compilation-unit> + </test-case> + <test-case FilePath="comparison"> <compilation-unit name="like"> <output-dir compare="Text">like</output-dir> </compilation-unit> diff --git a/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md b/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md index 9ecc29b..37bd2f0 100644 --- a/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md +++ b/asterixdb/asterix-doc/src/main/markdown/sqlpp/2_expr.md @@ -53,7 +53,7 @@ The following table summarizes the precedence order (from higher to lower) of th | || | String concatenation | | IS NULL, IS NOT NULL, IS MISSING, IS NOT MISSING, <br/>IS UNKNOWN, IS NOT UNKNOWN, IS VALUED, IS NOT VALUED | Unknown value comparison | | BETWEEN, NOT BETWEEN | Range comparison (inclusive on both sides) | -| =, !=, <>, <, >, <=, >=, LIKE, NOT LIKE, IN, NOT IN | Comparison | +| =, !=, <>, <, >, <=, >=, LIKE, NOT LIKE, IN, NOT IN, IS DISTINCT FROM, IS NOT DISTINCT FROM | Comparison | | NOT | Logical negation | | AND | Conjunction | | OR | Disjunction | @@ -130,6 +130,8 @@ The following table enumerates all of the comparison operators available in SQL+ | >= | Greater than or equal to | FROM customers AS c <br/> WHERE c.rating >= 640 <br/> SELECT *; | | LIKE | Test if the left side matches a pattern defined on the right side; in the pattern, "%" matches any string while "_" matches any character. | FROM customers AS c WHERE c.name LIKE "%Dodge%" SELECT *;| | NOT LIKE | Test if the left side does not match a pattern defined on the right side; in the pattern, "%" matches any string while "_" matches any character. | FROM customers AS c WHERE c.name NOT LIKE "%Dodge%" SELECT *;| +| IS DISTINCT FROM | Inequality test that that treats NULL values as equal to each other and MISSING values as equal to each other | FROM orders AS o <br/> WHERE o.order_date IS DISTINCT FROM o.ship_date <br/> SELECT *; | | +| IS NOT DISTINCT FROM | Equality test that treats NULL values as equal to each other and MISSING values as equal to each other | FROM orders AS o <br/> WHERE o.order_date IS NOT DISTINCT FROM o.ship_date <br/> SELECT *; | The following table summarizes how the missing value comparison operators work. diff --git a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/struct/OperatorType.java b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/struct/OperatorType.java index f4f2ae1..e207e75 100644 --- a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/struct/OperatorType.java +++ b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/struct/OperatorType.java @@ -41,7 +41,9 @@ public enum OperatorType { IN("in"), NOT_IN("not_in"), BETWEEN("between"), - NOT_BETWEEN("not_between"); + NOT_BETWEEN("not_between"), + DISTINCT("distinct"), + NOT_DISTINCT("not_distinct"); private static final OperatorType[] VALUES = values(); diff --git a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java index c47634e..93a14ca 100644 --- a/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java +++ b/asterixdb/asterix-lang-sqlpp/src/main/java/org/apache/asterix/lang/sqlpp/rewrites/visitor/OperatorExpressionVisitor.java @@ -29,13 +29,17 @@ import org.apache.asterix.common.functions.FunctionSignature; import org.apache.asterix.lang.common.base.Expression; import org.apache.asterix.lang.common.base.ILangExpression; import org.apache.asterix.lang.common.expression.CallExpr; +import org.apache.asterix.lang.common.expression.LiteralExpr; import org.apache.asterix.lang.common.expression.OperatorExpr; import org.apache.asterix.lang.common.expression.QuantifiedExpression; import org.apache.asterix.lang.common.expression.QuantifiedExpression.Quantifier; import org.apache.asterix.lang.common.expression.VariableExpr; +import org.apache.asterix.lang.common.literal.FalseLiteral; +import org.apache.asterix.lang.common.literal.TrueLiteral; import org.apache.asterix.lang.common.rewrites.LangRewritingContext; import org.apache.asterix.lang.common.struct.OperatorType; import org.apache.asterix.lang.common.struct.QuantifiedPair; +import org.apache.asterix.lang.sqlpp.expression.CaseExpression; import org.apache.asterix.lang.sqlpp.util.FunctionMapUtil; import org.apache.asterix.lang.sqlpp.util.SqlppRewriteUtil; import org.apache.asterix.lang.sqlpp.visitor.base.AbstractSqlppExpressionScopingVisitor; @@ -71,8 +75,9 @@ public class OperatorExpressionVisitor extends AbstractSqlppExpressionScopingVis case BETWEEN: case NOT_BETWEEN: return processBetweenOperator(operatorExpr, opType); - default: - break; + case DISTINCT: + case NOT_DISTINCT: + return processDistinctOperator(operatorExpr, opType); } return operatorExpr; } @@ -144,18 +149,83 @@ public class OperatorExpressionVisitor extends AbstractSqlppExpressionScopingVis Expression targetCopy = (Expression) SqlppRewriteUtil.deepCopy(target); Expression rightComparison = createOperatorExpression(OperatorType.LE, targetCopy, right, operatorExpr.getHints(), operatorExpr.getSourceLocation()); - OperatorExpr andExpr = new OperatorExpr(); - andExpr.addOperand(leftComparison); - andExpr.addOperand(rightComparison); - andExpr.addOperator(OperatorType.AND); - andExpr.setSourceLocation(operatorExpr.getSourceLocation()); - if (opType == OperatorType.BETWEEN) { - return andExpr; - } else { - CallExpr callExpr = new CallExpr(new FunctionSignature(BuiltinFunctions.NOT), - new ArrayList<>(Collections.singletonList(andExpr))); - callExpr.setSourceLocation(operatorExpr.getSourceLocation()); - return callExpr; + Expression andExpr = createOperatorExpression(OperatorType.AND, leftComparison, rightComparison, null, + operatorExpr.getSourceLocation()); + switch (opType) { + case BETWEEN: + return andExpr; + case NOT_BETWEEN: + CallExpr callExpr = new CallExpr(new FunctionSignature(BuiltinFunctions.NOT), + new ArrayList<>(Collections.singletonList(andExpr))); + callExpr.setSourceLocation(operatorExpr.getSourceLocation()); + return callExpr; + default: + throw new IllegalArgumentException(String.valueOf(opType)); + } + } + + private Expression processDistinctOperator(OperatorExpr operatorExpr, OperatorType opType) + throws CompilationException { + + // lhs IS NOT DISTINCT FROM rhs => + // CASE + // WHEN (lhs = rhs) OR (lhs IS NULL AND rhs IS NULL) OR (lhs IS MISSING AND rhs IS MISSING) + // THEN TRUE + // ELSE FALSE + // END + // + // lhs IS DISTINCT FROM rhs => NOT ( lhs IS NOT DISTINCT FROM rhs ) + + Expression lhs = operatorExpr.getExprList().get(0); + Expression rhs = operatorExpr.getExprList().get(1); + + Expression lhsEqRhs = createOperatorExpression(OperatorType.EQ, lhs, rhs, operatorExpr.getHints(), + operatorExpr.getSourceLocation()); + + CallExpr lhsIsNull = new CallExpr(new FunctionSignature(BuiltinFunctions.IS_NULL), + new ArrayList<>(Collections.singletonList((Expression) SqlppRewriteUtil.deepCopy(lhs)))); + lhsIsNull.setSourceLocation(operatorExpr.getSourceLocation()); + + CallExpr rhsIsNull = new CallExpr(new FunctionSignature(BuiltinFunctions.IS_NULL), + new ArrayList<>(Collections.singletonList((Expression) SqlppRewriteUtil.deepCopy(rhs)))); + rhsIsNull.setSourceLocation(operatorExpr.getSourceLocation()); + + CallExpr lhsIsMissing = new CallExpr(new FunctionSignature(BuiltinFunctions.IS_MISSING), + new ArrayList<>(Collections.singletonList((Expression) SqlppRewriteUtil.deepCopy(lhs)))); + lhsIsMissing.setSourceLocation(operatorExpr.getSourceLocation()); + + CallExpr rhsIsMissing = new CallExpr(new FunctionSignature(BuiltinFunctions.IS_MISSING), + new ArrayList<>(Collections.singletonList((Expression) SqlppRewriteUtil.deepCopy(rhs)))); + rhsIsMissing.setSourceLocation(operatorExpr.getSourceLocation()); + + Expression bothAreNull = createOperatorExpression(OperatorType.AND, lhsIsNull, rhsIsNull, null, + operatorExpr.getSourceLocation()); + + Expression bothAreMissing = createOperatorExpression(OperatorType.AND, lhsIsMissing, rhsIsMissing, null, + operatorExpr.getSourceLocation()); + + Expression bothAreNullOrMissing = createOperatorExpression(OperatorType.OR, bothAreNull, bothAreMissing, null, + operatorExpr.getSourceLocation()); + + Expression eqOrNullOrMissing = createOperatorExpression(OperatorType.OR, lhsEqRhs, bothAreNullOrMissing, null, + operatorExpr.getSourceLocation()); + + CaseExpression caseExpr = new CaseExpression(new LiteralExpr(TrueLiteral.INSTANCE), + new ArrayList<>(Collections.singletonList(eqOrNullOrMissing)), + new ArrayList<>(Collections.singletonList(new LiteralExpr(TrueLiteral.INSTANCE))), + new LiteralExpr(FalseLiteral.INSTANCE)); + caseExpr.setSourceLocation(operatorExpr.getSourceLocation()); + + switch (opType) { + case NOT_DISTINCT: + return caseExpr; + case DISTINCT: + CallExpr callExpr = new CallExpr(new FunctionSignature(BuiltinFunctions.NOT), + new ArrayList<>(Collections.singletonList(caseExpr))); + callExpr.setSourceLocation(operatorExpr.getSourceLocation()); + return callExpr; + default: + throw new IllegalArgumentException(String.valueOf(opType)); } } @@ -173,5 +243,4 @@ public class OperatorExpressionVisitor extends AbstractSqlppExpressionScopingVis } return comparison; } - } diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj index b240779..ca4d903 100644 --- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj +++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj @@ -3264,6 +3264,7 @@ Expression RelExpr() throws ParseException: { boolean not = false; OperatorExpr op = null; + Token opToken = null; Expression operand = null; IExpressionAnnotation annotation = null; } @@ -3271,8 +3272,12 @@ Expression RelExpr() throws ParseException: operand = BetweenExpr() ( - LOOKAHEAD(2)( <LT> | <GT> | <LE> | <GE> | <EQ> | <NE> | <LG> |<SIMILAR> | (<NOT> { not = true; })? <IN>) + LOOKAHEAD(3)( <LT> | <GT> | <LE> | <GE> | <EQ> | <NE> | <LG> |<SIMILAR> | (<NOT> { not = true; })? <IN> | + <IS> (<NOT> { not = true; })? <DISTINCT> { opToken = token; } <FROM> ) { + if (opToken == null) { + opToken = token; + } Token hintToken = fetchHint(token, SqlppHint.HASH_BROADCAST_JOIN_HINT, SqlppHint.INDEXED_NESTED_LOOP_JOIN_HINT, SqlppHint.SKIP_SECONDARY_INDEX_SEARCH_HINT, SqlppHint.USE_SECONDARY_INDEX_SEARCH_HINT @@ -3280,7 +3285,7 @@ Expression RelExpr() throws ParseException: if (hintToken != null) { annotation = parseExpressionAnnotation(hintToken); } - String operator = token.image.toLowerCase(); + String operator = opToken.image.toLowerCase(); if (operator.equals("<>")){ operator = "!="; } @@ -3382,7 +3387,9 @@ Expression IsExpr() throws ParseException: } { operand = LikeExpr() - ( <IS> + ( + LOOKAHEAD(3) + <IS> (<NOT> { not = true; notToken = token; })? ( <NULL> { fn = BuiltinFunctions.IS_NULL; } |