This is an automated email from the ASF dual-hosted git repository.
zstan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new c76d322e12 IGNITE-22171: Sql. Row cast fails with NPE (#4010)
c76d322e12 is described below
commit c76d322e12f079864ad1f93c4baa37ce568c2013
Author: Max Zhuravkov <[email protected]>
AuthorDate: Wed Jul 3 20:55:13 2024 +0300
IGNITE-22171: Sql. Row cast fails with NPE (#4010)
---
.../sql/basic_queries/test_simple_table.test | 2 +-
.../generic/test_in_list_of_single_element.test | 10 +-
.../integrationTest/sql/types/row/test_row.test | 49 ++++++
.../sql/engine/prepare/IgniteSqlValidator.java | 183 +++++++++++++++------
.../internal/sql/engine/util/IgniteResource.java | 3 +
.../sql/engine/planner/DynamicParametersTest.java | 18 +-
6 files changed, 205 insertions(+), 60 deletions(-)
diff --git
a/modules/sql-engine/src/integrationTest/sql/basic_queries/test_simple_table.test
b/modules/sql-engine/src/integrationTest/sql/basic_queries/test_simple_table.test
index a509fc2e93..bdd66f9e1d 100644
---
a/modules/sql-engine/src/integrationTest/sql/basic_queries/test_simple_table.test
+++
b/modules/sql-engine/src/integrationTest/sql/basic_queries/test_simple_table.test
@@ -84,7 +84,7 @@ b 2
b 2
skipif ignite3
-# https://issues.apache.org/jira/browse/IGNITE-22171
+# https://issues.apache.org/jira/browse/IGNITE-22084
query II rowsort
SELECT * FROM (VALUES ROW(CAST(ROW(1, 2.0) AS ROW(x BIGINT, y DECIMAL(2,
1)))));
----
diff --git
a/modules/sql-engine/src/integrationTest/sql/function/generic/test_in_list_of_single_element.test
b/modules/sql-engine/src/integrationTest/sql/function/generic/test_in_list_of_single_element.test
index 2f127947c8..7526b10bc7 100644
---
a/modules/sql-engine/src/integrationTest/sql/function/generic/test_in_list_of_single_element.test
+++
b/modules/sql-engine/src/integrationTest/sql/function/generic/test_in_list_of_single_element.test
@@ -178,31 +178,37 @@ SELECT NULL IN (SELECT 1)
----
null
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
# Row value constructions
+skipif ignite3
query T
SELECT (1, 'hello', TIMESTAMP '2022-02-01 10:30:28') IN ((1, 'hello',
TIMESTAMP '2022-02-01 10:30:28'));
----
true
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+skipif ignite3
query T
SELECT (1, 'hello') IN ((1, 'world'));
----
false
skipif ignite3
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-22014
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
query T
SELECT (1, 'hello') IN ((1, NULL));
----
NULL
+skipif ignite3
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
query T
SELECT (1, 'hello') NOT IN ((1, 'hello'));
----
false
skipif ignite3
-# Ignored: https://issues.apache.org/jira/browse/IGNITE-22014
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
query T
SELECT (1, NULL) IN ((1, NULL));
----
diff --git a/modules/sql-engine/src/integrationTest/sql/types/row/test_row.test
b/modules/sql-engine/src/integrationTest/sql/types/row/test_row.test
index 6358642cf7..d08d86c364 100644
--- a/modules/sql-engine/src/integrationTest/sql/types/row/test_row.test
+++ b/modules/sql-engine/src/integrationTest/sql/types/row/test_row.test
@@ -18,11 +18,16 @@ INSERT INTO emp VALUES(3, 'Noah', 47, 1200)
statement ok
INSERT INTO emp VALUES(4, 'Dave', 42, 1700)
+skipif ignite3
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+# Error ROW datatype is not supported
query T
SELECT empname FROM emp WHERE (empname, empage, salary) = ('Johnah', 47, 1200)
----
Johnah
+skipif ignite3
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
query T
SELECT empname FROM emp WHERE ('Johnah', 47) IN ((empname, empage))
----
@@ -43,3 +48,47 @@ SELECT DISTINCT (empage, salary) FROM emp
(42,1700)
(47,1200)
(47,2000)
+
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+statement error: ROW datatype is not supported
+SELECT CAST(ROW(1, 2.0) AS ROW(x BIGINT, y DOUBLE))
+
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+statement error: ROW datatype is not supported
+SELECT ROW(1, 2.0)
+
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+statement error: ROW datatype is not supported
+SELECT (1, 2.0)
+
+# (empage, salary) uses the ROW operator
+statement ok
+SELECT empage, salary FROM emp GROUP BY (empage, salary)
+
+statement error: ROW datatype is not supported
+SELECT (1, 2) IN ((1,2))
+
+statement ok
+SELECT * FROM emp WHERE empid = ANY (VALUES (CAST(2 as BIGINT)), (CAST(1 as
SMALLINT)))
+
+statement ok
+SELECT (VALUES (CAST(2 as BIGINT)))
+
+statement ok
+SELECT (VALUES ROW(CAST(2 as BIGINT)))
+
+statement ok
+SELECT (VALUES ROW(2))
+
+skipif ignite3
+# https://issues.apache.org/jira/browse/IGNITE-22613: Sql. Scalar subquery
that returns more than one column produces unexpected validation error
+statement error: Scalar subquery returns more than 1 column
+SELECT (VALUES ROW(2, 4))
+
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+statement error: ROW datatype is not supported
+SELECT (VALUES ROW(ROW(2)))
+
+# https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add support for row
data type.
+statement error: ROW datatype is not supported
+SELECT * FROM (SELECT (1, true) as a) as t(x)
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
index db4340c866..54b4bc193c 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/prepare/IgniteSqlValidator.java
@@ -29,6 +29,7 @@ import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
import java.math.BigDecimal;
import java.util.AbstractList;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
@@ -528,54 +529,6 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
super.validateAggregateParams(aggCall, filter, null, orderList, scope);
}
- /** {@inheritDoc} */
- @Override
- public void validateCall(
- SqlCall call,
- SqlValidatorScope scope
- ) {
- super.validateCall(call, scope);
-
- SqlOperator operator = call.getOperator();
-
- // IgniteCustomType:
- // Since custom data types use ANY that is a catch all type for type
checkers,
- // if a function is called with custom data type argument does not
belong to CUSTOM_TYPE_FUNCTIONS,
- // then this should be considered a validation error.
-
- if (call.getOperandList().isEmpty()
- || !(operator instanceof SqlFunction)
- ||
IgniteSqlOperatorTable.CUSTOM_TYPE_FUNCTIONS.contains(operator)) {
- return;
- }
-
- for (SqlNode node : call.getOperandList()) {
- RelDataType type = getValidatedNodeTypeIfKnown(node);
- // Argument type is not known yet (alias) or it is not a custom
data type.
- if ((!(type instanceof IgniteCustomType))) {
- continue;
- }
-
- String name = call.getOperator().getName();
-
- // Call to getAllowedSignatures throws NPE, if operandTypeChecker
is null.
- if (operator.getOperandTypeChecker() != null) {
- // If signatures are available, then return:
- // Cannot apply 'F' to arguments of type 'F(<ARG_TYPE>)'.
Supported form(s): 'F(<TYPE>)'
- String allowedSignatures = operator.getAllowedSignatures();
- throw newValidationError(call,
- RESOURCE.canNotApplyOp2Type(name,
- call.getCallSignature(this, scope),
- allowedSignatures));
- } else {
- // Otherwise return an error w/o supported forms:
- // Cannot apply 'F' to arguments of type 'F(<ARG_TYPE>)'
- throw newValidationError(call,
IgniteResource.INSTANCE.canNotApplyOp2Type(name,
- call.getCallSignature(this, scope)));
- }
- }
- }
-
/** {@inheritDoc} */
@Override
public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) {
@@ -663,6 +616,10 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
RelDataType firstType;
RelDataType returnType = super.deriveType(scope, ret);
+ if (returnType.isStruct()) {
+ throw newValidationError(expr,
IgniteResource.INSTANCE.dataTypeIsNotSupported(returnType.getSqlTypeName().getName()));
+ }
+
if (first instanceof SqlDynamicParam) {
SqlDynamicParam dynamicParam = (SqlDynamicParam) first;
firstType = deriveDynamicParamType(dynamicParam);
@@ -923,6 +880,126 @@ public class IgniteSqlValidator extends SqlValidatorImpl {
return Commons.implicitPkEnabled() &&
Commons.IMPLICIT_PK_COL_NAME.equals(alias);
}
+ // We use these scopes to filter out valid usages of a ROW operator.
+ private final ArrayDeque<CallScope> callScopes = new ArrayDeque<>();
+
+ /** {@inheritDoc} */
+ @Override
+ protected void validateValues(SqlCall node, RelDataType targetRowType,
SqlValidatorScope scope) {
+ // TODO: https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add
support for row data type.
+ // ROW operator is used in VALUES (row), (row1)
+ callScopes.push(CallScope.VALUES);
+ try {
+ super.validateValues(node, targetRowType, scope);
+ } finally {
+ callScopes.pop();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void validateGroupClause(SqlSelect select) {
+ // Calcite uses the ROW operator in the GROUP BY clause in the
following cases:
+ // - GROUP BY GROUPING SET ((a, b), (c, d))
+ // - GROUP BY (a, b) (but GROUP BY a, b does not use the ROW operator)
+ //
+ // We need to make sure that the validator won't reject such clauses.
+ SqlNodeList group = select.getGroup() == null ? SqlNodeList.EMPTY :
select.getGroup();
+ boolean rowInGroupScope = false;
+
+ for (SqlNode node : group) {
+ if (node.getKind() == SqlKind.GROUPING_SETS || node.getKind() ==
SqlKind.ROW) {
+ rowInGroupScope = true;
+ break;
+ }
+ }
+
+ if (!rowInGroupScope) {
+ super.validateGroupClause(select);
+ } else {
+ callScopes.push(CallScope.GROUP);
+
+ try {
+ super.validateGroupClause(select);
+ } finally {
+ callScopes.pop();
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void validateCall(SqlCall call, SqlValidatorScope scope) {
+ CallScope callScope = callScopes.peek();
+ boolean validatingRowOperator = call.getOperator() ==
SqlStdOperatorTable.ROW;
+ boolean insideValues = callScope == CallScope.VALUES;
+ boolean insideGroupClause = callScope == CallScope.GROUP;
+ boolean valuesCall = call.getOperator() == SqlStdOperatorTable.VALUES;
+
+ if (validatingRowOperator && !(insideValues || insideGroupClause)) {
+ throw newValidationError(call,
IgniteResource.INSTANCE.dataTypeIsNotSupported(call.getOperator().getName()));
+ }
+
+ if (valuesCall) {
+ // VALUES in the WHERE clause in VALUES operator, which is not
validated via validateValues method.
+ callScopes.push(CallScope.VALUES);
+ } else if (insideGroupClause) {
+ // Allow GROUPING SET ( (a,b), (c, d) ) and GROUP BY (a, b)
+ callScopes.push(CallScope.GROUP);
+ } else {
+ callScopes.push(CallScope.OTHER);
+ }
+
+ try {
+ super.validateCall(call, scope);
+
+ checkCallsWithCustomTypes(call, scope);
+ } finally {
+ callScopes.pop();
+ }
+ }
+
+ private void checkCallsWithCustomTypes(SqlCall call, SqlValidatorScope
scope) {
+ SqlOperator operator = call.getOperator();
+
+ // IgniteCustomType:
+ // Since custom data types use ANY that is a catch all type for type
checkers,
+ // if a function is called with custom data type argument does not
belong to CUSTOM_TYPE_FUNCTIONS,
+ // then this should be considered a validation error.
+
+ if (call.getOperandList().isEmpty()
+ || !(operator instanceof SqlFunction)
+ ||
IgniteSqlOperatorTable.CUSTOM_TYPE_FUNCTIONS.contains(operator)) {
+ return;
+ }
+
+ for (SqlNode node : call.getOperandList()) {
+ RelDataType type = getValidatedNodeTypeIfKnown(node);
+ // Argument type is not known yet (alias) or it is not a custom
data type.
+ if ((!(type instanceof IgniteCustomType))) {
+ continue;
+ }
+
+ String name = call.getOperator().getName();
+
+ // Call to getAllowedSignatures throws NPE, if operandTypeChecker
is null.
+ if (operator.getOperandTypeChecker() != null) {
+ // If signatures are available, then return:
+ // Cannot apply 'F' to arguments of type 'F(<ARG_TYPE>)'.
Supported form(s): 'F(<TYPE>)'
+ String allowedSignatures = operator.getAllowedSignatures();
+ throw newValidationError(call,
+ RESOURCE.canNotApplyOp2Type(name,
+ call.getCallSignature(this, scope),
+ allowedSignatures));
+ } else {
+ // Otherwise return an error w/o supported forms:
+ // Cannot apply 'F' to arguments of type 'F(<ARG_TYPE>)'
+ throw newValidationError(call,
IgniteResource.INSTANCE.canNotApplyOp2Type(name,
+ call.getCallSignature(this, scope)));
+ }
+ }
+ }
+
/** {@inheritDoc} */
@Override
protected void inferUnknownTypes(RelDataType inferredType,
SqlValidatorScope scope, SqlNode node) {
@@ -1163,4 +1240,14 @@ public class IgniteSqlValidator extends SqlValidatorImpl
{
this.hasValue = false;
}
}
+
+ /**
+ * Scope to distinguish between different usages of the ROW operator.
+ */
+ // TODO: https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add
support for row data type. Remove after row type is supported.
+ private enum CallScope {
+ VALUES,
+ GROUP,
+ OTHER
+ }
}
diff --git
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
index ea95ab312f..774b20ee55 100644
---
a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
+++
b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteResource.java
@@ -82,6 +82,9 @@ public interface IgniteResource {
@Resources.BaseMessage("CHAR datatype is not supported in table")
Resources.ExInst<SqlValidatorException>
charDataTypeIsNotSupportedInTable();
+ @BaseMessage("{0} datatype is not supported'")
+ ExInst<SqlValidatorException> dataTypeIsNotSupported(String a0);
+
@Resources.BaseMessage("Length for type {0} must be at least 1")
Resources.ExInst<SqlValidatorException> invalidStringLength(String
typeName);
diff --git
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
index 158a186f8f..1376a6b8fa 100644
---
a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
+++
b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/DynamicParametersTest.java
@@ -131,16 +131,16 @@ public class DynamicParametersTest extends
AbstractPlannerTest {
sql("SELECT ? IN ('1', 2)", 2)
.parameterTypes(nullable(NativeTypes.INT32))
- .project("OR(=(?0, 1), =(?0, 2))"),
-
- sql("SELECT (?,?) IN ((1,2))", 1, 2)
- .parameterTypes(nullable(NativeTypes.INT32),
nullable(NativeTypes.INT32))
- .project("AND(=(?0, 1), =(?1, 2))"),
-
- sql("SELECT (?,?) IN ((1,2))", "1",
"2").fails(requireExplicitCast),
- sql("SELECT (?,?) IN (('1', 2))", 1,
"2").fails(requireExplicitCast),
- sql("SELECT (?,?) IN ((1, '2'))", "1",
"2").fails(requireExplicitCast)
+ .project("OR(=(?0, 1), =(?0, 2))")
);
+ // TODO https://issues.apache.org/jira/browse/IGNITE-22084: Sql. Add
support for row data type.
+ // sql("SELECT (?,?) IN ((1,2))", 1, 2)
+ // .parameterTypes(nullable(NativeTypes.INT32),
nullable(NativeTypes.INT32))
+ // .project("AND(=(?0, 1), =(?1, 2))"),
+
+ // sql("SELECT (?,?) IN ((1,2))", "1", "2").fails(requireExplicitCast),
+ // sql("SELECT (?,?) IN (('1', 2))", 1,
"2").fails(requireExplicitCast),
+ // sql("SELECT (?,?) IN ((1, '2'))", "1",
"2").fails(requireExplicitCast)
}
/** CASE expression. */