This is an automated email from the ASF dual-hosted git repository. alexpl pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push: new 82cbc4c4913 IGNITE-16836 SQL Calcite: Fix SQL functions operand type inference - Fixes #9969. 82cbc4c4913 is described below commit 82cbc4c4913e0ca7c0dcba01b11e50f199c65de8 Author: Aleksey Plekhanov <plehanov.a...@gmail.com> AuthorDate: Thu Apr 14 16:03:49 2022 +0300 IGNITE-16836 SQL Calcite: Fix SQL functions operand type inference - Fixes #9969. Signed-off-by: Aleksey Plekhanov <plehanov.a...@gmail.com> --- .../query/calcite/CalciteQueryProcessor.java | 2 +- .../query/calcite/prepare/IgniteSqlValidator.java | 62 +++++++ .../query/calcite/type/IgniteTypeFactory.java | 10 ++ .../calcite/{ => integration}/FunctionsTest.java | 181 +++++++++------------ .../UserDefinedFunctionsIntegrationTest.java | 1 + .../ignite/testsuites/IntegrationTestSuite.java | 2 +- 6 files changed, 153 insertions(+), 105 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java index 5450a62b842..538fdb4ff60 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/CalciteQueryProcessor.java @@ -430,7 +430,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query try { return executionSvc.executePlan(qry, plan.apply(qry)); } - catch (Exception e) { + catch (Throwable e) { boolean isCanceled = qry.isCancelled(); if (qrys != null) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java index c77d463c56e..5c3ccb294c1 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteSqlValidator.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.query.calcite.prepare; import java.math.BigDecimal; +import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; @@ -31,6 +32,7 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.sql.JoinConditionType; import org.apache.calcite.sql.SqlAggFunction; import org.apache.calcite.sql.SqlCall; +import org.apache.calcite.sql.SqlCallBinding; import org.apache.calcite.sql.SqlDelete; import org.apache.calcite.sql.SqlDynamicParam; import org.apache.calcite.sql.SqlIdentifier; @@ -47,6 +49,10 @@ import org.apache.calcite.sql.SqlUpdate; import org.apache.calcite.sql.SqlUtil; import org.apache.calcite.sql.dialect.CalciteSqlDialect; import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.sql.type.FamilyOperandTypeChecker; +import org.apache.calcite.sql.type.SqlOperandTypeChecker; +import org.apache.calcite.sql.type.SqlOperandTypeInference; +import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.validate.SelectScope; import org.apache.calcite.sql.validate.SqlValidator; @@ -480,4 +486,60 @@ public class IgniteSqlValidator extends SqlValidatorImpl { return QueryUtils.KEY_FIELD_NAME.equalsIgnoreCase(alias) || QueryUtils.VAL_FIELD_NAME.equalsIgnoreCase(alias); } + + /** {@inheritDoc} */ + @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { + if (node instanceof SqlDynamicParam && inferredType.equals(unknownType)) { + // Infer type of dynamic parameters of unknown type as OTHER. + // Parameter will be converted from Object class to required class in runtime. + // Such an approach helps to bypass some cases where parameter types can never be inferred (for example, + // in expression "CASE WHEN ... THEN ? ELSE ? END"), but also has new issues: if SQL function's method + // has overloads, it's not possible to find correct unique method to call, so random method will be choosen. + // For such functions operand type inference should be implemented to find the correct method + // (see https://issues.apache.org/jira/browse/CALCITE-4347). + setValidatedNodeType(node, typeFactory().createCustomType(Object.class)); + } + else if (node instanceof SqlCall) { + final SqlValidatorScope newScope = scopes.get(node); + + if (newScope != null) + scope = newScope; + + final SqlCall call = (SqlCall)node; + final SqlOperandTypeInference operandTypeInference = call.getOperator().getOperandTypeInference(); + final SqlOperandTypeChecker operandTypeChecker = call.getOperator().getOperandTypeChecker(); + final SqlCallBinding callBinding = new SqlCallBinding(this, scope, call); + final List<SqlNode> operands = callBinding.operands(); + final RelDataType[] operandTypes = new RelDataType[operands.size()]; + + Arrays.fill(operandTypes, unknownType); + + if (operandTypeInference != null) + operandTypeInference.inferOperandTypes(callBinding, inferredType, operandTypes); + else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { + // Infer operand types from checker for dynamic parameters if it's possible. + FamilyOperandTypeChecker checker = (FamilyOperandTypeChecker)operandTypeChecker; + + for (int i = 0; i < checker.getOperandCountRange().getMax(); i++) { + if (i >= operandTypes.length) + break; + + SqlTypeFamily family = checker.getOperandSqlTypeFamily(i); + RelDataType type = family.getDefaultConcreteType(typeFactory()); + + if (type != null && operands.get(i) instanceof SqlDynamicParam) + operandTypes[i] = type; + } + } + + for (int i = 0; i < operands.size(); ++i) { + final SqlNode operand = operands.get(i); + + if (operand != null) + inferUnknownTypes(operandTypes[i], scope, operand); + } + } + else + super.inferUnknownTypes(inferredType, scope, node); + } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeFactory.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeFactory.java index 2ede484e599..d5b4a62d4d2 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeFactory.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeFactory.java @@ -55,6 +55,9 @@ public class IgniteTypeFactory extends JavaTypeFactoryImpl { private static final SqlIntervalQualifier INTERVAL_QUALIFIER_DAY_TIME = new SqlIntervalQualifier(TimeUnit.DAY, TimeUnit.SECOND, SqlParserPos.ZERO); + /** */ + private final RelDataType unknownType; + /** */ private final Charset charset; @@ -77,6 +80,8 @@ public class IgniteTypeFactory extends JavaTypeFactoryImpl { // If JVM default charset is not supported by Calcite - use UTF-8. charset = StandardCharsets.UTF_8; } + + unknownType = createUnknownType(); } /** {@inheritDoc} */ @@ -294,6 +299,11 @@ public class IgniteTypeFactory extends JavaTypeFactoryImpl { /** {@inheritDoc} */ @Override public RelDataType createTypeWithNullability(RelDataType type, boolean nullable) { + // TODO workaround for https://issues.apache.org/jira/browse/CALCITE-4872 + // Remove this after update to Calcite 1.30. + if (unknownType.equals(type)) + return type; + if (type instanceof IgniteCustomType && type.isNullable() != nullable) return createCustomType(((IgniteCustomType)type).storageType(), nullable); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java similarity index 52% rename from modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java rename to modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java index 3508d6d2afc..7d27d4a156e 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/FunctionsTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java @@ -15,65 +15,50 @@ * limitations under the License. */ -package org.apache.ignite.internal.processors.query.calcite; +package org.apache.ignite.internal.processors.query.calcite.integration; +import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.util.List; import java.util.function.LongFunction; import org.apache.calcite.sql.validate.SqlValidatorException; -import org.apache.ignite.Ignite; import org.apache.ignite.IgniteCache; import org.apache.ignite.calcite.CalciteQueryEngineConfiguration; import org.apache.ignite.configuration.CacheConfiguration; -import org.apache.ignite.internal.IgniteEx; import org.apache.ignite.internal.processors.query.IgniteSQLException; -import org.apache.ignite.internal.processors.query.QueryEngine; -import org.apache.ignite.internal.processors.query.calcite.util.Commons; import org.apache.ignite.internal.util.typedef.internal.U; -import org.apache.ignite.testframework.GridTestUtils; -import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest; import org.junit.Test; /** * Test Ignite SQL functions. */ -public class FunctionsTest extends GridCommonAbstractTest { - /** */ - private static QueryEngine qryEngine; - +public class FunctionsTest extends AbstractBasicIntegrationTest { /** */ private static final Object[] NULL_RESULT = new Object[] { null }; - /** {@inheritDoc} */ - @Override protected void beforeTestsStarted() throws Exception { - Ignite grid = startGridsMultiThreaded(3); - - qryEngine = Commons.lookupComponent(((IgniteEx)grid).context(), QueryEngine.class); - } - /** */ @Test public void testTimestampDiffWithFractionsOfSecond() { - checkQuery("SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:28.000', " + + assertQuery("SELECT TIMESTAMPDIFF(MICROSECOND, TIMESTAMP '2022-02-01 10:30:28.000', " + "TIMESTAMP '2022-02-01 10:30:28.128')").returns(128000).check(); - checkQuery("SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:28.000', " + + assertQuery("SELECT TIMESTAMPDIFF(NANOSECOND, TIMESTAMP '2022-02-01 10:30:28.000', " + "TIMESTAMP '2022-02-01 10:30:28.128')").returns(128000000L).check(); } /** */ @Test public void testLength() { - checkQuery("SELECT LENGTH('TEST')").returns(4).check(); - checkQuery("SELECT LENGTH(NULL)").returns(NULL_RESULT).check(); + assertQuery("SELECT LENGTH('TEST')").returns(4).check(); + assertQuery("SELECT LENGTH(NULL)").returns(NULL_RESULT).check(); } /** */ @Test public void testQueryEngine() { - checkQuery("SELECT QUERY_ENGINE()").returns(CalciteQueryEngineConfiguration.ENGINE_NAME).check(); + assertQuery("SELECT QUERY_ENGINE()").returns(CalciteQueryEngineConfiguration.ENGINE_NAME).check(); } /** */ @@ -94,7 +79,7 @@ public class FunctionsTest extends GridCommonAbstractTest { while (true) { long tsBeg = U.currentTimeMillis(); - List<List<?>> res = qryEngine.query(null, "PUBLIC", sql).get(0).getAll(); + List<List<?>> res = sql(sql); long tsEnd = U.currentTimeMillis(); @@ -120,45 +105,43 @@ public class FunctionsTest extends GridCommonAbstractTest { /** */ @Test public void testReplace() { - checkQuery("SELECT REPLACE('12341234', '1', '55')").returns("5523455234").check(); - checkQuery("SELECT REPLACE(NULL, '1', '5')").returns(NULL_RESULT).check(); - checkQuery("SELECT REPLACE('1', NULL, '5')").returns(NULL_RESULT).check(); - checkQuery("SELECT REPLACE('11', '1', NULL)").returns(NULL_RESULT).check(); - checkQuery("SELECT REPLACE('11', '1', '')").returns("").check(); + assertQuery("SELECT REPLACE('12341234', '1', '55')").returns("5523455234").check(); + assertQuery("SELECT REPLACE(NULL, '1', '5')").returns(NULL_RESULT).check(); + assertQuery("SELECT REPLACE('1', NULL, '5')").returns(NULL_RESULT).check(); + assertQuery("SELECT REPLACE('11', '1', NULL)").returns(NULL_RESULT).check(); + assertQuery("SELECT REPLACE('11', '1', '')").returns("").check(); } /** */ @Test public void testRange() { - checkQuery("SELECT * FROM table(system_range(1, 4))") + assertQuery("SELECT * FROM table(system_range(1, 4))") .returns(1L) .returns(2L) .returns(3L) .returns(4L) .check(); - checkQuery("SELECT * FROM table(system_range(1, 4, 2))") + assertQuery("SELECT * FROM table(system_range(1, 4, 2))") .returns(1L) .returns(3L) .check(); - checkQuery("SELECT * FROM table(system_range(4, 1, -1))") + assertQuery("SELECT * FROM table(system_range(4, 1, -1))") .returns(4L) .returns(3L) .returns(2L) .returns(1L) .check(); - checkQuery("SELECT * FROM table(system_range(4, 1, -2))") + assertQuery("SELECT * FROM table(system_range(4, 1, -2))") .returns(4L) .returns(2L) .check(); - assertEquals(0, qryEngine.query(null, "PUBLIC", - "SELECT * FROM table(system_range(4, 1))").get(0).getAll().size()); + assertEquals(0, sql("SELECT * FROM table(system_range(4, 1))").size()); - assertEquals(0, qryEngine.query(null, "PUBLIC", - "SELECT * FROM table(system_range(null, 1))").get(0).getAll().size()); + assertEquals(0, sql("SELECT * FROM table(system_range(null, 1))").size()); assertThrows("SELECT * FROM table(system_range(1, 1, 0))", IllegalArgumentException.class, "Increment can't be 0"); @@ -182,7 +165,7 @@ public class FunctionsTest extends GridCommonAbstractTest { awaitPartitionMapExchange(); // Correlated INNER join. - checkQuery("SELECT t._val FROM \"test\".Integer t WHERE t._val < 5 AND " + + assertQuery("SELECT t._val FROM \"test\".Integer t WHERE t._val < 5 AND " + "t._key in (SELECT x FROM table(system_range(t._val, t._val))) ") .returns(0) .returns(1) @@ -192,25 +175,24 @@ public class FunctionsTest extends GridCommonAbstractTest { .check(); // Correlated LEFT joins. - checkQuery("SELECT t._val FROM \"test\".Integer t WHERE t._val < 5 AND " + + assertQuery("SELECT t._val FROM \"test\".Integer t WHERE t._val < 5 AND " + "EXISTS (SELECT x FROM table(system_range(t._val, t._val)) WHERE mod(x, 2) = 0) ") .returns(0) .returns(2) .returns(4) .check(); - checkQuery("SELECT t._val FROM \"test\".Integer t WHERE t._val < 5 AND " + + assertQuery("SELECT t._val FROM \"test\".Integer t WHERE t._val < 5 AND " + "NOT EXISTS (SELECT x FROM table(system_range(t._val, t._val)) WHERE mod(x, 2) = 0) ") .returns(1) .returns(3) .check(); - assertEquals(0, qryEngine.query(null, "PUBLIC", - "SELECT t._val FROM \"test\".Integer t WHERE " + - "EXISTS (SELECT x FROM table(system_range(t._val, null))) ").get(0).getAll().size()); + assertEquals(0, sql("SELECT t._val FROM \"test\".Integer t WHERE " + + "EXISTS (SELECT x FROM table(system_range(t._val, null))) ").size()); // Non-correlated join. - checkQuery("SELECT t._val FROM \"test\".Integer t JOIN table(system_range(1, 50)) as r ON t._key = r.x " + + assertQuery("SELECT t._val FROM \"test\".Integer t JOIN table(system_range(1, 50)) as r ON t._key = r.x " + "WHERE mod(r.x, 10) = 0") .returns(10) .returns(20) @@ -223,45 +205,45 @@ public class FunctionsTest extends GridCommonAbstractTest { /** */ @Test public void testPercentRemainder() { - checkQuery("SELECT 3 % 2").returns(1).check(); - checkQuery("SELECT 4 % 2").returns(0).check(); - checkQuery("SELECT NULL % 2").returns(NULL_RESULT).check(); - checkQuery("SELECT 3 % NULL::int").returns(NULL_RESULT).check(); - checkQuery("SELECT 3 % NULL").returns(NULL_RESULT).check(); + assertQuery("SELECT 3 % 2").returns(1).check(); + assertQuery("SELECT 4 % 2").returns(0).check(); + assertQuery("SELECT NULL % 2").returns(NULL_RESULT).check(); + assertQuery("SELECT 3 % NULL::int").returns(NULL_RESULT).check(); + assertQuery("SELECT 3 % NULL").returns(NULL_RESULT).check(); } /** */ @Test public void testNullFunctionArguments() { // Don't infer result data type from arguments (result is always INTEGER_NULLABLE). - checkQuery("SELECT ASCII(NULL)").returns(NULL_RESULT).check(); + assertQuery("SELECT ASCII(NULL)").returns(NULL_RESULT).check(); // Inferring result data type from first STRING argument. - checkQuery("SELECT REPLACE(NULL, '1', '2')").returns(NULL_RESULT).check(); + assertQuery("SELECT REPLACE(NULL, '1', '2')").returns(NULL_RESULT).check(); // Inferring result data type from both arguments. - checkQuery("SELECT MOD(1, null)").returns(NULL_RESULT).check(); + assertQuery("SELECT MOD(1, null)").returns(NULL_RESULT).check(); // Inferring result data type from first NUMERIC argument. - checkQuery("SELECT TRUNCATE(NULL, 0)").returns(NULL_RESULT).check(); + assertQuery("SELECT TRUNCATE(NULL, 0)").returns(NULL_RESULT).check(); // Inferring arguments data types and then inferring result data type from all arguments. - checkQuery("SELECT FALSE AND NULL").returns(false).check(); + assertQuery("SELECT FALSE AND NULL").returns(false).check(); } /** */ @Test public void testMonthnameDayname() { - checkQuery("SELECT MONTHNAME(DATE '2021-01-01')").returns("January").check(); - checkQuery("SELECT DAYNAME(DATE '2021-01-01')").returns("Friday").check(); + assertQuery("SELECT MONTHNAME(DATE '2021-01-01')").returns("January").check(); + assertQuery("SELECT DAYNAME(DATE '2021-01-01')").returns("Friday").check(); } /** */ @Test public void testTypeOf() { - checkQuery("SELECT TYPEOF(1)").returns("INTEGER").check(); - checkQuery("SELECT TYPEOF(1.1::DOUBLE)").returns("DOUBLE").check(); - checkQuery("SELECT TYPEOF(1.1::DECIMAL(3, 2))").returns("DECIMAL(3, 2)").check(); - checkQuery("SELECT TYPEOF('a')").returns("CHAR(1)").check(); - checkQuery("SELECT TYPEOF('a'::varchar(1))").returns("VARCHAR(1)").check(); - checkQuery("SELECT TYPEOF(NULL)").returns("NULL").check(); - checkQuery("SELECT TYPEOF(NULL::VARCHAR(100))").returns("VARCHAR(100)").check(); + assertQuery("SELECT TYPEOF(1)").returns("INTEGER").check(); + assertQuery("SELECT TYPEOF(1.1::DOUBLE)").returns("DOUBLE").check(); + assertQuery("SELECT TYPEOF(1.1::DECIMAL(3, 2))").returns("DECIMAL(3, 2)").check(); + assertQuery("SELECT TYPEOF('a')").returns("CHAR(1)").check(); + assertQuery("SELECT TYPEOF('a'::varchar(1))").returns("VARCHAR(1)").check(); + assertQuery("SELECT TYPEOF(NULL)").returns("NULL").check(); + assertQuery("SELECT TYPEOF(NULL::VARCHAR(100))").returns("VARCHAR(100)").check(); assertThrows("SELECT TYPEOF()", SqlValidatorException.class, "Invalid number of arguments"); assertThrows("SELECT TYPEOF(1, 2)", SqlValidatorException.class, "Invalid number of arguments"); } @@ -269,49 +251,42 @@ public class FunctionsTest extends GridCommonAbstractTest { /** */ @Test public void testRegex() { - checkQuery("SELECT 'abcd' ~ 'ab[cd]'").returns(true).check(); - checkQuery("SELECT 'abcd' ~ 'ab[cd]$'").returns(false).check(); - checkQuery("SELECT 'abcd' ~ 'ab[CD]'").returns(false).check(); - checkQuery("SELECT 'abcd' ~* 'ab[cd]'").returns(true).check(); - checkQuery("SELECT 'abcd' ~* 'ab[cd]$'").returns(false).check(); - checkQuery("SELECT 'abcd' ~* 'ab[CD]'").returns(true).check(); - checkQuery("SELECT 'abcd' !~ 'ab[cd]'").returns(false).check(); - checkQuery("SELECT 'abcd' !~ 'ab[cd]$'").returns(true).check(); - checkQuery("SELECT 'abcd' !~ 'ab[CD]'").returns(true).check(); - checkQuery("SELECT 'abcd' !~* 'ab[cd]'").returns(false).check(); - checkQuery("SELECT 'abcd' !~* 'ab[cd]$'").returns(true).check(); - checkQuery("SELECT 'abcd' !~* 'ab[CD]'").returns(false).check(); - checkQuery("SELECT null ~ 'ab[cd]'").returns(NULL_RESULT).check(); - checkQuery("SELECT 'abcd' ~ null").returns(NULL_RESULT).check(); - checkQuery("SELECT null ~ null").returns(NULL_RESULT).check(); - checkQuery("SELECT null ~* 'ab[cd]'").returns(NULL_RESULT).check(); - checkQuery("SELECT 'abcd' ~* null").returns(NULL_RESULT).check(); - checkQuery("SELECT null ~* null").returns(NULL_RESULT).check(); - checkQuery("SELECT null !~ 'ab[cd]'").returns(NULL_RESULT).check(); - checkQuery("SELECT 'abcd' !~ null").returns(NULL_RESULT).check(); - checkQuery("SELECT null !~ null").returns(NULL_RESULT).check(); - checkQuery("SELECT null !~* 'ab[cd]'").returns(NULL_RESULT).check(); - checkQuery("SELECT 'abcd' !~* null").returns(NULL_RESULT).check(); - checkQuery("SELECT null !~* null").returns(NULL_RESULT).check(); + assertQuery("SELECT 'abcd' ~ 'ab[cd]'").returns(true).check(); + assertQuery("SELECT 'abcd' ~ 'ab[cd]$'").returns(false).check(); + assertQuery("SELECT 'abcd' ~ 'ab[CD]'").returns(false).check(); + assertQuery("SELECT 'abcd' ~* 'ab[cd]'").returns(true).check(); + assertQuery("SELECT 'abcd' ~* 'ab[cd]$'").returns(false).check(); + assertQuery("SELECT 'abcd' ~* 'ab[CD]'").returns(true).check(); + assertQuery("SELECT 'abcd' !~ 'ab[cd]'").returns(false).check(); + assertQuery("SELECT 'abcd' !~ 'ab[cd]$'").returns(true).check(); + assertQuery("SELECT 'abcd' !~ 'ab[CD]'").returns(true).check(); + assertQuery("SELECT 'abcd' !~* 'ab[cd]'").returns(false).check(); + assertQuery("SELECT 'abcd' !~* 'ab[cd]$'").returns(true).check(); + assertQuery("SELECT 'abcd' !~* 'ab[CD]'").returns(false).check(); + assertQuery("SELECT null ~ 'ab[cd]'").returns(NULL_RESULT).check(); + assertQuery("SELECT 'abcd' ~ null").returns(NULL_RESULT).check(); + assertQuery("SELECT null ~ null").returns(NULL_RESULT).check(); + assertQuery("SELECT null ~* 'ab[cd]'").returns(NULL_RESULT).check(); + assertQuery("SELECT 'abcd' ~* null").returns(NULL_RESULT).check(); + assertQuery("SELECT null ~* null").returns(NULL_RESULT).check(); + assertQuery("SELECT null !~ 'ab[cd]'").returns(NULL_RESULT).check(); + assertQuery("SELECT 'abcd' !~ null").returns(NULL_RESULT).check(); + assertQuery("SELECT null !~ null").returns(NULL_RESULT).check(); + assertQuery("SELECT null !~* 'ab[cd]'").returns(NULL_RESULT).check(); + assertQuery("SELECT 'abcd' !~* null").returns(NULL_RESULT).check(); + assertQuery("SELECT null !~* null").returns(NULL_RESULT).check(); assertThrows("SELECT 'abcd' ~ '[a-z'", IgniteSQLException.class, null); } /** */ - private void assertThrows(String qry, Class<? extends Throwable> cls, String msg) { - GridTestUtils.assertThrowsAnyCause( - log, - () -> qryEngine.query(null, "PUBLIC", qry).get(0).getAll(), - cls, - msg - ); - } - - /** */ - private QueryChecker checkQuery(String qry) { - return new QueryChecker(qry) { - @Override protected QueryEngine getEngine() { - return qryEngine; - } - }; + @Test + public void testDynamicParameterTypesInference() { + assertQuery("SELECT lower(?)").withParams("ASD").returns("asd").check(); + assertQuery("SELECT ? % ?").withParams(11, 10).returns(BigDecimal.valueOf(1)).check(); + assertQuery("SELECT sqrt(?)").withParams(4d).returns(2d).check(); + assertQuery("SELECT last_day(?)").withParams(Date.valueOf("2022-01-01")) + .returns(Date.valueOf("2022-01-31")).check(); + assertQuery("SELECT ?").withParams("asd").returns("asd").check(); + assertQuery("SELECT coalesce(?, ?)").withParams("a", 10).returns("a").check(); } } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UserDefinedFunctionsIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UserDefinedFunctionsIntegrationTest.java index 17cbd3f8fef..2f5796393cf 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UserDefinedFunctionsIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/UserDefinedFunctionsIntegrationTest.java @@ -80,6 +80,7 @@ public class UserDefinedFunctionsIntegrationTest extends AbstractBasicIntegratio assertQuery("SELECT echo(name) FROM emp3").returns("Igor3").returns("Roman3").check(); assertQuery("SELECT sq(salary) FROM EMP2_SCHEMA.emp2").returns(100d).returns(400d).check(); assertQuery("SELECT sq(salary) FROM \"emp1\".emp1").returns(1d).returns(4d).check(); + assertQuery("SELECT echo(?)").withParams("test").returns("test").check(); // Check type inference. assertThrows("SELECT add(1, 2)"); assertThrows("SELECT mul(1, 2)"); assertThrows("SELECT EMP2_SCHEMA.mul(1, 2)"); diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java index bde32f8a45e..cd24f539df8 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java +++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IntegrationTestSuite.java @@ -20,7 +20,6 @@ package org.apache.ignite.testsuites; import org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessorTest; import org.apache.ignite.internal.processors.query.calcite.CancelTest; import org.apache.ignite.internal.processors.query.calcite.DateTimeTest; -import org.apache.ignite.internal.processors.query.calcite.FunctionsTest; import org.apache.ignite.internal.processors.query.calcite.LimitOffsetTest; import org.apache.ignite.internal.processors.query.calcite.SqlFieldsQueryUsageTest; import org.apache.ignite.internal.processors.query.calcite.UnstableTopologyTest; @@ -29,6 +28,7 @@ import org.apache.ignite.internal.processors.query.calcite.integration.CalciteBa import org.apache.ignite.internal.processors.query.calcite.integration.CalciteErrorHandlilngIntegrationTest; import org.apache.ignite.internal.processors.query.calcite.integration.CorrelatesIntegrationTest; import org.apache.ignite.internal.processors.query.calcite.integration.DataTypesTest; +import org.apache.ignite.internal.processors.query.calcite.integration.FunctionsTest; import org.apache.ignite.internal.processors.query.calcite.integration.HashSpoolIntegrationTest; import org.apache.ignite.internal.processors.query.calcite.integration.IndexDdlIntegrationTest; import org.apache.ignite.internal.processors.query.calcite.integration.IndexRebuildIntegrationTest;