From 91d6e244c28aebfccd4c214b3ca99af1cc30427d Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Mon, 7 Oct 2024 20:17:12 +0300 Subject: [PATCH 01/43] raw --- .../calcite/exec/exp/IgniteSqlFunctions.java | 85 ++++--- .../calcite/integration/DataTypesTest.java | 220 ++++++++++++++++++ 2 files changed, 278 insertions(+), 27 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java index 5c6130af64e30..42e7a9a51b633 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java @@ -42,6 +42,9 @@ * Ignite SQL functions. */ public class IgniteSqlFunctions { + /** */ + public static final String NUMERIC_FIELD_OVERFLOW_ERROR = "Numeric field overflow"; + /** * Default constructor. */ @@ -64,46 +67,34 @@ public static String toString(BigDecimal x) { return x == null ? null : x.toPlainString(); } - /** */ - private static BigDecimal setScale(int precision, int scale, BigDecimal decimal) { - return precision == IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL) - ? decimal : decimal.setScale(scale, RoundingMode.HALF_UP); - } - /** CAST(DOUBLE AS DECIMAL). */ public static BigDecimal toBigDecimal(double val, int precision, int scale) { - BigDecimal decimal = BigDecimal.valueOf(val); - return setScale(precision, scale, decimal); + return toBigDecimal((Double) val, precision, scale); } /** CAST(FLOAT AS DECIMAL). */ public static BigDecimal toBigDecimal(float val, int precision, int scale) { - BigDecimal decimal = new BigDecimal(String.valueOf(val)); - return setScale(precision, scale, decimal); + return toBigDecimal((Float) val, precision, scale); } /** CAST(java long AS DECIMAL). */ public static BigDecimal toBigDecimal(long val, int precision, int scale) { - BigDecimal decimal = BigDecimal.valueOf(val); - return setScale(precision, scale, decimal); + return convertDecimal(BigDecimal.valueOf(val), precision, scale); } /** CAST(INT AS DECIMAL). */ public static BigDecimal toBigDecimal(int val, int precision, int scale) { - BigDecimal decimal = new BigDecimal(val); - return setScale(precision, scale, decimal); + return convertDecimal(new BigDecimal(val), precision, scale); } /** CAST(java short AS DECIMAL). */ public static BigDecimal toBigDecimal(short val, int precision, int scale) { - BigDecimal decimal = new BigDecimal(String.valueOf(val)); - return setScale(precision, scale, decimal); + return convertDecimal(new BigDecimal(val), precision, scale); } /** CAST(java byte AS DECIMAL). */ public static BigDecimal toBigDecimal(byte val, int precision, int scale) { - BigDecimal decimal = new BigDecimal(String.valueOf(val)); - return setScale(precision, scale, decimal); + return convertDecimal(new BigDecimal(val), precision, scale); } /** CAST(BOOL AS DECIMAL). */ @@ -115,21 +106,61 @@ public static BigDecimal toBigDecimal(boolean val, int precision, int scale) { public static BigDecimal toBigDecimal(String s, int precision, int scale) { if (s == null) return null; - BigDecimal decimal = new BigDecimal(s.trim()); - return setScale(precision, scale, decimal); + + return convertDecimal(new BigDecimal(s.trim()), precision, scale); + } + + /** + * Converts the given {@code BigDecimal} to a decimal with the given {@code precision} and {@code scale} + * according to SQL spec for CAST specification: General Rules, 8. + */ + public static BigDecimal convertDecimal(BigDecimal val, int precision, int scale) { + assert precision > 0 : "Invalid precision: " + precision; + + int dfltPrecision = IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL); + + if (precision == dfltPrecision) { + // This branch covers at least one known case: access to dynamic parameter from context. + // In this scenario precision = DefaultTypePrecision, because types for dynamic params + // are created by toSql(createType(param.class)). + return val; + } + + boolean nonZero = !val.unscaledValue().equals(BigInteger.ZERO); + + if (nonZero) { + if (scale > precision) + throw new IllegalArgumentException(NUMERIC_FIELD_OVERFLOW_ERROR); + else { + int curSignificantDigits = val.precision() - val.scale(); + int expectedSignificantDigits = precision - scale; + + if (curSignificantDigits > expectedSignificantDigits) + throw new IllegalArgumentException(NUMERIC_FIELD_OVERFLOW_ERROR); + + } + } + + return val.setScale(scale, RoundingMode.HALF_UP); } /** CAST(REAL AS DECIMAL). */ public static BigDecimal toBigDecimal(Number num, int precision, int scale) { if (num == null) return null; - // There are some values of "long" that cannot be represented as "double". - // Not so "int". If it isn't a long, go straight to double. - BigDecimal decimal = num instanceof BigDecimal ? ((BigDecimal)num) - : num instanceof BigInteger ? new BigDecimal((BigInteger)num) - : num instanceof Long ? new BigDecimal(num.longValue()) - : BigDecimal.valueOf(num.doubleValue()); - return setScale(precision, scale, decimal); + + BigDecimal dec; + + if (num instanceof Float || num instanceof Double) + dec = BigDecimal.valueOf(num.doubleValue()); + else if (num instanceof BigDecimal) + dec = (BigDecimal)num; + else if (num instanceof BigInteger) + dec = new BigDecimal((BigInteger)num); + else + dec = new BigDecimal(num.longValue()); + + return convertDecimal(dec, precision, scale); } /** Cast object depending on type to DECIMAL. */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index c350fc2d80ff3..0814e105894bc 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -24,15 +24,20 @@ import java.util.UUID; import java.util.stream.Collectors; import com.google.common.collect.ImmutableSet; +import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.runtime.CalciteException; import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.tools.FrameworkConfig; import org.apache.calcite.tools.Frameworks; import org.apache.ignite.IgniteCache; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.configuration.CacheConfiguration; import org.apache.ignite.internal.processors.query.IgniteSQLException; +import org.apache.ignite.internal.processors.query.calcite.exec.exp.IgniteSqlFunctions; import org.apache.ignite.internal.processors.query.calcite.hint.HintDefinition; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.util.Commons; import org.apache.ignite.internal.util.typedef.F; import org.junit.Test; @@ -573,6 +578,221 @@ public void testNumericConversion() { .check(); } + /** */ + @Test + public void testCastsOfVarcharLiterals() { + doTestCastsOfVarcharLiterals(false); + } + + /** */ + @Test + public void testCastsOfVarcharLiteralsAsDynamicparameters() { + doTestCastsOfVarcharLiterals(true); + } + + /** */ + private void doTestCastsOfVarcharLiterals(boolean dynamics) { + for (List params : varcharCasts()) { + String val = params.get(0).toString(); + RelDataType type = (RelDataType)params.get(1); + String result = params.get(2).toString(); + + if (dynamics) + assertQuery(String.format("SELECT CAST(? AS %s)", type)).withParams(val).returns(result).check(); + else + assertQuery(String.format("SELECT CAST('%s' AS %s)", val, type)).returns(result).check(); + } + } + + /** */ + private static List> varcharCasts() { + IgniteTypeFactory tf = Commons.typeFactory(); + + return Arrays.asList( + Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 3), "abc"), + Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 5), "abcde"), + Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 6), "abcde"), + Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR), "abcde"), + Arrays.asList("abcde", tf.createSqlType(SqlTypeName.CHAR), "a"), + Arrays.asList("abcde", tf.createSqlType(SqlTypeName.CHAR, 3), "abc") + ); + } + + /** */ + @Test + public void testCastsOfNumericLiterals() { + doTestCastsOfNumericLiterals(false, false); + } + + /** */ + @Test + public void testCastsOfNumericLiteralsAsNumerics() { + doTestCastsOfNumericLiterals(false, true); + } + + /** */ + @Test + public void testCastsOfNumericLiteralsAsDynamicParameters() { + doTestCastsOfNumericLiterals(true, false); + } + + /** */ + private void doTestCastsOfNumericLiterals(boolean dynamic, boolean numeric) { + assert !dynamic || !numeric; + + for (List params : decimalCastsTestParams()) { + assert params.size() == 4 : "Wrong params lenght: " + params.size(); + + RelDataType inputType = (RelDataType)params.get(0); + Object input = params.get(1); + RelDataType targetType = (RelDataType)params.get(2); + Object result = params.get(3); + + log.info("Params: inputType=" + inputType + ", inputValue=" + input + ", targetType=" + targetType + + ", expectedResult=" + result); + + if (dynamic) { + String qry = String.format("SELECT CAST(? AS %s)", targetType); + + if (result instanceof Exception) + assertThrows(qry, (Class)result.getClass(), ((Throwable)result).getMessage(), input); + else + assertQuery(qry).withParams(inputType).returns(result); + } + else { + String qry = numeric + ? String.format("SELECT CAST(%s::%s AS %s)", asLiteral(input, inputType), inputType, targetType) + : String.format("SELECT CAST(%s AS %s)", asLiteral(input, inputType), targetType); + + if (result instanceof Exception) + assertThrows(qry, (Class)result.getClass(), ((Throwable)result).getMessage()); + else + assertQuery(qry).returns(result); + } + } + } + + /** */ + private static String asLiteral(Object val, RelDataType type) { + return SqlTypeUtil.isCharacter(type) ? String.format("'%s'", val) : String.valueOf(val); + } + + /** */ + private static List> decimalCastsTestParams() { + IgniteTypeFactory tf = Commons.typeFactory(); + + RelDataType varcharType = tf.createSqlType(SqlTypeName.VARCHAR); + RelDataType tinyIntType = tf.createSqlType(SqlTypeName.TINYINT); + RelDataType smallIntType = tf.createSqlType(SqlTypeName.SMALLINT); + RelDataType integerType = tf.createSqlType(SqlTypeName.INTEGER); + RelDataType bigintType = tf.createSqlType(SqlTypeName.BIGINT); + RelDataType realType = tf.createSqlType(SqlTypeName.REAL); + RelDataType doubleType = tf.createSqlType(SqlTypeName.DOUBLE); + RelDataType decimal4Type = decimalType(4); + + Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_FIELD_OVERFLOW_ERROR); + Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); + + return Arrays.asList( + // String + Arrays.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), + Arrays.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), + Arrays.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), + Arrays.asList(varcharType, "12345", decimalType(5, 1), overflowErr), + Arrays.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), + Arrays.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), + Arrays.asList(varcharType, "100", decimalType(2, 0), overflowErr), + + // Numeric + Arrays.asList(decimal4Type, "100", decimalType(3), new BigDecimal("100")), + Arrays.asList(decimal4Type, "100", decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(decimal4Type, "100.12", decimalType(5, 1), new BigDecimal("100.1")), + Arrays.asList(decimal4Type, "100.12", decimalType(5, 0), new BigDecimal("100")), + Arrays.asList(decimal4Type, "100", decimalType(2, 0), overflowErr), + Arrays.asList(decimal4Type, "100.12", decimalType(5, 2), new BigDecimal("100.12")), + + // Tinyint + Arrays.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), + Arrays.asList(tinyIntType, (byte)100, decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(tinyIntType, (byte)100, decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(tinyIntType, (byte)100, decimalType(2, 0), overflowErr), + + // Smallint + Arrays.asList(smallIntType, (short)100, decimalType(3), new BigDecimal("100")), + Arrays.asList(smallIntType, (short)100, decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(smallIntType, (short)100, decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(smallIntType, (short)100, decimalType(2, 0), overflowErr), + + // Integer + Arrays.asList(integerType, 100, decimalType(3), new BigDecimal("100")), + Arrays.asList(integerType, 100, decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(integerType, 100, decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(integerType, 100, decimalType(2, 0), overflowErr), + + // Bigint + Arrays.asList(bigintType, 100L, decimalType(3), new BigDecimal("100")), + Arrays.asList(bigintType, 100L, decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(bigintType, 100L, decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(bigintType, 100L, decimalType(2, 0), overflowErr), + + // Real + Arrays.asList(realType, 100.0f, decimalType(3), new BigDecimal("100")), + Arrays.asList(realType, 100.0f, decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(realType, 100.0f, decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(realType, 100.0f, decimalType(2, 0), overflowErr), + Arrays.asList(realType, 0.1f, decimalType(1, 1), new BigDecimal("0.1")), + Arrays.asList(realType, 0.1f, decimalType(2, 2), new BigDecimal("0.10")), + Arrays.asList(realType, 10.12f, decimalType(2, 1), overflowErr), + Arrays.asList(realType, 0.12f, decimalType(1, 2), overflowErr), + + // Double + Arrays.asList(doubleType, 100.0d, decimalType(3), new BigDecimal("100")), + Arrays.asList(doubleType, 100.0d, decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(doubleType, 100.0d, decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(doubleType, 100.0d, decimalType(2, 0), overflowErr), + Arrays.asList(doubleType, 0.1d, decimalType(1, 1), new BigDecimal("0.1")), + Arrays.asList(doubleType, 0.1d, decimalType(2, 2), new BigDecimal("0.10")), + Arrays.asList(doubleType, 10.12d, decimalType(2, 1), overflowErr), + Arrays.asList(doubleType, 0.12d, decimalType(1, 2), overflowErr), + + // Decimal + Arrays.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), + Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(3), new BigDecimal("100")), + Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), + Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), + Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(2, 0), overflowErr), + Arrays.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), + Arrays.asList(decimalType(4, 2), new BigDecimal("10.12"), decimalType(2, 1), overflowErr), + Arrays.asList(decimalType(2, 2), new BigDecimal("0.12"), decimalType(1, 2), overflowErr), + Arrays.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) + ); + } + + /** */ + private static RelDataType decimalType(int precision, int scale) { + return Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, precision, scale); + } + + /** */ + private static RelDataType decimalType(int precision) { + return Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, precision, RelDataType.SCALE_NOT_SPECIFIED); + } + + /** */ + @Test + public void testCastNumLiterals() throws Exception { + // Cast or validation error expected. Returns: 100L. +// assertThrows("SELECT CAST('100.1' AS BIGINT)", Exception.class, "cannot cast"); + + // An overflow error expected. Returns: Infinity. + assertThrows("SELECT CAST(1e39 AS REAL)", Exception.class, "Overflow"); +// +// // An overflow error expected. Returns: Infinity. +// assertThrows("SELECT CAST(1e39 AS FLOAT)", Exception.class, "Overflow"); + } + /** */ @Test public void testFunctionArgsToNumericImplicitConversion() { From 93bf93b1aa6cb68cea5db1be0da64ff616a1b8ac Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Mon, 7 Oct 2024 20:35:33 +0300 Subject: [PATCH 02/43] raw --- .../calcite/exec/exp/IgniteRexBuilder.java | 1 + .../calcite/exec/exp/IgniteSqlFunctions.java | 87 ++++++----- .../calcite/prepare/IgniteSqlValidator.java | 82 +++++++++- .../calcite/integration/DataTypesTest.java | 147 ++++++++---------- 4 files changed, 187 insertions(+), 130 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java index e26d249437cc9..5750bbe2426e8 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java @@ -23,6 +23,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexLiteral; +import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java index 42e7a9a51b633..1e9cdcde3c918 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java @@ -45,6 +45,12 @@ public class IgniteSqlFunctions { /** */ public static final String NUMERIC_FIELD_OVERFLOW_ERROR = "Numeric field overflow"; + /** */ + private static final int DFLT_NUM_PRECISION = IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL); + + /** */ + private static final RoundingMode NUMERIC_ROUNDING_MODE = RoundingMode.HALF_UP; + /** * Default constructor. */ @@ -69,32 +75,40 @@ public static String toString(BigDecimal x) { /** CAST(DOUBLE AS DECIMAL). */ public static BigDecimal toBigDecimal(double val, int precision, int scale) { - return toBigDecimal((Double) val, precision, scale); + return removeDefaultScale(precision, scale, toBigDecimal(BigDecimal.valueOf(val), precision, scale)); } /** CAST(FLOAT AS DECIMAL). */ public static BigDecimal toBigDecimal(float val, int precision, int scale) { - return toBigDecimal((Float) val, precision, scale); + return removeDefaultScale(precision, scale, toBigDecimal(BigDecimal.valueOf(val), precision, scale)); + } + + /** // Removes redundant scale in case of default DECIMAL (without passed precision and scale). */ + private static BigDecimal removeDefaultScale(int precision, int scale, BigDecimal floating) { + if (precision == DFLT_NUM_PRECISION && scale == 0 && floating.compareTo(floating.setScale(0, RoundingMode.CEILING)) == 0) + return floating.setScale(0, RoundingMode.CEILING); + + return floating; } /** CAST(java long AS DECIMAL). */ public static BigDecimal toBigDecimal(long val, int precision, int scale) { - return convertDecimal(BigDecimal.valueOf(val), precision, scale); + return toBigDecimal(BigDecimal.valueOf(val), precision, scale); } /** CAST(INT AS DECIMAL). */ public static BigDecimal toBigDecimal(int val, int precision, int scale) { - return convertDecimal(new BigDecimal(val), precision, scale); + return toBigDecimal(BigDecimal.valueOf(val), precision, scale); } /** CAST(java short AS DECIMAL). */ public static BigDecimal toBigDecimal(short val, int precision, int scale) { - return convertDecimal(new BigDecimal(val), precision, scale); + return toBigDecimal(BigDecimal.valueOf(val), precision, scale); } /** CAST(java byte AS DECIMAL). */ public static BigDecimal toBigDecimal(byte val, int precision, int scale) { - return convertDecimal(new BigDecimal(val), precision, scale); + return toBigDecimal(BigDecimal.valueOf(val), precision, scale); } /** CAST(BOOL AS DECIMAL). */ @@ -107,60 +121,47 @@ public static BigDecimal toBigDecimal(String s, int precision, int scale) { if (s == null) return null; - return convertDecimal(new BigDecimal(s.trim()), precision, scale); + return toBigDecimal(new BigDecimal(s.trim()), precision, scale); } /** - * Converts the given {@code BigDecimal} to a decimal with the given {@code precision} and {@code scale} + * Converts the given {@code Number} to a decimal with the given {@code precision} and {@code scale} * according to SQL spec for CAST specification: General Rules, 8. */ - public static BigDecimal convertDecimal(BigDecimal val, int precision, int scale) { + public static BigDecimal toBigDecimal(Number val, int precision, int scale) { assert precision > 0 : "Invalid precision: " + precision; + assert scale >= 0 : "Invalid scale: " + scale; - int dfltPrecision = IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL); - - if (precision == dfltPrecision) { - // This branch covers at least one known case: access to dynamic parameter from context. - // In this scenario precision = DefaultTypePrecision, because types for dynamic params - // are created by toSql(createType(param.class)). - return val; - } - - boolean nonZero = !val.unscaledValue().equals(BigInteger.ZERO); + if (val == null) + return null; - if (nonZero) { - if (scale > precision) - throw new IllegalArgumentException(NUMERIC_FIELD_OVERFLOW_ERROR); - else { - int curSignificantDigits = val.precision() - val.scale(); - int expectedSignificantDigits = precision - scale; + if (precision == DFLT_NUM_PRECISION) + return convertToBigDecimal(val); - if (curSignificantDigits > expectedSignificantDigits) - throw new IllegalArgumentException(NUMERIC_FIELD_OVERFLOW_ERROR); + BigDecimal dec = convertToBigDecimal(val); - } - } + if (scale > precision || (dec.precision() - dec.scale() > precision - scale)) + throw new IllegalArgumentException(NUMERIC_FIELD_OVERFLOW_ERROR); - return val.setScale(scale, RoundingMode.HALF_UP); + return dec.setScale(scale, NUMERIC_ROUNDING_MODE); } - /** CAST(REAL AS DECIMAL). */ - public static BigDecimal toBigDecimal(Number num, int precision, int scale) { - if (num == null) - return null; - + /** */ + private static BigDecimal convertToBigDecimal(Number value) { BigDecimal dec; - if (num instanceof Float || num instanceof Double) - dec = BigDecimal.valueOf(num.doubleValue()); - else if (num instanceof BigDecimal) - dec = (BigDecimal)num; - else if (num instanceof BigInteger) - dec = new BigDecimal((BigInteger)num); + if (value instanceof Float) + dec = BigDecimal.valueOf(value.floatValue()); + else if (value instanceof Double) + dec = BigDecimal.valueOf(value.doubleValue()); + else if (value instanceof BigDecimal) + dec = (BigDecimal) value; + else if (value instanceof BigInteger) + dec = new BigDecimal((BigInteger) value); else - dec = new BigDecimal(num.longValue()); + dec = new BigDecimal(value.longValue()); - return convertDecimal(dec, precision, scale); + return dec; } /** Cast object depending on type to DECIMAL. */ 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 2565a7bdfd1d1..c074a3e697b14 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 @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.prepare.CalciteCatalogReader; import org.apache.calcite.prepare.Prepare; @@ -55,6 +56,7 @@ 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.type.SqlTypeUtil; import org.apache.calcite.sql.validate.SelectScope; import org.apache.calcite.sql.validate.SqlQualified; import org.apache.calcite.sql.validate.SqlValidator; @@ -285,6 +287,47 @@ else if (n instanceof SqlDynamicParam) { super.validateCall(call, scope); } + /** {@inheritDoc} */ + @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { + RelDataType dataType = super.deriveType(scope, expr); + + // Dynamic params + if (dataType.equals(unknownType) && expr instanceof SqlDynamicParam) { + // If type of dynamic parameter has not been inferred, use a type of its value. + RelDataType paramType = dynamicParameterType((SqlDynamicParam) expr); + + // If paramType is unknown setValidatedNodeType is a no-op. + setValidatedNodeType(expr, paramType); + + return paramType; + } + + return dataType; + } + + /** @return A type of the given dynamic parameter. */ + private RelDataType dynamicParameterType(SqlDynamicParam dynamicParam) { + IgniteTypeFactory typeFactory = (IgniteTypeFactory)getTypeFactory(); + + Object val = parameters[dynamicParam.getIndex()]; + + RelDataType paramType; + + // IgniteCustomType: first we must check whether dynamic parameter is a custom data type. + // If so call createCustomType with appropriate arguments. + if (val instanceof UUID) + paramType = typeFactory.createCustomType(UUID.class); + else if (val == null) + paramType = typeFactory.createSqlType(SqlTypeName.NULL); + else + paramType = typeFactory.toSql(typeFactory.createType(val.getClass())); + + // Dynamic parameters are always nullable. + // Otherwise, it seems to cause "Conversion to relational algebra failed to preserve datatypes" errors + // in some cases. + return typeFactory.createTypeWithNullability(paramType, true); + } + /** {@inheritDoc} */ @Override public String deriveAlias(SqlNode node, int ordinal) { if (node.isA(HUMAN_READABLE_ALIASES_FOR)) { @@ -524,13 +567,9 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (node instanceof SqlDynamicParam && inferredType.equals(unknownType)) { - if (parameters.length > ((SqlDynamicParam)node).getIndex()) { - Object param = parameters[((SqlDynamicParam)node).getIndex()]; - - setValidatedNodeType(node, (param == null) ? typeFactory().createSqlType(SqlTypeName.NULL) : - typeFactory().toSql(typeFactory().createType(param.getClass()))); - } + if (node instanceof SqlDynamicParam) { + if (parameters.length > ((SqlDynamicParam)node).getIndex()) + setValidatedNodeType(node, inferDynamicParamType(inferredType, (SqlDynamicParam) node)); else setValidatedNodeType(node, typeFactory().createCustomType(Object.class)); } @@ -578,6 +617,35 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } + /** */ + private RelDataType inferDynamicParamType(RelDataType inferredType, SqlDynamicParam dynamicParam) { + RelDataType type = dynamicParameterType(dynamicParam); + + RelDataType paramTypeToUse; + + /* + * If inferredType is unknown - use a type of dynamic parameter since there is no other source of type information. + * + * If parameter's type and the inferredType do not match - use parameter's type. + * This makes CAST operations to work correctly. Otherwise, cast's operand is going to have + * the same type as a target type which is not correct as it + * makes every CAST operation eligible to redundant type conversion elimination + * at later stages: + * E.g: CAST(? AS INTEGER) where ?='hello' operand is going to be inferred as INTEGER + * although it is a string. + * + * In other cases use the inferredType and we rely on type inference rules provided by + * operator's SqlOperandTypeInference and SqlOperandTypeCheckers. + */ + + if (inferredType.equals(unknownType) || (!SqlTypeUtil.equalSansNullability(type, inferredType))) + paramTypeToUse = type; + else + paramTypeToUse = inferredType; + + return typeFactory.createTypeWithNullability(paramTypeToUse, true); + } + /** {@inheritDoc} */ @Override public SqlLiteral resolveLiteral(SqlLiteral literal) { if (literal instanceof SqlNumericLiteral && literal.createSqlType(typeFactory).getSqlTypeName() == SqlTypeName.BIGINT) { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index ed7d7b09ba7b7..dfb05705246e3 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -18,7 +18,6 @@ package org.apache.ignite.internal.processors.query.calcite.integration; import java.math.BigDecimal; -import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -498,7 +497,7 @@ public void testIsNotDistinctFromTypeConversion() { sql("CREATE INDEX t2_idx ON t2(i2idx)"); sql("INSERT INTO t2 VALUES (0, 0, 0, null, '0'), (11, null, 1, 1, '1'), (2, 2, 2, 2, '22'), (3, 3, null, 3, null)"); - for (HintDefinition hint : Arrays.asList(HintDefinition.MERGE_JOIN, HintDefinition.NL_JOIN, HintDefinition.CNL_JOIN)) { + for (HintDefinition hint : F.asList(HintDefinition.MERGE_JOIN, HintDefinition.NL_JOIN, HintDefinition.CNL_JOIN)) { String h = "/*+ " + hint.name() + " */ "; // Primary keys, indexed. @@ -609,7 +608,7 @@ public void testCastsOfVarcharLiterals() { /** */ @Test - public void testCastsOfVarcharLiteralsAsDynamicparameters() { + public void testCastsOfVarcharLiteralsAsDynamicParameters() { doTestCastsOfVarcharLiterals(true); } @@ -631,13 +630,13 @@ private void doTestCastsOfVarcharLiterals(boolean dynamics) { private static List> varcharCasts() { IgniteTypeFactory tf = Commons.typeFactory(); - return Arrays.asList( - Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 3), "abc"), - Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 5), "abcde"), - Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 6), "abcde"), - Arrays.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR), "abcde"), - Arrays.asList("abcde", tf.createSqlType(SqlTypeName.CHAR), "a"), - Arrays.asList("abcde", tf.createSqlType(SqlTypeName.CHAR, 3), "abc") + return F.asList( + F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 3), "abc"), + F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 5), "abcde"), + F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 6), "abcde"), + F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR), "abcde"), + F.asList("abcde", tf.createSqlType(SqlTypeName.CHAR), "a"), + F.asList("abcde", tf.createSqlType(SqlTypeName.CHAR, 3), "abc") ); } @@ -716,80 +715,81 @@ private static List> decimalCastsTestParams() { Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_FIELD_OVERFLOW_ERROR); Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); - return Arrays.asList( + //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) + return F.>asList( // String - Arrays.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), - Arrays.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), - Arrays.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), - Arrays.asList(varcharType, "12345", decimalType(5, 1), overflowErr), - Arrays.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), - Arrays.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), - Arrays.asList(varcharType, "100", decimalType(2, 0), overflowErr), + F.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), + F.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), + F.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), + F.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), + F.asList(varcharType, "12345", decimalType(5, 1), overflowErr), + F.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), + F.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), + F.asList(varcharType, "100", decimalType(2, 0), overflowErr), // Numeric - Arrays.asList(decimal4Type, "100", decimalType(3), new BigDecimal("100")), - Arrays.asList(decimal4Type, "100", decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(decimal4Type, "100.12", decimalType(5, 1), new BigDecimal("100.1")), - Arrays.asList(decimal4Type, "100.12", decimalType(5, 0), new BigDecimal("100")), - Arrays.asList(decimal4Type, "100", decimalType(2, 0), overflowErr), - Arrays.asList(decimal4Type, "100.12", decimalType(5, 2), new BigDecimal("100.12")), + F.asList(decimal4Type, "100", decimalType(3), new BigDecimal("100")), + F.asList(decimal4Type, "100", decimalType(3, 0), new BigDecimal("100")), + F.asList(decimal4Type, "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(decimal4Type, "100.12", decimalType(5, 0), new BigDecimal("100")), + F.asList(decimal4Type, "100", decimalType(2, 0), overflowErr), + F.asList(decimal4Type, "100.12", decimalType(5, 2), new BigDecimal("100.12")), // Tinyint - Arrays.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), - Arrays.asList(tinyIntType, (byte)100, decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(tinyIntType, (byte)100, decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(tinyIntType, (byte)100, decimalType(2, 0), overflowErr), + F.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), + F.asList(tinyIntType, (byte)100, decimalType(3, 0), new BigDecimal("100")), + F.asList(tinyIntType, (byte)100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(tinyIntType, (byte)100, decimalType(2, 0), overflowErr), // Smallint - Arrays.asList(smallIntType, (short)100, decimalType(3), new BigDecimal("100")), - Arrays.asList(smallIntType, (short)100, decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(smallIntType, (short)100, decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(smallIntType, (short)100, decimalType(2, 0), overflowErr), + F.asList(smallIntType, (short)100, decimalType(3), new BigDecimal("100")), + F.asList(smallIntType, (short)100, decimalType(3, 0), new BigDecimal("100")), + F.asList(smallIntType, (short)100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(smallIntType, (short)100, decimalType(2, 0), overflowErr), // Integer - Arrays.asList(integerType, 100, decimalType(3), new BigDecimal("100")), - Arrays.asList(integerType, 100, decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(integerType, 100, decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(integerType, 100, decimalType(2, 0), overflowErr), + F.asList(integerType, 100, decimalType(3), new BigDecimal("100")), + F.asList(integerType, 100, decimalType(3, 0), new BigDecimal("100")), + F.asList(integerType, 100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(integerType, 100, decimalType(2, 0), overflowErr), // Bigint - Arrays.asList(bigintType, 100L, decimalType(3), new BigDecimal("100")), - Arrays.asList(bigintType, 100L, decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(bigintType, 100L, decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(bigintType, 100L, decimalType(2, 0), overflowErr), + F.asList(bigintType, 100L, decimalType(3), new BigDecimal("100")), + F.asList(bigintType, 100L, decimalType(3, 0), new BigDecimal("100")), + F.asList(bigintType, 100L, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(bigintType, 100L, decimalType(2, 0), overflowErr), // Real - Arrays.asList(realType, 100.0f, decimalType(3), new BigDecimal("100")), - Arrays.asList(realType, 100.0f, decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(realType, 100.0f, decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(realType, 100.0f, decimalType(2, 0), overflowErr), - Arrays.asList(realType, 0.1f, decimalType(1, 1), new BigDecimal("0.1")), - Arrays.asList(realType, 0.1f, decimalType(2, 2), new BigDecimal("0.10")), - Arrays.asList(realType, 10.12f, decimalType(2, 1), overflowErr), - Arrays.asList(realType, 0.12f, decimalType(1, 2), overflowErr), + F.asList(realType, 100.0f, decimalType(3), new BigDecimal("100")), + F.asList(realType, 100.0f, decimalType(3, 0), new BigDecimal("100")), + F.asList(realType, 100.0f, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(realType, 100.0f, decimalType(2, 0), overflowErr), + F.asList(realType, 0.1f, decimalType(1, 1), new BigDecimal("0.1")), + F.asList(realType, 0.1f, decimalType(2, 2), new BigDecimal("0.10")), + F.asList(realType, 10.12f, decimalType(2, 1), overflowErr), + F.asList(realType, 0.12f, decimalType(1, 2), overflowErr), // Double - Arrays.asList(doubleType, 100.0d, decimalType(3), new BigDecimal("100")), - Arrays.asList(doubleType, 100.0d, decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(doubleType, 100.0d, decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(doubleType, 100.0d, decimalType(2, 0), overflowErr), - Arrays.asList(doubleType, 0.1d, decimalType(1, 1), new BigDecimal("0.1")), - Arrays.asList(doubleType, 0.1d, decimalType(2, 2), new BigDecimal("0.10")), - Arrays.asList(doubleType, 10.12d, decimalType(2, 1), overflowErr), - Arrays.asList(doubleType, 0.12d, decimalType(1, 2), overflowErr), + F.asList(doubleType, 100.0d, decimalType(3), new BigDecimal("100")), + F.asList(doubleType, 100.0d, decimalType(3, 0), new BigDecimal("100")), + F.asList(doubleType, 100.0d, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(doubleType, 100.0d, decimalType(2, 0), overflowErr), + F.asList(doubleType, 0.1d, decimalType(1, 1), new BigDecimal("0.1")), + F.asList(doubleType, 0.1d, decimalType(2, 2), new BigDecimal("0.10")), + F.asList(doubleType, 10.12d, decimalType(2, 1), overflowErr), + F.asList(doubleType, 0.12d, decimalType(1, 2), overflowErr), // Decimal - Arrays.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), - Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(3), new BigDecimal("100")), - Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), - Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), - Arrays.asList(decimalType(3), new BigDecimal("100"), decimalType(2, 0), overflowErr), - Arrays.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), - Arrays.asList(decimalType(4, 2), new BigDecimal("10.12"), decimalType(2, 1), overflowErr), - Arrays.asList(decimalType(2, 2), new BigDecimal("0.12"), decimalType(1, 2), overflowErr), - Arrays.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) + F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(3), new BigDecimal("100")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(2, 0), overflowErr), + F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), + F.asList(decimalType(4, 2), new BigDecimal("10.12"), decimalType(2, 1), overflowErr), + F.asList(decimalType(2, 2), new BigDecimal("0.12"), decimalType(1, 2), overflowErr), + F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) ); } @@ -803,19 +803,6 @@ private static RelDataType decimalType(int precision) { return Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, precision, RelDataType.SCALE_NOT_SPECIFIED); } - /** */ - @Test - public void testCastNumLiterals() throws Exception { - // Cast or validation error expected. Returns: 100L. -// assertThrows("SELECT CAST('100.1' AS BIGINT)", Exception.class, "cannot cast"); - - // An overflow error expected. Returns: Infinity. - assertThrows("SELECT CAST(1e39 AS REAL)", Exception.class, "Overflow"); -// -// // An overflow error expected. Returns: Infinity. -// assertThrows("SELECT CAST(1e39 AS FLOAT)", Exception.class, "Overflow"); - } - /** */ @Test public void testFunctionArgsToNumericImplicitConversion() { From 6f188de71269c98fe4f8872cfa98df729b16f953 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Tue, 8 Oct 2024 22:56:42 +0300 Subject: [PATCH 03/43] alpha --- .../calcite/exec/exp/IgniteRexBuilder.java | 1 - .../calcite/exec/exp/IgniteSqlFunctions.java | 9 +- .../calcite/prepare/IgniteSqlValidator.java | 110 ++++++------------ .../calcite/integration/DataTypesTest.java | 28 ++--- .../DynamicParametersIntegrationTest.java | 2 +- .../JoinRehashIntegrationTest.java | 2 +- 6 files changed, 55 insertions(+), 97 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java index 5750bbe2426e8..e26d249437cc9 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java @@ -23,7 +23,6 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexLiteral; -import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.calcite.util.TypeUtils; diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java index 1e9cdcde3c918..84604b049a0b4 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java @@ -83,7 +83,7 @@ public static BigDecimal toBigDecimal(float val, int precision, int scale) { return removeDefaultScale(precision, scale, toBigDecimal(BigDecimal.valueOf(val), precision, scale)); } - /** // Removes redundant scale in case of default DECIMAL (without passed precision and scale). */ + /** Removes redundant scale in case of default DECIMAL (without passed precision and scale). */ private static BigDecimal removeDefaultScale(int precision, int scale, BigDecimal floating) { if (precision == DFLT_NUM_PRECISION && scale == 0 && floating.compareTo(floating.setScale(0, RoundingMode.CEILING)) == 0) return floating.setScale(0, RoundingMode.CEILING); @@ -125,8 +125,7 @@ public static BigDecimal toBigDecimal(String s, int precision, int scale) { } /** - * Converts the given {@code Number} to a decimal with the given {@code precision} and {@code scale} - * according to SQL spec for CAST specification: General Rules, 8. + * Converts the given {@code val} to a decimal with the given {@code precision} and {@code scale}. */ public static BigDecimal toBigDecimal(Number val, int precision, int scale) { assert precision > 0 : "Invalid precision: " + precision; @@ -155,9 +154,9 @@ private static BigDecimal convertToBigDecimal(Number value) { else if (value instanceof Double) dec = BigDecimal.valueOf(value.doubleValue()); else if (value instanceof BigDecimal) - dec = (BigDecimal) value; + dec = (BigDecimal)value; else if (value instanceof BigInteger) - dec = new BigDecimal((BigInteger) value); + dec = new BigDecimal((BigInteger)value); else dec = new BigDecimal(value.longValue()); 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 c074a3e697b14..78f77e0ce351e 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 @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.UUID; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.prepare.CalciteCatalogReader; import org.apache.calcite.prepare.Prepare; @@ -71,6 +70,7 @@ import org.apache.ignite.internal.processors.query.calcite.schema.IgniteCacheTable; import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable; import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDecimalLiteral; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteCustomType; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.internal.processors.query.calcite.util.IgniteResource; import org.apache.ignite.internal.util.typedef.F; @@ -287,47 +287,6 @@ else if (n instanceof SqlDynamicParam) { super.validateCall(call, scope); } - /** {@inheritDoc} */ - @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { - RelDataType dataType = super.deriveType(scope, expr); - - // Dynamic params - if (dataType.equals(unknownType) && expr instanceof SqlDynamicParam) { - // If type of dynamic parameter has not been inferred, use a type of its value. - RelDataType paramType = dynamicParameterType((SqlDynamicParam) expr); - - // If paramType is unknown setValidatedNodeType is a no-op. - setValidatedNodeType(expr, paramType); - - return paramType; - } - - return dataType; - } - - /** @return A type of the given dynamic parameter. */ - private RelDataType dynamicParameterType(SqlDynamicParam dynamicParam) { - IgniteTypeFactory typeFactory = (IgniteTypeFactory)getTypeFactory(); - - Object val = parameters[dynamicParam.getIndex()]; - - RelDataType paramType; - - // IgniteCustomType: first we must check whether dynamic parameter is a custom data type. - // If so call createCustomType with appropriate arguments. - if (val instanceof UUID) - paramType = typeFactory.createCustomType(UUID.class); - else if (val == null) - paramType = typeFactory.createSqlType(SqlTypeName.NULL); - else - paramType = typeFactory.toSql(typeFactory.createType(val.getClass())); - - // Dynamic parameters are always nullable. - // Otherwise, it seems to cause "Conversion to relational algebra failed to preserve datatypes" errors - // in some cases. - return typeFactory.createTypeWithNullability(paramType, true); - } - /** {@inheritDoc} */ @Override public String deriveAlias(SqlNode node, int ordinal) { if (node.isA(HUMAN_READABLE_ALIASES_FOR)) { @@ -567,13 +526,10 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (node instanceof SqlDynamicParam) { - if (parameters.length > ((SqlDynamicParam)node).getIndex()) - setValidatedNodeType(node, inferDynamicParamType(inferredType, (SqlDynamicParam) node)); - else - setValidatedNodeType(node, typeFactory().createCustomType(Object.class)); - } - else if (node instanceof SqlCall) { + if (inferDynamicParamType(inferredType, node)) + return; + + if (node instanceof SqlCall) { final SqlValidatorScope newScope = scopes.get(node); if (newScope != null) @@ -617,33 +573,37 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** */ - private RelDataType inferDynamicParamType(RelDataType inferredType, SqlDynamicParam dynamicParam) { - RelDataType type = dynamicParameterType(dynamicParam); - - RelDataType paramTypeToUse; - - /* - * If inferredType is unknown - use a type of dynamic parameter since there is no other source of type information. - * - * If parameter's type and the inferredType do not match - use parameter's type. - * This makes CAST operations to work correctly. Otherwise, cast's operand is going to have - * the same type as a target type which is not correct as it - * makes every CAST operation eligible to redundant type conversion elimination - * at later stages: - * E.g: CAST(? AS INTEGER) where ?='hello' operand is going to be inferred as INTEGER - * although it is a string. - * - * In other cases use the inferredType and we rely on type inference rules provided by - * operator's SqlOperandTypeInference and SqlOperandTypeCheckers. - */ - - if (inferredType.equals(unknownType) || (!SqlTypeUtil.equalSansNullability(type, inferredType))) - paramTypeToUse = type; - else - paramTypeToUse = inferredType; + /** + * Tries to infer set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam}, its index + * is actual to {@link #parameters}. + * + * @return {@code True} is new type was set. {@code False} otherwise. + */ + private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { + SqlDynamicParam dynamicParam; + + if (parameters == null || !(node instanceof SqlDynamicParam) || inferredType instanceof IgniteCustomType + || (dynamicParam = (SqlDynamicParam)node).getIndex() >= parameters.length) + return false; + + Object passedVal = parameters[((SqlDynamicParam)node).getIndex()]; + + if (passedVal == null) { + if (!inferredType.equals(unknownType)) + return false; + + setValidatedNodeType(node, typeFactory.createSqlType(SqlTypeName.NULL)); + } + else { + RelDataType type = typeFactory().toSql(typeFactory().createType(passedVal.getClass())); + + if (SqlTypeUtil.equalSansNullability(type, inferredType)) + return false; + + setValidatedNodeType(dynamicParam, type); + } - return typeFactory.createTypeWithNullability(paramTypeToUse, true); + return true; } /** {@inheritDoc} */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index dfb05705246e3..697f341f8acaf 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -602,19 +602,19 @@ public void testNumericConversion() { /** */ @Test - public void testCastsOfVarcharLiterals() { - doTestCastsOfVarcharLiterals(false); + public void testCastsOfVarcharsAsLiterals() { + doTestCastsOfVarchars(false); } /** */ @Test - public void testCastsOfVarcharLiteralsAsDynamicParameters() { - doTestCastsOfVarcharLiterals(true); + public void testCastsOfVarcharsAsDynamicParameters() { + doTestCastsOfVarchars(true); } /** */ - private void doTestCastsOfVarcharLiterals(boolean dynamics) { - for (List params : varcharCasts()) { + private void doTestCastsOfVarchars(boolean dynamics) { + for (List params : varcharsToCast()) { String val = params.get(0).toString(); RelDataType type = (RelDataType)params.get(1); String result = params.get(2).toString(); @@ -627,7 +627,7 @@ private void doTestCastsOfVarcharLiterals(boolean dynamics) { } /** */ - private static List> varcharCasts() { + private static List> varcharsToCast() { IgniteTypeFactory tf = Commons.typeFactory(); return F.asList( @@ -643,26 +643,26 @@ private static List> varcharCasts() { /** */ @Test public void testCastsOfNumericLiterals() { - doTestCastsOfNumericLiterals(false, false); + doTestCastsOfNumeric(false, false); } /** */ @Test - public void testCastsOfNumericLiteralsAsNumerics() { - doTestCastsOfNumericLiterals(false, true); + public void testCastsOfNumericAsDynamicParameters() { + doTestCastsOfNumeric(false, true); } /** */ @Test public void testCastsOfNumericLiteralsAsDynamicParameters() { - doTestCastsOfNumericLiterals(true, false); + doTestCastsOfNumeric(true, false); } /** */ - private void doTestCastsOfNumericLiterals(boolean dynamic, boolean numeric) { + private void doTestCastsOfNumeric(boolean dynamic, boolean numeric) { assert !dynamic || !numeric; - for (List params : decimalCastsTestParams()) { + for (List params : numericsToCast()) { assert params.size() == 4 : "Wrong params lenght: " + params.size(); RelDataType inputType = (RelDataType)params.get(0); @@ -700,7 +700,7 @@ private static String asLiteral(Object val, RelDataType type) { } /** */ - private static List> decimalCastsTestParams() { + private static List> numericsToCast() { IgniteTypeFactory tf = Commons.typeFactory(); RelDataType varcharType = tf.createSqlType(SqlTypeName.VARCHAR); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index 89fba933b5bdc..b3eea80c0f32f 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -71,7 +71,7 @@ public void testDynamicParameters() { assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); - assertQuery("SELECT ? % ?").withParams(11, 10).returns(BigDecimal.ONE).check(); + assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) .returns(Date.valueOf("2022-01-31")).check(); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java index 8b6c46aba791a..d2ea0b300705e 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java @@ -49,7 +49,7 @@ public void testResourceCleanup() throws Exception { // AbstractBasicIntegrationTest.afterTest method. GridTestUtils.runMultiThreaded(() -> { for (int i = 0; i < 100; i++) - sql(sql, i % 10); + sql(sql, "region" + (i % 10)); }, 10, "query_starter"); } From 30caf3882a3051b1c9ab9ca88c281d716e5d31c0 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 9 Oct 2024 15:24:09 +0300 Subject: [PATCH 04/43] impl --- .../query/calcite/integration/DataTypesTest.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 697f341f8acaf..9398e9c67107b 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -717,18 +717,9 @@ private static List> numericsToCast() { //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) return F.>asList( - // String - F.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), - F.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), - F.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), - F.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), + // Numeric F.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), F.asList(varcharType, "12345", decimalType(5, 1), overflowErr), - F.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), - F.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), - F.asList(varcharType, "100", decimalType(2, 0), overflowErr), - - // Numeric F.asList(decimal4Type, "100", decimalType(3), new BigDecimal("100")), F.asList(decimal4Type, "100", decimalType(3, 0), new BigDecimal("100")), F.asList(decimal4Type, "100.12", decimalType(5, 1), new BigDecimal("100.1")), From abf07340b6e9983440a9eb84ec459d1805717f11 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 9 Oct 2024 15:50:33 +0300 Subject: [PATCH 05/43] manual review --- .../calcite/exec/exp/IgniteSqlFunctions.java | 16 ++-- .../calcite/prepare/IgniteSqlValidator.java | 2 +- .../calcite/integration/DataTypesTest.java | 78 ++++++++++--------- 3 files changed, 51 insertions(+), 45 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java index 84604b049a0b4..a4371d4a2f2d4 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java @@ -43,7 +43,7 @@ */ public class IgniteSqlFunctions { /** */ - public static final String NUMERIC_FIELD_OVERFLOW_ERROR = "Numeric field overflow"; + public static final String NUMERIC_OVERFLOW_ERROR = "Numeric field overflow."; /** */ private static final int DFLT_NUM_PRECISION = IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL); @@ -84,11 +84,11 @@ public static BigDecimal toBigDecimal(float val, int precision, int scale) { } /** Removes redundant scale in case of default DECIMAL (without passed precision and scale). */ - private static BigDecimal removeDefaultScale(int precision, int scale, BigDecimal floating) { - if (precision == DFLT_NUM_PRECISION && scale == 0 && floating.compareTo(floating.setScale(0, RoundingMode.CEILING)) == 0) - return floating.setScale(0, RoundingMode.CEILING); + private static BigDecimal removeDefaultScale(int precision, int scale, BigDecimal val) { + if (precision == DFLT_NUM_PRECISION && scale == 0 && val.compareTo(val.setScale(0, NUMERIC_ROUNDING_MODE)) == 0) + return val.setScale(0, NUMERIC_ROUNDING_MODE); - return floating; + return val; } /** CAST(java long AS DECIMAL). */ @@ -124,9 +124,7 @@ public static BigDecimal toBigDecimal(String s, int precision, int scale) { return toBigDecimal(new BigDecimal(s.trim()), precision, scale); } - /** - * Converts the given {@code val} to a decimal with the given {@code precision} and {@code scale}. - */ + /** Converts {@code val} to a {@link BigDecimal} with the given {@code precision} and {@code scale}. */ public static BigDecimal toBigDecimal(Number val, int precision, int scale) { assert precision > 0 : "Invalid precision: " + precision; assert scale >= 0 : "Invalid scale: " + scale; @@ -140,7 +138,7 @@ public static BigDecimal toBigDecimal(Number val, int precision, int scale) { BigDecimal dec = convertToBigDecimal(val); if (scale > precision || (dec.precision() - dec.scale() > precision - scale)) - throw new IllegalArgumentException(NUMERIC_FIELD_OVERFLOW_ERROR); + throw new IllegalArgumentException(NUMERIC_OVERFLOW_ERROR); return dec.setScale(scale, NUMERIC_ROUNDING_MODE); } 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 78f77e0ce351e..a6e8b38230b5a 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 @@ -574,7 +574,7 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { } /** - * Tries to infer set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam}, its index + * Tries to infer set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index * is actual to {@link #parameters}. * * @return {@code True} is new type was set. {@code False} otherwise. diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 9398e9c67107b..1e51afc755424 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -602,18 +602,18 @@ public void testNumericConversion() { /** */ @Test - public void testCastsOfVarcharsAsLiterals() { - doTestCastsOfVarchars(false); + public void testVarcharsCastsLiterals() { + doTestVarcharsCasts(false); } /** */ @Test - public void testCastsOfVarcharsAsDynamicParameters() { - doTestCastsOfVarchars(true); + public void testVarcharsCastsDynamicParameters() { + doTestVarcharsCasts(true); } /** */ - private void doTestCastsOfVarchars(boolean dynamics) { + private void doTestVarcharsCasts(boolean dynamics) { for (List params : varcharsToCast()) { String val = params.get(0).toString(); RelDataType type = (RelDataType)params.get(1); @@ -642,54 +642,54 @@ private static List> varcharsToCast() { /** */ @Test - public void testCastsOfNumericLiterals() { - doTestCastsOfNumeric(false, false); + public void testNumericsLiterals() { + doTestNumericsCasts(false, false); } /** */ @Test - public void testCastsOfNumericAsDynamicParameters() { - doTestCastsOfNumeric(false, true); + public void testNumericsLiteralsPrecasted() { + doTestNumericsCasts(true, false); } /** */ @Test - public void testCastsOfNumericLiteralsAsDynamicParameters() { - doTestCastsOfNumeric(true, false); + public void testNumericsCastsDynamicParameters() { + doTestNumericsCasts(false, true); } /** */ - private void doTestCastsOfNumeric(boolean dynamic, boolean numeric) { - assert !dynamic || !numeric; + private void doTestNumericsCasts(boolean dynamic, boolean precasted) { + assert !dynamic || !precasted; for (List params : numericsToCast()) { assert params.size() == 4 : "Wrong params lenght: " + params.size(); RelDataType inputType = (RelDataType)params.get(0); - Object input = params.get(1); + Object inputVal = params.get(1); RelDataType targetType = (RelDataType)params.get(2); - Object result = params.get(3); + Object expectedRes = params.get(3); - log.info("Params: inputType=" + inputType + ", inputValue=" + input + ", targetType=" + targetType - + ", expectedResult=" + result); + log.info("Params: inputType=" + inputType + ", inputValue=" + inputVal + ", targetType=" + targetType + + ", expectedResult=" + expectedRes); if (dynamic) { String qry = String.format("SELECT CAST(? AS %s)", targetType); - if (result instanceof Exception) - assertThrows(qry, (Class)result.getClass(), ((Throwable)result).getMessage(), input); + if (expectedRes instanceof Exception) + assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage(), inputVal); else - assertQuery(qry).withParams(inputType).returns(result); + assertQuery(qry).withParams(inputType).returns(expectedRes); } else { - String qry = numeric - ? String.format("SELECT CAST(%s::%s AS %s)", asLiteral(input, inputType), inputType, targetType) - : String.format("SELECT CAST(%s AS %s)", asLiteral(input, inputType), targetType); + String qry = precasted + ? String.format("SELECT CAST(%s::%s AS %s)", asLiteral(inputVal, inputType), inputType, targetType) + : String.format("SELECT CAST(%s AS %s)", asLiteral(inputVal, inputType), targetType); - if (result instanceof Exception) - assertThrows(qry, (Class)result.getClass(), ((Throwable)result).getMessage()); + if (expectedRes instanceof Exception) + assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage()); else - assertQuery(qry).returns(result); + assertQuery(qry).returns(expectedRes); } } } @@ -710,22 +710,30 @@ private static List> numericsToCast() { RelDataType bigintType = tf.createSqlType(SqlTypeName.BIGINT); RelDataType realType = tf.createSqlType(SqlTypeName.REAL); RelDataType doubleType = tf.createSqlType(SqlTypeName.DOUBLE); - RelDataType decimal4Type = decimalType(4); - Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_FIELD_OVERFLOW_ERROR); + Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_OVERFLOW_ERROR); Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) return F.>asList( - // Numeric + // String + F.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), + F.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), + F.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), + F.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), F.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), F.asList(varcharType, "12345", decimalType(5, 1), overflowErr), - F.asList(decimal4Type, "100", decimalType(3), new BigDecimal("100")), - F.asList(decimal4Type, "100", decimalType(3, 0), new BigDecimal("100")), - F.asList(decimal4Type, "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(decimal4Type, "100.12", decimalType(5, 0), new BigDecimal("100")), - F.asList(decimal4Type, "100", decimalType(2, 0), overflowErr), - F.asList(decimal4Type, "100.12", decimalType(5, 2), new BigDecimal("100.12")), + F.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), + F.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), + F.asList(varcharType, "100", decimalType(2, 0), overflowErr), + + // Numeric + F.asList(decimalType(4), "100", decimalType(3), new BigDecimal("100")), + F.asList(decimalType(4), "100", decimalType(3, 0), new BigDecimal("100")), + F.asList(decimalType(4), "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(decimalType(4), "100.12", decimalType(5, 0), new BigDecimal("100")), + F.asList(decimalType(4), "100", decimalType(2, 0), overflowErr), + F.asList(decimalType(4), "100.12", decimalType(5, 2), new BigDecimal("100.12")), // Tinyint F.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), From f176d69319eef2372479c25603fe1c5d2f1e1083 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 9 Oct 2024 16:20:56 +0300 Subject: [PATCH 06/43] reimpl --- .../calcite/prepare/IgniteSqlValidator.java | 9 +++--- .../calcite/integration/DataTypesTest.java | 28 +++++++++---------- .../JoinRehashIntegrationTest.java | 2 +- 3 files changed, 20 insertions(+), 19 deletions(-) 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 a6e8b38230b5a..2d93730238541 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 @@ -586,7 +586,7 @@ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { || (dynamicParam = (SqlDynamicParam)node).getIndex() >= parameters.length) return false; - Object passedVal = parameters[((SqlDynamicParam)node).getIndex()]; + Object passedVal = parameters[dynamicParam.getIndex()]; if (passedVal == null) { if (!inferredType.equals(unknownType)) @@ -595,12 +595,13 @@ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { setValidatedNodeType(node, typeFactory.createSqlType(SqlTypeName.NULL)); } else { - RelDataType type = typeFactory().toSql(typeFactory().createType(passedVal.getClass())); + RelDataType passedValType = typeFactory().toSql(typeFactory().createType(passedVal.getClass())); - if (SqlTypeUtil.equalSansNullability(type, inferredType)) + if (unknownType.equals(passedValType) || SqlTypeUtil.equalSansNullability(passedValType, inferredType) + || inferredType == typeFactory.leastRestrictive(F.asList(inferredType, passedValType))) return false; - setValidatedNodeType(dynamicParam, type); + setValidatedNodeType(node, passedValType); } return true; diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 1e51afc755424..bbc87d3e67a06 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -602,19 +602,19 @@ public void testNumericConversion() { /** */ @Test - public void testVarcharsCastsLiterals() { - doTestVarcharsCasts(false); + public void testCoercionOfVarcharLiterals() { + doTestCoercionOfVarchars(false); } /** */ @Test - public void testVarcharsCastsDynamicParameters() { - doTestVarcharsCasts(true); + public void testCoercionOfVarcharDynamicParameters() { + doTestCoercionOfVarchars(true); } /** */ - private void doTestVarcharsCasts(boolean dynamics) { - for (List params : varcharsToCast()) { + private void doTestCoercionOfVarchars(boolean dynamics) { + for (List params : varcharsToCoerce()) { String val = params.get(0).toString(); RelDataType type = (RelDataType)params.get(1); String result = params.get(2).toString(); @@ -627,7 +627,7 @@ private void doTestVarcharsCasts(boolean dynamics) { } /** */ - private static List> varcharsToCast() { + private static List> varcharsToCoerce() { IgniteTypeFactory tf = Commons.typeFactory(); return F.asList( @@ -642,24 +642,24 @@ private static List> varcharsToCast() { /** */ @Test - public void testNumericsLiterals() { - doTestNumericsCasts(false, false); + public void testCoercionOfNumericLiterals() { + doTestCoercionOfNumerics(false, false); } /** */ @Test - public void testNumericsLiteralsPrecasted() { - doTestNumericsCasts(true, false); + public void testCoercionOfNumericLiteralsPrecasted() { + doTestCoercionOfNumerics(true, false); } /** */ @Test - public void testNumericsCastsDynamicParameters() { - doTestNumericsCasts(false, true); + public void testCoercionOfNumericDynamicParameters() { + doTestCoercionOfNumerics(false, true); } /** */ - private void doTestNumericsCasts(boolean dynamic, boolean precasted) { + private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { assert !dynamic || !precasted; for (List params : numericsToCast()) { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java index d2ea0b300705e..8b6c46aba791a 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java @@ -49,7 +49,7 @@ public void testResourceCleanup() throws Exception { // AbstractBasicIntegrationTest.afterTest method. GridTestUtils.runMultiThreaded(() -> { for (int i = 0; i < 100; i++) - sql(sql, "region" + (i % 10)); + sql(sql, i % 10); }, 10, "query_starter"); } From 957b4fb63461eef6dfcbf2af48206529c322f6ca Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 9 Oct 2024 19:34:34 +0300 Subject: [PATCH 07/43] reimpl tested --- .../calcite/prepare/IgniteSqlValidator.java | 46 +++++++++++-------- 1 file changed, 28 insertions(+), 18 deletions(-) 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 2d93730238541..86ad42b9bf1d5 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 @@ -70,7 +70,6 @@ import org.apache.ignite.internal.processors.query.calcite.schema.IgniteCacheTable; import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable; import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDecimalLiteral; -import org.apache.ignite.internal.processors.query.calcite.type.IgniteCustomType; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.internal.processors.query.calcite.util.IgniteResource; import org.apache.ignite.internal.util.typedef.F; @@ -574,35 +573,46 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { } /** - * Tries to infer set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index + * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index * is actual to {@link #parameters}. * - * @return {@code True} is new type was set. {@code False} otherwise. + * @return {@code True} if a new type was set. {@code False} otherwise. */ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - SqlDynamicParam dynamicParam; - - if (parameters == null || !(node instanceof SqlDynamicParam) || inferredType instanceof IgniteCustomType - || (dynamicParam = (SqlDynamicParam)node).getIndex() >= parameters.length) + if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length) return false; - Object passedVal = parameters[dynamicParam.getIndex()]; + Object val = parameters[((SqlDynamicParam)node).getIndex()]; - if (passedVal == null) { - if (!inferredType.equals(unknownType)) - return false; + if (val == null) { + if (inferredType.equals(unknownType)) { + setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL)); + + return true; + } - setValidatedNodeType(node, typeFactory.createSqlType(SqlTypeName.NULL)); + return false; } - else { - RelDataType passedValType = typeFactory().toSql(typeFactory().createType(passedVal.getClass())); - if (unknownType.equals(passedValType) || SqlTypeUtil.equalSansNullability(passedValType, inferredType) - || inferredType == typeFactory.leastRestrictive(F.asList(inferredType, passedValType))) - return false; + RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass())); + + if (SqlTypeUtil.equalSansNullability(valType, inferredType)) + return false; + + assert !unknownType.equals(valType); + + if (valType.getFamily().equals(inferredType.getFamily())) { + RelDataType leastRestrictive = typeFactory().leastRestrictive(F.asList(inferredType, valType)); + + assert leastRestrictive != null; - setValidatedNodeType(node, passedValType); + if (inferredType == leastRestrictive) + return false; } + else if (!unknownType.equals(inferredType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) + return false; + + setValidatedNodeType(node, valType); return true; } From a857b749876d31a16b68ba48f5c0935340e67d9c Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 11 Oct 2024 15:51:49 +0300 Subject: [PATCH 08/43] test fix --- .../query/calcite/integration/DataTypesTest.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index bbc87d3e67a06..85adaffc4ee5d 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -649,19 +649,23 @@ public void testCoercionOfNumericLiterals() { /** */ @Test public void testCoercionOfNumericLiteralsPrecasted() { - doTestCoercionOfNumerics(true, false); + doTestCoercionOfNumerics(false, true); } /** */ @Test public void testCoercionOfNumericDynamicParameters() { - doTestCoercionOfNumerics(false, true); + doTestCoercionOfNumerics(true, false); } /** */ - private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { - assert !dynamic || !precasted; + @Test + public void testCoercionOfNumericDynamicParametersPrecasted() { + doTestCoercionOfNumerics(true, true); + } + /** */ + private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { for (List params : numericsToCast()) { assert params.size() == 4 : "Wrong params lenght: " + params.size(); @@ -674,7 +678,9 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { + ", expectedResult=" + expectedRes); if (dynamic) { - String qry = String.format("SELECT CAST(? AS %s)", targetType); + String qry = precasted + ? String.format("SELECT CAST(?::%s AS %s)", inputType, targetType) + : String.format("SELECT CAST(? AS %s)", targetType); if (expectedRes instanceof Exception) assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage(), inputVal); From 1c69c5ade858bff84cef1440b22265fab7f571fe Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 18 Oct 2024 11:48:28 +0300 Subject: [PATCH 09/43] fix --- .../calcite/integration/DataTypesTest.java | 169 +++++++++--------- 1 file changed, 87 insertions(+), 82 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 526bc117d90cb..00ac0e06bddc2 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -581,21 +581,25 @@ public void testNumericConversion() { /** */ @Test public void testCoercionOfVarcharLiterals() { + assumeNoTransactions(); + doTestCoercionOfVarchars(false); } /** */ @Test public void testCoercionOfVarcharDynamicParameters() { + assumeNoTransactions(); + doTestCoercionOfVarchars(true); } /** */ private void doTestCoercionOfVarchars(boolean dynamics) { - for (List params : varcharsToCoerce()) { - String val = params.get(0).toString(); - RelDataType type = (RelDataType)params.get(1); - String result = params.get(2).toString(); + for (List params : varcharsToCoerce()) { + String val = params.get(0); + String type = params.get(1); + String result = params.get(2); if (dynamics) assertQuery(String.format("SELECT CAST(? AS %s)", type)).withParams(val).returns(result).check(); @@ -605,40 +609,46 @@ private void doTestCoercionOfVarchars(boolean dynamics) { } /** */ - private static List> varcharsToCoerce() { - IgniteTypeFactory tf = Commons.typeFactory(); - + private static List> varcharsToCoerce() { return F.asList( - F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 3), "abc"), - F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 5), "abcde"), - F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR, 6), "abcde"), - F.asList("abcde", tf.createSqlType(SqlTypeName.VARCHAR), "abcde"), - F.asList("abcde", tf.createSqlType(SqlTypeName.CHAR), "a"), - F.asList("abcde", tf.createSqlType(SqlTypeName.CHAR, 3), "abc") + F.asList("abcde", "VARCHAR(3)", "abc"), + F.asList("abcde", "VARCHAR(5)", "abcde"), + F.asList("abcde", "VARCHAR(6)", "abcde"), + F.asList("abcde", "VARCHAR", "abcde"), + F.asList("abcde", "CHAR", "a"), + F.asList("abcde", "CHAR(3)", "abc") ); } /** */ @Test public void testCoercionOfNumericLiterals() { + assumeNoTransactions(); + doTestCoercionOfNumerics(false, false); } /** */ @Test public void testCoercionOfNumericLiteralsPrecasted() { + assumeNoTransactions(); + doTestCoercionOfNumerics(false, true); } /** */ @Test public void testCoercionOfNumericDynamicParameters() { + assumeNoTransactions(); + doTestCoercionOfNumerics(true, false); } /** */ @Test public void testCoercionOfNumericDynamicParametersPrecasted() { + assumeNoTransactions(); + doTestCoercionOfNumerics(true, true); } @@ -647,7 +657,7 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { for (List params : numericsToCast()) { assert params.size() == 4 : "Wrong params lenght: " + params.size(); - RelDataType inputType = (RelDataType)params.get(0); + String inputType = params.get(0).toString(); Object inputVal = params.get(1); RelDataType targetType = (RelDataType)params.get(2); Object expectedRes = params.get(3); @@ -685,94 +695,84 @@ private static String asLiteral(Object val, RelDataType type) { /** */ private static List> numericsToCast() { - IgniteTypeFactory tf = Commons.typeFactory(); - - RelDataType varcharType = tf.createSqlType(SqlTypeName.VARCHAR); - RelDataType tinyIntType = tf.createSqlType(SqlTypeName.TINYINT); - RelDataType smallIntType = tf.createSqlType(SqlTypeName.SMALLINT); - RelDataType integerType = tf.createSqlType(SqlTypeName.INTEGER); - RelDataType bigintType = tf.createSqlType(SqlTypeName.BIGINT); - RelDataType realType = tf.createSqlType(SqlTypeName.REAL); - RelDataType doubleType = tf.createSqlType(SqlTypeName.DOUBLE); - Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_OVERFLOW_ERROR); Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) return F.>asList( // String - F.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), - F.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), - F.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), - F.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), - F.asList(varcharType, "12345", decimalType(5, 1), overflowErr), - F.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), - F.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), - F.asList(varcharType, "100", decimalType(2, 0), overflowErr), + F.asList("VARCHAR", "100", decimalType(3), new BigDecimal("100")), + F.asList("VARCHAR", "100", decimalType(3, 0), new BigDecimal("100")), + F.asList("VARCHAR", "100", decimalType(4, 1), new BigDecimal("100.0")), + F.asList("VARCHAR", "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList("VARCHAR", "lame", decimalType(5, 1), numFormatErr), + F.asList("VARCHAR", "12345", decimalType(5, 1), overflowErr), + F.asList("VARCHAR", "1234", decimalType(5, 1), new BigDecimal("1234.0")), + F.asList("VARCHAR", "100.12", decimalType(1, 0), overflowErr), + F.asList("VARCHAR", "100", decimalType(2, 0), overflowErr), // Numeric - F.asList(decimalType(4), "100", decimalType(3), new BigDecimal("100")), - F.asList(decimalType(4), "100", decimalType(3, 0), new BigDecimal("100")), - F.asList(decimalType(4), "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(decimalType(4), "100.12", decimalType(5, 0), new BigDecimal("100")), - F.asList(decimalType(4), "100", decimalType(2, 0), overflowErr), - F.asList(decimalType(4), "100.12", decimalType(5, 2), new BigDecimal("100.12")), + F.asList("DECIMAL(4)", "100", decimalType(3), new BigDecimal("100")), + F.asList("DECIMAL(4)", "100", decimalType(3, 0), new BigDecimal("100")), + F.asList("DECIMAL(4)", "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList("DECIMAL(4)", "100.12", decimalType(5, 0), new BigDecimal("100")), + F.asList("DECIMAL(4)", "100", decimalType(2, 0), overflowErr), + F.asList("DECIMAL(4)", "100.12", decimalType(5, 2), new BigDecimal("100.12")), // Tinyint - F.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), - F.asList(tinyIntType, (byte)100, decimalType(3, 0), new BigDecimal("100")), - F.asList(tinyIntType, (byte)100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(tinyIntType, (byte)100, decimalType(2, 0), overflowErr), + F.asList("TINYINT", (byte)100, decimalType(3), new BigDecimal("100")), + F.asList("TINYINT", (byte)100, decimalType(3, 0), new BigDecimal("100")), + F.asList("TINYINT", (byte)100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList("TINYINT", (byte)100, decimalType(2, 0), overflowErr), // Smallint - F.asList(smallIntType, (short)100, decimalType(3), new BigDecimal("100")), - F.asList(smallIntType, (short)100, decimalType(3, 0), new BigDecimal("100")), - F.asList(smallIntType, (short)100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(smallIntType, (short)100, decimalType(2, 0), overflowErr), + F.asList("SMALLINT", (short)100, decimalType(3), new BigDecimal("100")), + F.asList("SMALLINT", (short)100, decimalType(3, 0), new BigDecimal("100")), + F.asList("SMALLINT", (short)100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList("SMALLINT", (short)100, decimalType(2, 0), overflowErr), // Integer - F.asList(integerType, 100, decimalType(3), new BigDecimal("100")), - F.asList(integerType, 100, decimalType(3, 0), new BigDecimal("100")), - F.asList(integerType, 100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(integerType, 100, decimalType(2, 0), overflowErr), + F.asList("INTEGER", 100, decimalType(3), new BigDecimal("100")), + F.asList("INTEGER", 100, decimalType(3, 0), new BigDecimal("100")), + F.asList("INTEGER", 100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList("INTEGER", 100, decimalType(2, 0), overflowErr), // Bigint - F.asList(bigintType, 100L, decimalType(3), new BigDecimal("100")), - F.asList(bigintType, 100L, decimalType(3, 0), new BigDecimal("100")), - F.asList(bigintType, 100L, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(bigintType, 100L, decimalType(2, 0), overflowErr), + F.asList("BIGINT", 100L, decimalType(3), new BigDecimal("100")), + F.asList("BIGINT", 100L, decimalType(3, 0), new BigDecimal("100")), + F.asList("BIGINT", 100L, decimalType(4, 1), new BigDecimal("100.0")), + F.asList("BIGINT", 100L, decimalType(2, 0), overflowErr), // Real - F.asList(realType, 100.0f, decimalType(3), new BigDecimal("100")), - F.asList(realType, 100.0f, decimalType(3, 0), new BigDecimal("100")), - F.asList(realType, 100.0f, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(realType, 100.0f, decimalType(2, 0), overflowErr), - F.asList(realType, 0.1f, decimalType(1, 1), new BigDecimal("0.1")), - F.asList(realType, 0.1f, decimalType(2, 2), new BigDecimal("0.10")), - F.asList(realType, 10.12f, decimalType(2, 1), overflowErr), - F.asList(realType, 0.12f, decimalType(1, 2), overflowErr), + F.asList("REAL", 100.0f, decimalType(3), new BigDecimal("100")), + F.asList("REAL", 100.0f, decimalType(3, 0), new BigDecimal("100")), + F.asList("REAL", 100.0f, decimalType(4, 1), new BigDecimal("100.0")), + F.asList("REAL", 100.0f, decimalType(2, 0), overflowErr), + F.asList("REAL", 0.1f, decimalType(1, 1), new BigDecimal("0.1")), + F.asList("REAL", 0.1f, decimalType(2, 2), new BigDecimal("0.10")), + F.asList("REAL", 10.12f, decimalType(2, 1), overflowErr), + F.asList("REAL", 0.12f, decimalType(1, 2), overflowErr), // Double - F.asList(doubleType, 100.0d, decimalType(3), new BigDecimal("100")), - F.asList(doubleType, 100.0d, decimalType(3, 0), new BigDecimal("100")), - F.asList(doubleType, 100.0d, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(doubleType, 100.0d, decimalType(2, 0), overflowErr), - F.asList(doubleType, 0.1d, decimalType(1, 1), new BigDecimal("0.1")), - F.asList(doubleType, 0.1d, decimalType(2, 2), new BigDecimal("0.10")), - F.asList(doubleType, 10.12d, decimalType(2, 1), overflowErr), - F.asList(doubleType, 0.12d, decimalType(1, 2), overflowErr), + F.asList("DOUBLE", 100.0d, decimalType(3), new BigDecimal("100")), + F.asList("DOUBLE", 100.0d, decimalType(3, 0), new BigDecimal("100")), + F.asList("DOUBLE", 100.0d, decimalType(4, 1), new BigDecimal("100.0")), + F.asList("DOUBLE", 100.0d, decimalType(2, 0), overflowErr), + F.asList("DOUBLE", 0.1d, decimalType(1, 1), new BigDecimal("0.1")), + F.asList("DOUBLE", 0.1d, decimalType(2, 2), new BigDecimal("0.10")), + F.asList("DOUBLE", 10.12d, decimalType(2, 1), overflowErr), + F.asList("DOUBLE", 0.12d, decimalType(1, 2), overflowErr), // Decimal - F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(3), new BigDecimal("100")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(2, 0), overflowErr), - F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), - F.asList(decimalType(4, 2), new BigDecimal("10.12"), decimalType(2, 1), overflowErr), - F.asList(decimalType(2, 2), new BigDecimal("0.12"), decimalType(1, 2), overflowErr), - F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) + F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), + F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(3), new BigDecimal("100")), + F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), + F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), + F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(2, 0), overflowErr), + F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), + F.asList("DECIMAL(4, 2)", new BigDecimal("10.12"), decimalType(2, 1), overflowErr), + F.asList("DECIMAL(2, 2)", new BigDecimal("0.12"), decimalType(1, 2), overflowErr), + F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) ); } @@ -789,7 +789,7 @@ private static RelDataType decimalType(int precision) { /** */ @Test public void testFunctionArgsToNumericImplicitConversion() { - assumeTrue("Test use queries that doesn't touch any data. Skip for tx modes", sqlTxMode == SqlTransactionMode.NONE); + assumeNoTransactions(); assertQuery("select decode(?, 0, 0, 1, 1.0)").withParams(0).returns(new BigDecimal("0.0")).check(); assertQuery("select decode(?, 0, 0, 1, 1.0)").withParams(1).returns(new BigDecimal("1.0")).check(); @@ -864,7 +864,7 @@ public void testArithmeticOverflow() { /** */ @Test public void testCastDecimalOverflows() { - assumeTrue("Test use queries that doesn't touch any data. Skip for tx modes", sqlTxMode == SqlTransactionMode.NONE); + assumeNoTransactions(); // BIGINT assertQuery("SELECT CAST(9223372036854775807.1 AS BIGINT)").returns(9223372036854775807L).check(); @@ -943,4 +943,9 @@ public void testCastDecimalOverflows() { assertQuery("SELECT CAST('-128.1' AS TINYINT)").returns((byte)-128).check(); assertQuery("SELECT CAST('-128.9' AS TINYINT)").returns((byte)-128).check(); } + + /** */ + private void assumeNoTransactions() { + assumeTrue("Test use queries that doesn't touch any data. Skip for tx modes", sqlTxMode == SqlTransactionMode.NONE); + } } From 79e51abc5467d386a5402a06b8bbccf17858626d Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 18 Oct 2024 12:09:30 +0300 Subject: [PATCH 10/43] review fixex --- .../calcite/integration/DataTypesTest.java | 128 ++++++++++-------- 1 file changed, 71 insertions(+), 57 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 00ac0e06bddc2..c24187f6b06ab 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -657,7 +657,7 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { for (List params : numericsToCast()) { assert params.size() == 4 : "Wrong params lenght: " + params.size(); - String inputType = params.get(0).toString(); + RelDataType inputType = (RelDataType)params.get(0); Object inputVal = params.get(1); RelDataType targetType = (RelDataType)params.get(2); Object expectedRes = params.get(3); @@ -695,84 +695,98 @@ private static String asLiteral(Object val, RelDataType type) { /** */ private static List> numericsToCast() { + IgniteTypeFactory tf = Commons.typeFactory(); + + RelDataType varcharType = tf.createSqlType(SqlTypeName.VARCHAR); + RelDataType tinyIntType = tf.createSqlType(SqlTypeName.TINYINT); + RelDataType smallIntType = tf.createSqlType(SqlTypeName.SMALLINT); + RelDataType integerType = tf.createSqlType(SqlTypeName.INTEGER); + RelDataType bigintType = tf.createSqlType(SqlTypeName.BIGINT); + RelDataType realType = tf.createSqlType(SqlTypeName.REAL); + RelDataType doubleType = tf.createSqlType(SqlTypeName.DOUBLE); + Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_OVERFLOW_ERROR); Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) return F.>asList( // String - F.asList("VARCHAR", "100", decimalType(3), new BigDecimal("100")), - F.asList("VARCHAR", "100", decimalType(3, 0), new BigDecimal("100")), - F.asList("VARCHAR", "100", decimalType(4, 1), new BigDecimal("100.0")), - F.asList("VARCHAR", "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList("VARCHAR", "lame", decimalType(5, 1), numFormatErr), - F.asList("VARCHAR", "12345", decimalType(5, 1), overflowErr), - F.asList("VARCHAR", "1234", decimalType(5, 1), new BigDecimal("1234.0")), - F.asList("VARCHAR", "100.12", decimalType(1, 0), overflowErr), - F.asList("VARCHAR", "100", decimalType(2, 0), overflowErr), + F.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), + F.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), + F.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), + F.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(varcharType, "100.16", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(varcharType, "-100.16", decimalType(5, 1), new BigDecimal("-100.1")), + F.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), + F.asList(varcharType, "12345", decimalType(5, 1), overflowErr), + F.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), + F.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), + F.asList(varcharType, "100", decimalType(2, 0), overflowErr), // Numeric - F.asList("DECIMAL(4)", "100", decimalType(3), new BigDecimal("100")), - F.asList("DECIMAL(4)", "100", decimalType(3, 0), new BigDecimal("100")), - F.asList("DECIMAL(4)", "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList("DECIMAL(4)", "100.12", decimalType(5, 0), new BigDecimal("100")), - F.asList("DECIMAL(4)", "100", decimalType(2, 0), overflowErr), - F.asList("DECIMAL(4)", "100.12", decimalType(5, 2), new BigDecimal("100.12")), + F.asList(decimalType(4), "100", decimalType(3), new BigDecimal("100")), + F.asList(decimalType(4), "100", decimalType(3, 0), new BigDecimal("100")), + F.asList(decimalType(4), "100.12", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(decimalType(4), "100.12", decimalType(5, 0), new BigDecimal("100")), + F.asList(decimalType(4), "100.16", decimalType(5, 1), new BigDecimal("100.1")), + F.asList(decimalType(4), "-100.16", decimalType(5, 1), new BigDecimal("-100.1")), + F.asList(decimalType(4), "100", decimalType(2, 0), overflowErr), + F.asList(decimalType(4), "100.12", decimalType(5, 2), new BigDecimal("100.12")), // Tinyint - F.asList("TINYINT", (byte)100, decimalType(3), new BigDecimal("100")), - F.asList("TINYINT", (byte)100, decimalType(3, 0), new BigDecimal("100")), - F.asList("TINYINT", (byte)100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList("TINYINT", (byte)100, decimalType(2, 0), overflowErr), + F.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), + F.asList(tinyIntType, (byte)100, decimalType(3, 0), new BigDecimal("100")), + F.asList(tinyIntType, (byte)100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(tinyIntType, (byte)100, decimalType(2, 0), overflowErr), // Smallint - F.asList("SMALLINT", (short)100, decimalType(3), new BigDecimal("100")), - F.asList("SMALLINT", (short)100, decimalType(3, 0), new BigDecimal("100")), - F.asList("SMALLINT", (short)100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList("SMALLINT", (short)100, decimalType(2, 0), overflowErr), + F.asList(smallIntType, (short)100, decimalType(3), new BigDecimal("100")), + F.asList(smallIntType, (short)100, decimalType(3, 0), new BigDecimal("100")), + F.asList(smallIntType, (short)100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(smallIntType, (short)100, decimalType(2, 0), overflowErr), // Integer - F.asList("INTEGER", 100, decimalType(3), new BigDecimal("100")), - F.asList("INTEGER", 100, decimalType(3, 0), new BigDecimal("100")), - F.asList("INTEGER", 100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList("INTEGER", 100, decimalType(2, 0), overflowErr), + F.asList(integerType, 100, decimalType(3), new BigDecimal("100")), + F.asList(integerType, 100, decimalType(3, 0), new BigDecimal("100")), + F.asList(integerType, 100, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(integerType, 100, decimalType(2, 0), overflowErr), // Bigint - F.asList("BIGINT", 100L, decimalType(3), new BigDecimal("100")), - F.asList("BIGINT", 100L, decimalType(3, 0), new BigDecimal("100")), - F.asList("BIGINT", 100L, decimalType(4, 1), new BigDecimal("100.0")), - F.asList("BIGINT", 100L, decimalType(2, 0), overflowErr), + F.asList(bigintType, 100L, decimalType(3), new BigDecimal("100")), + F.asList(bigintType, 100L, decimalType(3, 0), new BigDecimal("100")), + F.asList(bigintType, 100L, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(bigintType, 100L, decimalType(2, 0), overflowErr), // Real - F.asList("REAL", 100.0f, decimalType(3), new BigDecimal("100")), - F.asList("REAL", 100.0f, decimalType(3, 0), new BigDecimal("100")), - F.asList("REAL", 100.0f, decimalType(4, 1), new BigDecimal("100.0")), - F.asList("REAL", 100.0f, decimalType(2, 0), overflowErr), - F.asList("REAL", 0.1f, decimalType(1, 1), new BigDecimal("0.1")), - F.asList("REAL", 0.1f, decimalType(2, 2), new BigDecimal("0.10")), - F.asList("REAL", 10.12f, decimalType(2, 1), overflowErr), - F.asList("REAL", 0.12f, decimalType(1, 2), overflowErr), + F.asList(realType, 100.0f, decimalType(3), new BigDecimal("100")), + F.asList(realType, 100.0f, decimalType(3, 0), new BigDecimal("100")), + F.asList(realType, 100.0f, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(realType, 100.0f, decimalType(2, 0), overflowErr), + F.asList(realType, 0.1f, decimalType(1, 1), new BigDecimal("0.1")), + F.asList(realType, 0.1f, decimalType(2, 2), new BigDecimal("0.10")), + F.asList(realType, 10.12f, decimalType(2, 1), overflowErr), + F.asList(realType, 0.12f, decimalType(1, 2), overflowErr), // Double - F.asList("DOUBLE", 100.0d, decimalType(3), new BigDecimal("100")), - F.asList("DOUBLE", 100.0d, decimalType(3, 0), new BigDecimal("100")), - F.asList("DOUBLE", 100.0d, decimalType(4, 1), new BigDecimal("100.0")), - F.asList("DOUBLE", 100.0d, decimalType(2, 0), overflowErr), - F.asList("DOUBLE", 0.1d, decimalType(1, 1), new BigDecimal("0.1")), - F.asList("DOUBLE", 0.1d, decimalType(2, 2), new BigDecimal("0.10")), - F.asList("DOUBLE", 10.12d, decimalType(2, 1), overflowErr), - F.asList("DOUBLE", 0.12d, decimalType(1, 2), overflowErr), + F.asList(doubleType, 100.0d, decimalType(3), new BigDecimal("100")), + F.asList(doubleType, 100.0d, decimalType(3, 0), new BigDecimal("100")), + F.asList(doubleType, 100.0d, decimalType(4, 1), new BigDecimal("100.0")), + F.asList(doubleType, 100.0d, decimalType(2, 0), overflowErr), + F.asList(doubleType, 0.1d, decimalType(1, 1), new BigDecimal("0.1")), + F.asList(doubleType, 0.1d, decimalType(2, 2), new BigDecimal("0.10")), + F.asList(doubleType, 10.12d, decimalType(2, 1), overflowErr), + F.asList(doubleType, 0.12d, decimalType(1, 2), overflowErr), // Decimal - F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), - F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(3), new BigDecimal("100")), - F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), - F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), - F.asList("DECIMAL(3)", new BigDecimal("100"), decimalType(2, 0), overflowErr), - F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), - F.asList("DECIMAL(4, 2)", new BigDecimal("10.12"), decimalType(2, 1), overflowErr), - F.asList("DECIMAL(2, 2)", new BigDecimal("0.12"), decimalType(1, 2), overflowErr), - F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) + F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(3), new BigDecimal("100")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), + F.asList(decimalType(3), new BigDecimal("100"), decimalType(2, 0), overflowErr), + F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), + F.asList(decimalType(4, 2), new BigDecimal("10.12"), decimalType(2, 1), overflowErr), + F.asList(decimalType(2, 2), new BigDecimal("0.12"), decimalType(1, 2), overflowErr), + F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) ); } From fe07143433cc669ee8f448f862aa610645048ab3 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 18 Oct 2024 12:34:07 +0300 Subject: [PATCH 11/43] review fix --- .../calcite/integration/DataTypesTest.java | 152 ++++++++---------- 1 file changed, 64 insertions(+), 88 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index c24187f6b06ab..65efa507f9572 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -23,10 +23,8 @@ import java.util.UUID; import java.util.stream.Collectors; import com.google.common.collect.ImmutableSet; -import org.apache.calcite.rel.type.RelDataType; import org.apache.calcite.runtime.CalciteException; import org.apache.calcite.sql.type.SqlTypeName; -import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.calcite.tools.FrameworkConfig; import org.apache.calcite.tools.Frameworks; import org.apache.ignite.IgniteCache; @@ -35,8 +33,6 @@ import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.calcite.exec.exp.IgniteSqlFunctions; import org.apache.ignite.internal.processors.query.calcite.hint.HintDefinition; -import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; -import org.apache.ignite.internal.processors.query.calcite.util.Commons; import org.apache.ignite.internal.util.typedef.F; import org.junit.Test; @@ -657,9 +653,9 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { for (List params : numericsToCast()) { assert params.size() == 4 : "Wrong params lenght: " + params.size(); - RelDataType inputType = (RelDataType)params.get(0); + String inputType = params.get(0).toString(); Object inputVal = params.get(1); - RelDataType targetType = (RelDataType)params.get(2); + String targetType = params.get(2).toString(); Object expectedRes = params.get(3); log.info("Params: inputType=" + inputType + ", inputValue=" + inputVal + ", targetType=" + targetType @@ -689,117 +685,97 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { } /** */ - private static String asLiteral(Object val, RelDataType type) { - return SqlTypeUtil.isCharacter(type) ? String.format("'%s'", val) : String.valueOf(val); + private static String asLiteral(Object val, String type) { + return type.equalsIgnoreCase("VARCHAR") ? String.format("'%s'", val) : String.valueOf(val); } /** */ private static List> numericsToCast() { - IgniteTypeFactory tf = Commons.typeFactory(); - - RelDataType varcharType = tf.createSqlType(SqlTypeName.VARCHAR); - RelDataType tinyIntType = tf.createSqlType(SqlTypeName.TINYINT); - RelDataType smallIntType = tf.createSqlType(SqlTypeName.SMALLINT); - RelDataType integerType = tf.createSqlType(SqlTypeName.INTEGER); - RelDataType bigintType = tf.createSqlType(SqlTypeName.BIGINT); - RelDataType realType = tf.createSqlType(SqlTypeName.REAL); - RelDataType doubleType = tf.createSqlType(SqlTypeName.DOUBLE); - Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_OVERFLOW_ERROR); Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) return F.>asList( // String - F.asList(varcharType, "100", decimalType(3), new BigDecimal("100")), - F.asList(varcharType, "100", decimalType(3, 0), new BigDecimal("100")), - F.asList(varcharType, "100", decimalType(4, 1), new BigDecimal("100.0")), - F.asList(varcharType, "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(varcharType, "100.16", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(varcharType, "-100.16", decimalType(5, 1), new BigDecimal("-100.1")), - F.asList(varcharType, "lame", decimalType(5, 1), numFormatErr), - F.asList(varcharType, "12345", decimalType(5, 1), overflowErr), - F.asList(varcharType, "1234", decimalType(5, 1), new BigDecimal("1234.0")), - F.asList(varcharType, "100.12", decimalType(1, 0), overflowErr), - F.asList(varcharType, "100", decimalType(2, 0), overflowErr), + F.asList("VARCHAR", "100", "DECIMAL(3)", new BigDecimal("100")), + F.asList("VARCHAR", "100", "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("VARCHAR", "100", "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("VARCHAR", "100.12", "DECIMAL(5, 1)", new BigDecimal("100.1")), + F.asList("VARCHAR", "100.16", "DECIMAL(5, 1)", new BigDecimal("100.1")), + F.asList("VARCHAR", "-100.16", "DECIMAL(5, 1)", new BigDecimal("-100.1")), + F.asList("VARCHAR", "lame", "DECIMAL(5, 1)", numFormatErr), + F.asList("VARCHAR", "12345", "DECIMAL(5, 1)", overflowErr), + F.asList("VARCHAR", "1234", "DECIMAL(5, 1)", new BigDecimal("1234.0")), + F.asList("VARCHAR", "100.12", "DECIMAL(1, 0)", overflowErr), + F.asList("VARCHAR", "100", "DECIMAL(2, 0)", overflowErr), // Numeric - F.asList(decimalType(4), "100", decimalType(3), new BigDecimal("100")), - F.asList(decimalType(4), "100", decimalType(3, 0), new BigDecimal("100")), - F.asList(decimalType(4), "100.12", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(decimalType(4), "100.12", decimalType(5, 0), new BigDecimal("100")), - F.asList(decimalType(4), "100.16", decimalType(5, 1), new BigDecimal("100.1")), - F.asList(decimalType(4), "-100.16", decimalType(5, 1), new BigDecimal("-100.1")), - F.asList(decimalType(4), "100", decimalType(2, 0), overflowErr), - F.asList(decimalType(4), "100.12", decimalType(5, 2), new BigDecimal("100.12")), + F.asList("DECIMAL(4)", "100", "DECIMAL(3)", new BigDecimal("100")), + F.asList("DECIMAL(4)", "100", "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("DECIMAL(4)", "100.12", "DECIMAL(5, 1)", new BigDecimal("100.1")), + F.asList("DECIMAL(4)", "100.12", "DECIMAL(5, 0)", new BigDecimal("100")), + F.asList("DECIMAL(4)", "100.16", "DECIMAL(5, 1)", new BigDecimal("100.1")), + F.asList("DECIMAL(4)", "-100.16", "DECIMAL(5, 1)", new BigDecimal("-100.1")), + F.asList("DECIMAL(4)", "100", "DECIMAL(2, 0)", overflowErr), + F.asList("DECIMAL(4)", "100.12", "DECIMAL(5, 2)", new BigDecimal("100.12")), // Tinyint - F.asList(tinyIntType, (byte)100, decimalType(3), new BigDecimal("100")), - F.asList(tinyIntType, (byte)100, decimalType(3, 0), new BigDecimal("100")), - F.asList(tinyIntType, (byte)100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(tinyIntType, (byte)100, decimalType(2, 0), overflowErr), + F.asList("TINYINT", (byte)100, "DECIMAL(3)", new BigDecimal("100")), + F.asList("TINYINT", (byte)100, "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("TINYINT", (byte)100, "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("TINYINT", (byte)100, "DECIMAL(2, 0)", overflowErr), // Smallint - F.asList(smallIntType, (short)100, decimalType(3), new BigDecimal("100")), - F.asList(smallIntType, (short)100, decimalType(3, 0), new BigDecimal("100")), - F.asList(smallIntType, (short)100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(smallIntType, (short)100, decimalType(2, 0), overflowErr), + F.asList("SMALLINT", (short)100, "DECIMAL(3)", new BigDecimal("100")), + F.asList("SMALLINT", (short)100, "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("SMALLINT", (short)100, "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("SMALLINT", (short)100, "DECIMAL(2, 0)", overflowErr), // Integer - F.asList(integerType, 100, decimalType(3), new BigDecimal("100")), - F.asList(integerType, 100, decimalType(3, 0), new BigDecimal("100")), - F.asList(integerType, 100, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(integerType, 100, decimalType(2, 0), overflowErr), + F.asList("INTEGER", 100, "DECIMAL(3)", new BigDecimal("100")), + F.asList("INTEGER", 100, "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("INTEGER", 100, "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("INTEGER", 100, "DECIMAL(2, 0)", overflowErr), // Bigint - F.asList(bigintType, 100L, decimalType(3), new BigDecimal("100")), - F.asList(bigintType, 100L, decimalType(3, 0), new BigDecimal("100")), - F.asList(bigintType, 100L, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(bigintType, 100L, decimalType(2, 0), overflowErr), + F.asList("BIGINT", 100L, "DECIMAL(3)", new BigDecimal("100")), + F.asList("BIGINT", 100L, "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("BIGINT", 100L, "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("BIGINT", 100L, "DECIMAL(2, 0)", overflowErr), // Real - F.asList(realType, 100.0f, decimalType(3), new BigDecimal("100")), - F.asList(realType, 100.0f, decimalType(3, 0), new BigDecimal("100")), - F.asList(realType, 100.0f, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(realType, 100.0f, decimalType(2, 0), overflowErr), - F.asList(realType, 0.1f, decimalType(1, 1), new BigDecimal("0.1")), - F.asList(realType, 0.1f, decimalType(2, 2), new BigDecimal("0.10")), - F.asList(realType, 10.12f, decimalType(2, 1), overflowErr), - F.asList(realType, 0.12f, decimalType(1, 2), overflowErr), + F.asList("REAL", 100.0f, "DECIMAL(3)", new BigDecimal("100")), + F.asList("REAL", 100.0f, "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("REAL", 100.0f, "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("REAL", 100.0f, "DECIMAL(2, 0)", overflowErr), + F.asList("REAL", 0.1f, "DECIMAL(1, 1)", new BigDecimal("0.1")), + F.asList("REAL", 0.1f, "DECIMAL(2, 2)", new BigDecimal("0.10")), + F.asList("REAL", 10.12f, "DECIMAL(2, 1)", overflowErr), + F.asList("REAL", 0.12f, "DECIMAL(1, 2)", overflowErr), // Double - F.asList(doubleType, 100.0d, decimalType(3), new BigDecimal("100")), - F.asList(doubleType, 100.0d, decimalType(3, 0), new BigDecimal("100")), - F.asList(doubleType, 100.0d, decimalType(4, 1), new BigDecimal("100.0")), - F.asList(doubleType, 100.0d, decimalType(2, 0), overflowErr), - F.asList(doubleType, 0.1d, decimalType(1, 1), new BigDecimal("0.1")), - F.asList(doubleType, 0.1d, decimalType(2, 2), new BigDecimal("0.10")), - F.asList(doubleType, 10.12d, decimalType(2, 1), overflowErr), - F.asList(doubleType, 0.12d, decimalType(1, 2), overflowErr), + F.asList("DOUBLE", 100.0d, "DECIMAL(3)", new BigDecimal("100")), + F.asList("DOUBLE", 100.0d, "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("DOUBLE", 100.0d, "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("DOUBLE", 100.0d, "DECIMAL(2, 0)", overflowErr), + F.asList("DOUBLE", 0.1d, "DECIMAL(1, 1)", new BigDecimal("0.1")), + F.asList("DOUBLE", 0.1d, "DECIMAL(2, 2)", new BigDecimal("0.10")), + F.asList("DOUBLE", 10.12d, "DECIMAL(2, 1)", overflowErr), + F.asList("DOUBLE", 0.12d, "DECIMAL(1, 2)", overflowErr), // Decimal - F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(3), new BigDecimal("100")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(3, 0), new BigDecimal("100")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(4, 1), new BigDecimal("100.0")), - F.asList(decimalType(3), new BigDecimal("100"), decimalType(2, 0), overflowErr), - F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(2, 2), new BigDecimal("0.10")), - F.asList(decimalType(4, 2), new BigDecimal("10.12"), decimalType(2, 1), overflowErr), - F.asList(decimalType(2, 2), new BigDecimal("0.12"), decimalType(1, 2), overflowErr), - F.asList(decimalType(1, 1), new BigDecimal("0.1"), decimalType(1, 1), new BigDecimal("0.1")) + F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), "DECIMAL(1, 1)", new BigDecimal("0.1")), + F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(3)", new BigDecimal("100")), + F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(2, 0)", overflowErr), + F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), "DECIMAL(2, 2)", new BigDecimal("0.10")), + F.asList("DECIMAL(4, 2)", new BigDecimal("10.12"), "DECIMAL(2, 1)", overflowErr), + F.asList("DECIMAL(2, 2)", new BigDecimal("0.12"), "DECIMAL(1, 2)", overflowErr), + F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), "DECIMAL(1, 1)", new BigDecimal("0.1")) ); } - /** */ - private static RelDataType decimalType(int precision, int scale) { - return Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, precision, scale); - } - - /** */ - private static RelDataType decimalType(int precision) { - return Commons.typeFactory().createSqlType(SqlTypeName.DECIMAL, precision, RelDataType.SCALE_NOT_SPECIFIED); - } - /** */ @Test public void testFunctionArgsToNumericImplicitConversion() { From 0340a88c405639411c69a267f5eb277d1ac49a66 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 18 Oct 2024 14:27:10 +0300 Subject: [PATCH 12/43] review fix --- .../query/calcite/exec/exp/IgniteSqlFunctions.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java index a4371d4a2f2d4..d7ecedf0f0923 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java @@ -85,8 +85,10 @@ public static BigDecimal toBigDecimal(float val, int precision, int scale) { /** Removes redundant scale in case of default DECIMAL (without passed precision and scale). */ private static BigDecimal removeDefaultScale(int precision, int scale, BigDecimal val) { - if (precision == DFLT_NUM_PRECISION && scale == 0 && val.compareTo(val.setScale(0, NUMERIC_ROUNDING_MODE)) == 0) - return val.setScale(0, NUMERIC_ROUNDING_MODE); + BigDecimal unscaled; + + if (precision == DFLT_NUM_PRECISION && scale == 0 && val.compareTo(unscaled = val.setScale(0, NUMERIC_ROUNDING_MODE)) == 0) + return unscaled; return val; } @@ -137,7 +139,7 @@ public static BigDecimal toBigDecimal(Number val, int precision, int scale) { BigDecimal dec = convertToBigDecimal(val); - if (scale > precision || (dec.precision() - dec.scale() > precision - scale)) + if (scale > precision || (dec.precision() - dec.scale() > precision - scale && !dec.unscaledValue().equals(BigInteger.ZERO))) throw new IllegalArgumentException(NUMERIC_OVERFLOW_ERROR); return dec.setScale(scale, NUMERIC_ROUNDING_MODE); From 4896715567fa1325d1cf1622fb747792e12342bf Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Tue, 22 Oct 2024 01:43:42 +0300 Subject: [PATCH 13/43] fix --- .../calcite/integration/DataTypesTest.java | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 65efa507f9572..a577fa9f96e6e 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -669,7 +669,7 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { if (expectedRes instanceof Exception) assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage(), inputVal); else - assertQuery(qry).withParams(inputType).returns(expectedRes); + assertQuery(qry).withParams(inputVal).returns(expectedRes).check(); } else { String qry = precasted @@ -679,7 +679,7 @@ private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { if (expectedRes instanceof Exception) assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage()); else - assertQuery(qry).returns(expectedRes); + assertQuery(qry).returns(expectedRes).check(); } } } @@ -689,7 +689,7 @@ private static String asLiteral(Object val, String type) { return type.equalsIgnoreCase("VARCHAR") ? String.format("'%s'", val) : String.valueOf(val); } - /** */ + /** @return input type, input value, target type, expected result. */ private static List> numericsToCast() { Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_OVERFLOW_ERROR); Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); @@ -701,8 +701,8 @@ private static List> numericsToCast() { F.asList("VARCHAR", "100", "DECIMAL(3, 0)", new BigDecimal("100")), F.asList("VARCHAR", "100", "DECIMAL(4, 1)", new BigDecimal("100.0")), F.asList("VARCHAR", "100.12", "DECIMAL(5, 1)", new BigDecimal("100.1")), - F.asList("VARCHAR", "100.16", "DECIMAL(5, 1)", new BigDecimal("100.1")), - F.asList("VARCHAR", "-100.16", "DECIMAL(5, 1)", new BigDecimal("-100.1")), + F.asList("VARCHAR", "100.16", "DECIMAL(5, 1)", new BigDecimal("100.2")), + F.asList("VARCHAR", "-100.16", "DECIMAL(5, 1)", new BigDecimal("-100.2")), F.asList("VARCHAR", "lame", "DECIMAL(5, 1)", numFormatErr), F.asList("VARCHAR", "12345", "DECIMAL(5, 1)", overflowErr), F.asList("VARCHAR", "1234", "DECIMAL(5, 1)", new BigDecimal("1234.0")), @@ -710,14 +710,19 @@ private static List> numericsToCast() { F.asList("VARCHAR", "100", "DECIMAL(2, 0)", overflowErr), // Numeric - F.asList("DECIMAL(4)", "100", "DECIMAL(3)", new BigDecimal("100")), - F.asList("DECIMAL(4)", "100", "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("DECIMAL(4)", "100.12", "DECIMAL(5, 1)", new BigDecimal("100.1")), - F.asList("DECIMAL(4)", "100.12", "DECIMAL(5, 0)", new BigDecimal("100")), - F.asList("DECIMAL(4)", "100.16", "DECIMAL(5, 1)", new BigDecimal("100.1")), - F.asList("DECIMAL(4)", "-100.16", "DECIMAL(5, 1)", new BigDecimal("-100.1")), - F.asList("DECIMAL(4)", "100", "DECIMAL(2, 0)", overflowErr), - F.asList("DECIMAL(4)", "100.12", "DECIMAL(5, 2)", new BigDecimal("100.12")), + F.asList("DECIMAL(1, 1)", "0.1", "DECIMAL(1, 1)", new BigDecimal("0.1")), + F.asList("DECIMAL(3)", "100", "DECIMAL(3)", new BigDecimal("100")), + F.asList("DECIMAL(5, 2)", "100.16", "DECIMAL(4, 1)", new BigDecimal("100.2")), + F.asList("DECIMAL(5, 2)", "-100.16", "DECIMAL(4, 1)", new BigDecimal("-100.2")), + F.asList("DECIMAL(5, 2)", "100.16", "DECIMAL(5, 2)", new BigDecimal("100.16")), + F.asList("DECIMAL(5, 2)", "-100.16", "DECIMAL(5, 2)", new BigDecimal("-100.16")), + F.asList("DECIMAL(3)", "100", "DECIMAL(3, 0)", new BigDecimal("100")), + F.asList("DECIMAL(3)", "100", "DECIMAL(4, 1)", new BigDecimal("100.0")), + F.asList("DECIMAL(3)", "100", "DECIMAL(2, 0)", overflowErr), + F.asList("DECIMAL(1, 1)", "0.1", "DECIMAL(2, 2)", new BigDecimal("0.10")), + F.asList("DECIMAL(4, 2)", "10.12", "DECIMAL(2, 1)", overflowErr), + F.asList("DECIMAL(2, 2)", "0.12", "DECIMAL(1, 2)", overflowErr), + F.asList("DECIMAL(1, 1)", "0.1", "DECIMAL(1, 1)", new BigDecimal("0.1")), // Tinyint F.asList("TINYINT", (byte)100, "DECIMAL(3)", new BigDecimal("100")), From 394ee9c9be64afb50cc9c9757c82b1d7ecaeb134 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Tue, 22 Oct 2024 15:42:45 +0300 Subject: [PATCH 14/43] research --- .../exec/exp/ExpressionFactoryImpl.java | 6 +++- .../query/calcite/prepare/PlannerHelper.java | 2 ++ .../calcite/integration/DataTypesTest.java | 32 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java index 3b15b6831cc78..6ebb2ff5654e5 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java @@ -558,7 +558,11 @@ private Scalar compile(List nodes, RelDataType type, boolean biInParams Class clazz = biInParams ? BiScalar.class : SingleScalar.class; - return Commons.compile(clazz, Expressions.toString(F.asList(decl), "\n", false)); + String src = Expressions.toString(F.asList(decl), "\n", false); + + System.err.println("TEST | compile: \n" + src); + + return Commons.compile(clazz, src); } /** */ diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java index 7618793255f03..4cb794fb3b327 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java @@ -119,6 +119,8 @@ public static IgniteRel optimize(SqlNode sqlNode, IgnitePlanner planner, IgniteL if (sqlNode.isA(ImmutableSet.of(SqlKind.INSERT, SqlKind.UPDATE, SqlKind.MERGE))) igniteRel = new FixDependentModifyNodeShuttle().visit(igniteRel); + System.err.println("TEST | Plan:\n" + RelOptUtil.toString(igniteRel)); + return igniteRel; } catch (Throwable ex) { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index a577fa9f96e6e..2a23be7926422 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -349,6 +349,38 @@ public void testUnsupportedTypes() { "'TIMESTAMP WITH LOCAL TIME ZONE' is not supported."); } + /** + * Test cases for decimal literals. + */ + @Test + public void testDecimalLiteral() { + assumeNoTransactions(); + + sql("CREATE TABLE t(id integer primary key, int_col integer)"); + sql("insert into t values (1, 1)"); + +// // OK +// assertQuery("SELECT id from t where int_col=CAST('1' as INTEGER)").returns(1).check(); +// assertQuery("SELECT id from t where int_col=CAST('1' as TINYINT)").returns(1).check(); +// assertQuery("SELECT id from t where int_col=CAST(1 as TINYINT)").returns(1).check(); +// +// // OK +// assertThrows("SELECT id from t where int_col=CAST(11111 as TINYINT)", ArithmeticException.class, "overflow"); +// assertThrows("SELECT id from t where int_col=CAST('11111' as TINYINT)", ArithmeticException.class, "overflow"); +// assertThrows("SELECT id from t where int_col=11111::TINYINT", ArithmeticException.class, "overflow"); +// assertThrows("SELECT id from t where int_col='11111'::TINYINT", ArithmeticException.class, "overflow"); + + // Fails +// assertThrows("SELECT id from t where int_col=CAST(? as TINYINT)", ArithmeticException.class, "overflow", 1111); +// assertThrows("SELECT id from t where int_col=CAST(? as TINYINT)", ArithmeticException.class, "overflow", "1111"); +// assertThrows("SELECT id from t where int_col=?::TINYINT", ArithmeticException.class, "overflow", 1111); +// assertThrows("SELECT id from t where int_col=?::TINYINT", ArithmeticException.class, "overflow", "1111"); + + // Fails + assertQuery("SELECT id from t where int_col=CAST(? as INTEGER)").withParams('1').returns(1).check(); +// assertQuery("SELECT id from t where int_col=CAST(? as TINYINT)").withParams('1').returns((byte)1).check(); + } + /** Cache API - SQL API cross check. */ @Test public void testBinaryCache() { From c0fe9684276358515d31e0d308b2ec528dc29b4b Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 23 Oct 2024 18:18:16 +0300 Subject: [PATCH 15/43] research --- .../calcite/prepare/IgniteSqlValidator.java | 270 ++++++++++++++++-- .../query/calcite/util/IgniteResource.java | 4 + 2 files changed, 251 insertions(+), 23 deletions(-) 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 131031364b82f..fe79ab78671b3 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 @@ -21,9 +21,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import java.util.UUID; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.prepare.CalciteCatalogReader; import org.apache.calcite.prepare.Prepare; @@ -75,6 +79,7 @@ import org.apache.ignite.internal.util.typedef.F; import org.jetbrains.annotations.Nullable; +import static org.apache.calcite.sql.type.SqlTypeName.INTEGER; import static org.apache.calcite.util.Static.RESOURCE; /** Validator. */ @@ -102,8 +107,11 @@ public class IgniteSqlValidator extends SqlValidatorImpl { HUMAN_READABLE_ALIASES_FOR = Collections.unmodifiableSet(kinds); } - /** Dynamic parameters. */ - Object[] parameters; + /** Dynamic parameters with passed values by indexes. */ + @Nullable private Map dynParams; + + /** Detected dynamic parameter nodes. */ + @Nullable private Set dynParamNodes; /** * Creates a validator. @@ -118,7 +126,101 @@ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogRe IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters) { super(opTab, catalogReader, typeFactory, config); - this.parameters = parameters; + if (!F.isEmpty(parameters)) { + dynParams = new HashMap<>(parameters.length); + + for (int i = 0; i < parameters.length; ++i) + dynParams.put(i, new DynamicParameterHolder(parameters[i])); + } + } + + /** {@inheritDoc} */ + @Override public SqlNode validate(SqlNode topNode) { + SqlNode node = super.validate(topNode); + + validateDynamicParameters(); + + return node; + } + + /** */ + private Map dynamicParameters() { + if (dynParams == null) + dynParams = new HashMap<>(); + + return dynParams; + } + + /** */ + @Nullable private DynamicParameterHolder dynamicParamater(int idx, boolean create) { + assert idx >= 0; + + if (create) + return dynamicParameters().computeIfAbsent(idx, idx0 -> new DynamicParameterHolder()); + + if (F.isEmpty(dynParams)) + return null; + + return dynParams.get(idx); + } + + /** */ + private void validateDynamicParameters() { + if (!F.isEmpty(dynParams)) { + dynParams.forEach((idx, pHolder) -> { + assert pHolder != null && pHolder.type != null && pHolder.node != null : "Dynamic parameter has not been validated: " + idx; + + if (pHolder.type == unknownType) { + throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation( + "Unable to determine type of a dynamic parameter #" + (idx + 1))); + } + }); + } + + if (F.isEmpty(dynParamNodes)) + return; + + String errMsg = null; + + for (SqlDynamicParam node : dynParamNodes) { + DynamicParameterHolder val = dynamicParamater(node.getIndex(), false); + + // TODO: no value passed err? + if (val == null || !val.hasValue) + continue; + + RelDataType valType = deriveTypeFromDynamicParamValue(val.val); + RelDataType derivedType = getValidatedNodeTypeIfKnown(node); + RelDataType paramType = val.type; + + if (!SqlTypeUtil.equalSansNullability(derivedType, valType)) { + errMsg = String.format( + "Type of dynamic parameter #%d value type does not match. Expected: %s, derived: %s", + node.getIndex(), valType.getFullTypeString(), derivedType.getFullTypeString() + ); + } + else if (!Objects.equals(paramType, derivedType)) { + errMsg = String.format( + "Type of dynamic parameter node #%d does not match. Expected: %s derived: %s", + node.getIndex(), paramType.getFullTypeString(), derivedType != null ? derivedType.getFullTypeString() : null + ); + } + + if (errMsg != null) + throw newValidationError(node, IgniteResource.INSTANCE.dynamicParameterValidation(errMsg)); + } + } + + /** */ + private RelDataType deriveTypeFromDynamicParamValue(@Nullable Object val) { + IgniteTypeFactory tf = typeFactory(); + + if (val instanceof UUID) + return tf.createCustomType(UUID.class); + else if (val == null) + return tf.createSqlType(SqlTypeName.NULL); + else + return tf.toSql(tf.createType(val.getClass())); } /** {@inheritDoc} */ @@ -258,22 +360,46 @@ private void checkIntegerLimit(SqlNode n, String nodeName) { throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); } else if (n instanceof SqlDynamicParam) { - // will fail in params check. - if (F.isEmpty(parameters)) - return; + SqlDynamicParam dynamicParam = (SqlDynamicParam)n; + RelDataType intType = typeFactory.createSqlType(INTEGER); - int idx = ((SqlDynamicParam)n).getIndex(); + if (!unspecified(dynamicParam)) { + Object param = dynamicParameterValue(dynamicParam); - if (idx < parameters.length) { - Object param = parameters[idx]; - if (parameters[idx] instanceof Integer) { + if (param instanceof Integer) { if ((Integer)param < 0) throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); } + else { + String actualType = deriveDynamicParamType(dynamicParam).toString(); + + String expectedType = intType.toString(); + + String errMsg = String.format("Incorrect type of a dynamic parameter. Expected '%s' but got '%s'", + expectedType, actualType); + + throw newValidationError(n, IgniteResource.INSTANCE.dynamicParameterValidation(errMsg)); + } } + + // Dynamic parameters are nullable. + dynamicParameterType(dynamicParam, typeFactory.createTypeWithNullability(intType, true)); } } + /** + * @returns Value of the dynamic parameter. + * @throws {@link IllegalArgumentException} if the value is not specified. + */ + @Nullable private Object dynamicParameterValue(SqlDynamicParam dynamicParam) { + DynamicParameterHolder valHolder = dynamicParamater(dynamicParam.getIndex(), true); + + if (!valHolder.hasValue) + throw new IllegalArgumentException(String.format("Value of dynamic parameter#%d is not specified", dynamicParam.getIndex())); + + return valHolder.val; + } + /** {@inheritDoc} */ @Override public void validateCall(SqlCall call, SqlValidatorScope scope) { if (call.getKind() == SqlKind.AS) { @@ -290,6 +416,73 @@ else if (call.getKind() == SqlKind.CAST) { super.validateCall(call, scope); } + /** {@inheritDoc} */ + @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { + if (expr instanceof SqlDynamicParam) + return deriveDynamicParamType((SqlDynamicParam)expr); + + return super.deriveType(scope, expr); + } + + /** Derives the type of the given dynamic parameter. */ + private RelDataType deriveDynamicParamType(SqlDynamicParam dynamicParam) { + if (dynParamNodes == null) + dynParamNodes = new HashSet<>(); + + dynParamNodes.add(dynamicParam); + + if (unspecified(dynamicParam)) { + RelDataType validatedNodeType = getValidatedNodeTypeIfKnown(dynamicParam); + + if (validatedNodeType == null) { + dynamicParameterType(dynamicParam, unknownType); + + return unknownType; + } + else { + dynamicParameterType(dynamicParam, validatedNodeType); + + return validatedNodeType; + } + } + else { + Object val = dynamicParameterValue(dynamicParam); + + RelDataType paramType = deriveTypeFromDynamicParamValue(val); + + // Dynamic parameters are always nullable. + RelDataType nullableType = typeFactory.createTypeWithNullability(paramType, true); + + dynamicParameterType(dynamicParam, nullableType); + + return nullableType; + } + } + + /** returns {@code True} if {@code param} has no value set. */ + public boolean unspecified(SqlDynamicParam param) { + return !dynamicParamater(param.getIndex(), true).hasValue; + } + + /** */ + private void dynamicParameterType(SqlDynamicParam node, RelDataType type) { + DynamicParameterHolder pHolder = dynamicParamater(node.getIndex(), true); + + dynamicParameterType(pHolder, node, type); + } + + /** */ + private void dynamicParameterType(DynamicParameterHolder pHolder, SqlDynamicParam node, RelDataType type) { + assert pHolder.node == null : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); + assert pHolder.type == null : "Type is already set for a dynamic parameter #" + node.getIndex(); + + pHolder.type = type; + + pHolder.node = node; + + setValidatedNodeType(node, type); + } + /** {@inheritDoc} */ @Override public String deriveAlias(SqlNode node, int ordinal) { if (node.isA(HUMAN_READABLE_ALIASES_FOR)) { @@ -578,32 +771,34 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { /** * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index - * is actual to {@link #parameters}. + * is actual to {@link #dynParams}. * * @return {@code True} if a new type was set. {@code False} otherwise. */ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length) + if (!(node instanceof SqlDynamicParam)) return false; - Object val = parameters[((SqlDynamicParam)node).getIndex()]; + SqlDynamicParam dnode = (SqlDynamicParam)node; - if (val == null) { - if (inferredType.equals(unknownType)) { - setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL)); + DynamicParameterHolder pHolder = dynamicParamater(dnode.getIndex(), true); - return true; - } + IgniteTypeFactory tf = typeFactory(); - return false; - } + RelDataType valType = pHolder.val == null ? + tf.createSqlType(SqlTypeName.NULL) + : tf.toSql(typeFactory().createType(pHolder.val.getClass())); + + if (inferredType.equals(unknownType) && pHolder.val == null) { + dynamicParameterType(pHolder, dnode, valType); - RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass())); + return true; + } if (SqlTypeUtil.equalSansNullability(valType, inferredType)) return false; - assert !unknownType.equals(valType); + assert !unknownType.equals(valType) : "Failed to guess type of a dynamic parameter."; if (valType.getFamily().equals(inferredType.getFamily())) { RelDataType leastRestrictive = typeFactory().leastRestrictive(F.asList(inferredType, valType)); @@ -616,7 +811,7 @@ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { else if (!unknownType.equals(inferredType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) return false; - setValidatedNodeType(node, valType); + dynamicParameterType(pHolder, dnode, valType); return true; } @@ -638,4 +833,33 @@ else if (!unknownType.equals(inferredType) && SqlTypeUtil.canCastFrom(valType, i return super.resolveLiteral(literal); } + + /** */ + private static final class DynamicParameterHolder { + /** */ + @Nullable private final Object val; + + /** */ + private final boolean hasValue; + + /** */ + private SqlDynamicParam node; + + /** */ + private RelDataType type; + + /** */ + private DynamicParameterHolder(@Nullable Object val) { + this.val = val; + + this.hasValue = true; + } + + /** */ + private DynamicParameterHolder() { + this.val = null; + + this.hasValue = false; + } + } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java index f4d7181c6fd3a..38620fd452edc 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java @@ -80,4 +80,8 @@ public interface IgniteResource { /** */ @Resources.BaseMessage("Operator ''CAST'' supports only the parameters: value and target type.") Resources.ExInst invalidCastParameters(); + + /** */ + @Resources.BaseMessage("Dynamic parameter validation error. ''{0}'") + Resources.ExInst dynamicParameterValidation(String value); } From 8d009cc886df614e2ec4bd713f6014eaf13cbd74 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 23 Oct 2024 20:15:15 +0300 Subject: [PATCH 16/43] alpha --- .../calcite/exec/exp/IgniteSqlFunctions.java | 91 +++---- .../calcite/prepare/IgniteSqlValidator.java | 165 +++++------- .../query/calcite/util/IgniteResource.java | 2 +- .../calcite/integration/DataTypesTest.java | 240 +----------------- 4 files changed, 97 insertions(+), 401 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java index d7ecedf0f0923..5c6130af64e30 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteSqlFunctions.java @@ -42,15 +42,6 @@ * Ignite SQL functions. */ public class IgniteSqlFunctions { - /** */ - public static final String NUMERIC_OVERFLOW_ERROR = "Numeric field overflow."; - - /** */ - private static final int DFLT_NUM_PRECISION = IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL); - - /** */ - private static final RoundingMode NUMERIC_ROUNDING_MODE = RoundingMode.HALF_UP; - /** * Default constructor. */ @@ -73,44 +64,46 @@ public static String toString(BigDecimal x) { return x == null ? null : x.toPlainString(); } + /** */ + private static BigDecimal setScale(int precision, int scale, BigDecimal decimal) { + return precision == IgniteTypeSystem.INSTANCE.getDefaultPrecision(SqlTypeName.DECIMAL) + ? decimal : decimal.setScale(scale, RoundingMode.HALF_UP); + } + /** CAST(DOUBLE AS DECIMAL). */ public static BigDecimal toBigDecimal(double val, int precision, int scale) { - return removeDefaultScale(precision, scale, toBigDecimal(BigDecimal.valueOf(val), precision, scale)); + BigDecimal decimal = BigDecimal.valueOf(val); + return setScale(precision, scale, decimal); } /** CAST(FLOAT AS DECIMAL). */ public static BigDecimal toBigDecimal(float val, int precision, int scale) { - return removeDefaultScale(precision, scale, toBigDecimal(BigDecimal.valueOf(val), precision, scale)); - } - - /** Removes redundant scale in case of default DECIMAL (without passed precision and scale). */ - private static BigDecimal removeDefaultScale(int precision, int scale, BigDecimal val) { - BigDecimal unscaled; - - if (precision == DFLT_NUM_PRECISION && scale == 0 && val.compareTo(unscaled = val.setScale(0, NUMERIC_ROUNDING_MODE)) == 0) - return unscaled; - - return val; + BigDecimal decimal = new BigDecimal(String.valueOf(val)); + return setScale(precision, scale, decimal); } /** CAST(java long AS DECIMAL). */ public static BigDecimal toBigDecimal(long val, int precision, int scale) { - return toBigDecimal(BigDecimal.valueOf(val), precision, scale); + BigDecimal decimal = BigDecimal.valueOf(val); + return setScale(precision, scale, decimal); } /** CAST(INT AS DECIMAL). */ public static BigDecimal toBigDecimal(int val, int precision, int scale) { - return toBigDecimal(BigDecimal.valueOf(val), precision, scale); + BigDecimal decimal = new BigDecimal(val); + return setScale(precision, scale, decimal); } /** CAST(java short AS DECIMAL). */ public static BigDecimal toBigDecimal(short val, int precision, int scale) { - return toBigDecimal(BigDecimal.valueOf(val), precision, scale); + BigDecimal decimal = new BigDecimal(String.valueOf(val)); + return setScale(precision, scale, decimal); } /** CAST(java byte AS DECIMAL). */ public static BigDecimal toBigDecimal(byte val, int precision, int scale) { - return toBigDecimal(BigDecimal.valueOf(val), precision, scale); + BigDecimal decimal = new BigDecimal(String.valueOf(val)); + return setScale(precision, scale, decimal); } /** CAST(BOOL AS DECIMAL). */ @@ -122,45 +115,21 @@ public static BigDecimal toBigDecimal(boolean val, int precision, int scale) { public static BigDecimal toBigDecimal(String s, int precision, int scale) { if (s == null) return null; - - return toBigDecimal(new BigDecimal(s.trim()), precision, scale); + BigDecimal decimal = new BigDecimal(s.trim()); + return setScale(precision, scale, decimal); } - /** Converts {@code val} to a {@link BigDecimal} with the given {@code precision} and {@code scale}. */ - public static BigDecimal toBigDecimal(Number val, int precision, int scale) { - assert precision > 0 : "Invalid precision: " + precision; - assert scale >= 0 : "Invalid scale: " + scale; - - if (val == null) + /** CAST(REAL AS DECIMAL). */ + public static BigDecimal toBigDecimal(Number num, int precision, int scale) { + if (num == null) return null; - - if (precision == DFLT_NUM_PRECISION) - return convertToBigDecimal(val); - - BigDecimal dec = convertToBigDecimal(val); - - if (scale > precision || (dec.precision() - dec.scale() > precision - scale && !dec.unscaledValue().equals(BigInteger.ZERO))) - throw new IllegalArgumentException(NUMERIC_OVERFLOW_ERROR); - - return dec.setScale(scale, NUMERIC_ROUNDING_MODE); - } - - /** */ - private static BigDecimal convertToBigDecimal(Number value) { - BigDecimal dec; - - if (value instanceof Float) - dec = BigDecimal.valueOf(value.floatValue()); - else if (value instanceof Double) - dec = BigDecimal.valueOf(value.doubleValue()); - else if (value instanceof BigDecimal) - dec = (BigDecimal)value; - else if (value instanceof BigInteger) - dec = new BigDecimal((BigInteger)value); - else - dec = new BigDecimal(value.longValue()); - - return dec; + // There are some values of "long" that cannot be represented as "double". + // Not so "int". If it isn't a long, go straight to double. + BigDecimal decimal = num instanceof BigDecimal ? ((BigDecimal)num) + : num instanceof BigInteger ? new BigDecimal((BigInteger)num) + : num instanceof Long ? new BigDecimal(num.longValue()) + : BigDecimal.valueOf(num.doubleValue()); + return setScale(precision, scale, decimal); } /** Cast object depending on type to DECIMAL. */ 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 fe79ab78671b3..c264467d9632e 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 @@ -107,7 +107,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl { HUMAN_READABLE_ALIASES_FOR = Collections.unmodifiableSet(kinds); } - /** Dynamic parameters with passed values by indexes. */ + /** Validated dynamic parameters with passed values by indexes. */ @Nullable private Map dynParams; /** Detected dynamic parameter nodes. */ @@ -144,37 +144,21 @@ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogRe } /** */ - private Map dynamicParameters() { - if (dynParams == null) - dynParams = new HashMap<>(); - - return dynParams; - } - - /** */ - @Nullable private DynamicParameterHolder dynamicParamater(int idx, boolean create) { + @Nullable private DynamicParameterHolder dynamicParamater(int idx) { assert idx >= 0; - if (create) - return dynamicParameters().computeIfAbsent(idx, idx0 -> new DynamicParameterHolder()); - - if (F.isEmpty(dynParams)) - return null; + if (dynParams == null) + dynParams = new HashMap<>(); - return dynParams.get(idx); + return dynParams.computeIfAbsent(idx, idx0 -> new DynamicParameterHolder()); } /** */ private void validateDynamicParameters() { - if (!F.isEmpty(dynParams)) { - dynParams.forEach((idx, pHolder) -> { - assert pHolder != null && pHolder.type != null && pHolder.node != null : "Dynamic parameter has not been validated: " + idx; + if (F.isEmpty(dynParams)) { + assert F.isEmpty(dynParamNodes); - if (pHolder.type == unknownType) { - throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation( - "Unable to determine type of a dynamic parameter #" + (idx + 1))); - } - }); + return; } if (F.isEmpty(dynParamNodes)) @@ -183,26 +167,30 @@ private void validateDynamicParameters() { String errMsg = null; for (SqlDynamicParam node : dynParamNodes) { - DynamicParameterHolder val = dynamicParamater(node.getIndex(), false); + DynamicParameterHolder pHolder = dynParams.get(node.getIndex()); - // TODO: no value passed err? - if (val == null || !val.hasValue) + assert pHolder != null && pHolder.type != null && pHolder.node != null : "Dynamic parameter has not been validated. Idx: " + node.getIndex(); + + if (pHolder.type == unknownType) { + throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation( + "Unable to determine type of a dynamic parameter #" + (node.getIndex() + 1))); + } + + if (!pHolder.hasValue) continue; - RelDataType valType = deriveTypeFromDynamicParamValue(val.val); - RelDataType derivedType = getValidatedNodeTypeIfKnown(node); - RelDataType paramType = val.type; + RelDataType knownType = getValidatedNodeTypeIfKnown(node); - if (!SqlTypeUtil.equalSansNullability(derivedType, valType)) { + if (!SqlTypeUtil.equalSansNullability(knownType, pHolder.type)) { errMsg = String.format( "Type of dynamic parameter #%d value type does not match. Expected: %s, derived: %s", - node.getIndex(), valType.getFullTypeString(), derivedType.getFullTypeString() + node.getIndex(), pHolder.type.getFullTypeString(), knownType.getFullTypeString() ); } - else if (!Objects.equals(paramType, derivedType)) { + else if (!Objects.equals(knownType, pHolder.type)) { errMsg = String.format( "Type of dynamic parameter node #%d does not match. Expected: %s derived: %s", - node.getIndex(), paramType.getFullTypeString(), derivedType != null ? derivedType.getFullTypeString() : null + node.getIndex(), pHolder.type.getFullTypeString(), knownType != null ? knownType.getFullTypeString() : null ); } @@ -212,13 +200,13 @@ else if (!Objects.equals(paramType, derivedType)) { } /** */ - private RelDataType deriveTypeFromDynamicParamValue(@Nullable Object val) { + private RelDataType deriveTypeFromValue(@Nullable Object val) { IgniteTypeFactory tf = typeFactory(); - if (val instanceof UUID) - return tf.createCustomType(UUID.class); - else if (val == null) + if (val == null) return tf.createSqlType(SqlTypeName.NULL); + else if (val instanceof UUID) + return tf.createCustomType(UUID.class); else return tf.toSql(tf.createType(val.getClass())); } @@ -382,8 +370,10 @@ else if (n instanceof SqlDynamicParam) { } } + DynamicParameterHolder pHolder = dynamicParamater(dynamicParam.getIndex()); + // Dynamic parameters are nullable. - dynamicParameterType(dynamicParam, typeFactory.createTypeWithNullability(intType, true)); + dynamicParameterType(pHolder, dynamicParam, typeFactory.createTypeWithNullability(intType, true)); } } @@ -392,7 +382,7 @@ else if (n instanceof SqlDynamicParam) { * @throws {@link IllegalArgumentException} if the value is not specified. */ @Nullable private Object dynamicParameterValue(SqlDynamicParam dynamicParam) { - DynamicParameterHolder valHolder = dynamicParamater(dynamicParam.getIndex(), true); + DynamicParameterHolder valHolder = dynamicParamater(dynamicParam.getIndex()); if (!valHolder.hasValue) throw new IllegalArgumentException(String.format("Value of dynamic parameter#%d is not specified", dynamicParam.getIndex())); @@ -419,62 +409,51 @@ else if (call.getKind() == SqlKind.CAST) { /** {@inheritDoc} */ @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { if (expr instanceof SqlDynamicParam) - return deriveDynamicParamType((SqlDynamicParam)expr); + return deriveDynamicParamType((SqlDynamicParam)expr).type; return super.deriveType(scope, expr); } - /** Derives the type of the given dynamic parameter. */ - private RelDataType deriveDynamicParamType(SqlDynamicParam dynamicParam) { + /** + * Derives the type of the given dynamic parameter. + * + * @return {@link DynamicParameterHolder} related to {@code node} with the derived type. + */ + private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { if (dynParamNodes == null) dynParamNodes = new HashSet<>(); - dynParamNodes.add(dynamicParam); + DynamicParameterHolder pHolder = dynamicParamater(node.getIndex()); - if (unspecified(dynamicParam)) { - RelDataType validatedNodeType = getValidatedNodeTypeIfKnown(dynamicParam); + if (pHolder.type != null && pHolder.hasValue) { + assert dynParamNodes.contains(node); + assert node.equals(pHolder.node); - if (validatedNodeType == null) { - dynamicParameterType(dynamicParam, unknownType); + setValidatedNodeType(node, pHolder.type); + } + else { + dynParamNodes.add(node); - return unknownType; - } + if (pHolder.hasValue) + dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(deriveTypeFromValue(pHolder.val), true)); else { - dynamicParameterType(dynamicParam, validatedNodeType); + RelDataType validatedNodeType = getValidatedNodeTypeIfKnown(node); - return validatedNodeType; + dynamicParameterType(pHolder, node, validatedNodeType == null ? unknownType : validatedNodeType); } } - else { - Object val = dynamicParameterValue(dynamicParam); - - RelDataType paramType = deriveTypeFromDynamicParamValue(val); - - // Dynamic parameters are always nullable. - RelDataType nullableType = typeFactory.createTypeWithNullability(paramType, true); - dynamicParameterType(dynamicParam, nullableType); - - return nullableType; - } + return pHolder; } /** returns {@code True} if {@code param} has no value set. */ public boolean unspecified(SqlDynamicParam param) { - return !dynamicParamater(param.getIndex(), true).hasValue; - } - - /** */ - private void dynamicParameterType(SqlDynamicParam node, RelDataType type) { - DynamicParameterHolder pHolder = dynamicParamater(node.getIndex(), true); - - dynamicParameterType(pHolder, node, type); + return !dynamicParamater(param.getIndex()).hasValue; } /** */ private void dynamicParameterType(DynamicParameterHolder pHolder, SqlDynamicParam node, RelDataType type) { - assert pHolder.node == null : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); - assert pHolder.type == null : "Type is already set for a dynamic parameter #" + node.getIndex(); + assert pHolder.node == null || node.equals(pHolder.node) : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); pHolder.type = type; @@ -722,10 +701,9 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (inferDynamicParamType(inferredType, node)) - return; - - if (node instanceof SqlCall) { + if (node instanceof SqlDynamicParam) + inferDynamicParamType(inferredType, (SqlDynamicParam)node); + else if (node instanceof SqlCall) { final SqlValidatorScope newScope = scopes.get(node); if (newScope != null) @@ -772,31 +750,14 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { /** * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index * is actual to {@link #dynParams}. - * - * @return {@code True} if a new type was set. {@code False} otherwise. */ - private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - if (!(node instanceof SqlDynamicParam)) - return false; + private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam node) { + DynamicParameterHolder pHolder = deriveDynamicParamType(node); - SqlDynamicParam dnode = (SqlDynamicParam)node; - - DynamicParameterHolder pHolder = dynamicParamater(dnode.getIndex(), true); - - IgniteTypeFactory tf = typeFactory(); - - RelDataType valType = pHolder.val == null ? - tf.createSqlType(SqlTypeName.NULL) - : tf.toSql(typeFactory().createType(pHolder.val.getClass())); - - if (inferredType.equals(unknownType) && pHolder.val == null) { - dynamicParameterType(pHolder, dnode, valType); - - return true; - } + RelDataType valType = pHolder.type; if (SqlTypeUtil.equalSansNullability(valType, inferredType)) - return false; + return; assert !unknownType.equals(valType) : "Failed to guess type of a dynamic parameter."; @@ -806,14 +767,12 @@ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { assert leastRestrictive != null; if (inferredType == leastRestrictive) - return false; + return; } else if (!unknownType.equals(inferredType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) - return false; - - dynamicParameterType(pHolder, dnode, valType); + return; - return true; + dynamicParameterType(pHolder, node, valType); } /** {@inheritDoc} */ diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java index 38620fd452edc..4a0fba5967929 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java @@ -82,6 +82,6 @@ public interface IgniteResource { Resources.ExInst invalidCastParameters(); /** */ - @Resources.BaseMessage("Dynamic parameter validation error. ''{0}'") + @Resources.BaseMessage("Dynamic parameter validation error. ''{0}''") Resources.ExInst dynamicParameterValidation(String value); } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 2a23be7926422..2d005ef7be2a3 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -18,6 +18,7 @@ package org.apache.ignite.internal.processors.query.calcite.integration; import java.math.BigDecimal; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -31,7 +32,6 @@ import org.apache.ignite.IgniteException; import org.apache.ignite.cache.QueryEntity; import org.apache.ignite.internal.processors.query.IgniteSQLException; -import org.apache.ignite.internal.processors.query.calcite.exec.exp.IgniteSqlFunctions; import org.apache.ignite.internal.processors.query.calcite.hint.HintDefinition; import org.apache.ignite.internal.util.typedef.F; import org.junit.Test; @@ -354,31 +354,11 @@ public void testUnsupportedTypes() { */ @Test public void testDecimalLiteral() { - assumeNoTransactions(); - sql("CREATE TABLE t(id integer primary key, int_col integer)"); sql("insert into t values (1, 1)"); -// // OK -// assertQuery("SELECT id from t where int_col=CAST('1' as INTEGER)").returns(1).check(); -// assertQuery("SELECT id from t where int_col=CAST('1' as TINYINT)").returns(1).check(); -// assertQuery("SELECT id from t where int_col=CAST(1 as TINYINT)").returns(1).check(); -// -// // OK -// assertThrows("SELECT id from t where int_col=CAST(11111 as TINYINT)", ArithmeticException.class, "overflow"); -// assertThrows("SELECT id from t where int_col=CAST('11111' as TINYINT)", ArithmeticException.class, "overflow"); -// assertThrows("SELECT id from t where int_col=11111::TINYINT", ArithmeticException.class, "overflow"); -// assertThrows("SELECT id from t where int_col='11111'::TINYINT", ArithmeticException.class, "overflow"); - - // Fails -// assertThrows("SELECT id from t where int_col=CAST(? as TINYINT)", ArithmeticException.class, "overflow", 1111); -// assertThrows("SELECT id from t where int_col=CAST(? as TINYINT)", ArithmeticException.class, "overflow", "1111"); -// assertThrows("SELECT id from t where int_col=?::TINYINT", ArithmeticException.class, "overflow", 1111); -// assertThrows("SELECT id from t where int_col=?::TINYINT", ArithmeticException.class, "overflow", "1111"); - // Fails assertQuery("SELECT id from t where int_col=CAST(? as INTEGER)").withParams('1').returns(1).check(); -// assertQuery("SELECT id from t where int_col=CAST(? as TINYINT)").withParams('1').returns((byte)1).check(); } /** Cache API - SQL API cross check. */ @@ -498,7 +478,7 @@ public void testIsNotDistinctFromTypeConversion() { sql("INSERT INTO t1 VALUES (1, 1, null, '1'), (2, 2, 2, '22'), (3, 33, 3, null), (4, null, 4, '4')"); sql("INSERT INTO t2 VALUES (0, 0, 0, null, '0'), (11, null, 1, 1, '1'), (2, 2, 2, 2, '22'), (3, 3, null, 3, null)"); - for (HintDefinition hint : F.asList(HintDefinition.MERGE_JOIN, HintDefinition.NL_JOIN, HintDefinition.CNL_JOIN)) { + for (HintDefinition hint : Arrays.asList(HintDefinition.MERGE_JOIN, HintDefinition.NL_JOIN, HintDefinition.CNL_JOIN)) { String h = "/*+ " + hint.name() + " */ "; // Primary keys, indexed. @@ -606,217 +586,10 @@ public void testNumericConversion() { .check(); } - /** */ - @Test - public void testCoercionOfVarcharLiterals() { - assumeNoTransactions(); - - doTestCoercionOfVarchars(false); - } - - /** */ - @Test - public void testCoercionOfVarcharDynamicParameters() { - assumeNoTransactions(); - - doTestCoercionOfVarchars(true); - } - - /** */ - private void doTestCoercionOfVarchars(boolean dynamics) { - for (List params : varcharsToCoerce()) { - String val = params.get(0); - String type = params.get(1); - String result = params.get(2); - - if (dynamics) - assertQuery(String.format("SELECT CAST(? AS %s)", type)).withParams(val).returns(result).check(); - else - assertQuery(String.format("SELECT CAST('%s' AS %s)", val, type)).returns(result).check(); - } - } - - /** */ - private static List> varcharsToCoerce() { - return F.asList( - F.asList("abcde", "VARCHAR(3)", "abc"), - F.asList("abcde", "VARCHAR(5)", "abcde"), - F.asList("abcde", "VARCHAR(6)", "abcde"), - F.asList("abcde", "VARCHAR", "abcde"), - F.asList("abcde", "CHAR", "a"), - F.asList("abcde", "CHAR(3)", "abc") - ); - } - - /** */ - @Test - public void testCoercionOfNumericLiterals() { - assumeNoTransactions(); - - doTestCoercionOfNumerics(false, false); - } - - /** */ - @Test - public void testCoercionOfNumericLiteralsPrecasted() { - assumeNoTransactions(); - - doTestCoercionOfNumerics(false, true); - } - - /** */ - @Test - public void testCoercionOfNumericDynamicParameters() { - assumeNoTransactions(); - - doTestCoercionOfNumerics(true, false); - } - - /** */ - @Test - public void testCoercionOfNumericDynamicParametersPrecasted() { - assumeNoTransactions(); - - doTestCoercionOfNumerics(true, true); - } - - /** */ - private void doTestCoercionOfNumerics(boolean dynamic, boolean precasted) { - for (List params : numericsToCast()) { - assert params.size() == 4 : "Wrong params lenght: " + params.size(); - - String inputType = params.get(0).toString(); - Object inputVal = params.get(1); - String targetType = params.get(2).toString(); - Object expectedRes = params.get(3); - - log.info("Params: inputType=" + inputType + ", inputValue=" + inputVal + ", targetType=" + targetType - + ", expectedResult=" + expectedRes); - - if (dynamic) { - String qry = precasted - ? String.format("SELECT CAST(?::%s AS %s)", inputType, targetType) - : String.format("SELECT CAST(? AS %s)", targetType); - - if (expectedRes instanceof Exception) - assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage(), inputVal); - else - assertQuery(qry).withParams(inputVal).returns(expectedRes).check(); - } - else { - String qry = precasted - ? String.format("SELECT CAST(%s::%s AS %s)", asLiteral(inputVal, inputType), inputType, targetType) - : String.format("SELECT CAST(%s AS %s)", asLiteral(inputVal, inputType), targetType); - - if (expectedRes instanceof Exception) - assertThrows(qry, (Class)expectedRes.getClass(), ((Throwable)expectedRes).getMessage()); - else - assertQuery(qry).returns(expectedRes).check(); - } - } - } - - /** */ - private static String asLiteral(Object val, String type) { - return type.equalsIgnoreCase("VARCHAR") ? String.format("'%s'", val) : String.valueOf(val); - } - - /** @return input type, input value, target type, expected result. */ - private static List> numericsToCast() { - Exception overflowErr = new IllegalArgumentException(IgniteSqlFunctions.NUMERIC_OVERFLOW_ERROR); - Exception numFormatErr = new NumberFormatException("is neither a decimal digit number"); - - //noinspection RedundantTypeArguments (explicit type arguments speedup compilation and analysis time) - return F.>asList( - // String - F.asList("VARCHAR", "100", "DECIMAL(3)", new BigDecimal("100")), - F.asList("VARCHAR", "100", "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("VARCHAR", "100", "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("VARCHAR", "100.12", "DECIMAL(5, 1)", new BigDecimal("100.1")), - F.asList("VARCHAR", "100.16", "DECIMAL(5, 1)", new BigDecimal("100.2")), - F.asList("VARCHAR", "-100.16", "DECIMAL(5, 1)", new BigDecimal("-100.2")), - F.asList("VARCHAR", "lame", "DECIMAL(5, 1)", numFormatErr), - F.asList("VARCHAR", "12345", "DECIMAL(5, 1)", overflowErr), - F.asList("VARCHAR", "1234", "DECIMAL(5, 1)", new BigDecimal("1234.0")), - F.asList("VARCHAR", "100.12", "DECIMAL(1, 0)", overflowErr), - F.asList("VARCHAR", "100", "DECIMAL(2, 0)", overflowErr), - - // Numeric - F.asList("DECIMAL(1, 1)", "0.1", "DECIMAL(1, 1)", new BigDecimal("0.1")), - F.asList("DECIMAL(3)", "100", "DECIMAL(3)", new BigDecimal("100")), - F.asList("DECIMAL(5, 2)", "100.16", "DECIMAL(4, 1)", new BigDecimal("100.2")), - F.asList("DECIMAL(5, 2)", "-100.16", "DECIMAL(4, 1)", new BigDecimal("-100.2")), - F.asList("DECIMAL(5, 2)", "100.16", "DECIMAL(5, 2)", new BigDecimal("100.16")), - F.asList("DECIMAL(5, 2)", "-100.16", "DECIMAL(5, 2)", new BigDecimal("-100.16")), - F.asList("DECIMAL(3)", "100", "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("DECIMAL(3)", "100", "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("DECIMAL(3)", "100", "DECIMAL(2, 0)", overflowErr), - F.asList("DECIMAL(1, 1)", "0.1", "DECIMAL(2, 2)", new BigDecimal("0.10")), - F.asList("DECIMAL(4, 2)", "10.12", "DECIMAL(2, 1)", overflowErr), - F.asList("DECIMAL(2, 2)", "0.12", "DECIMAL(1, 2)", overflowErr), - F.asList("DECIMAL(1, 1)", "0.1", "DECIMAL(1, 1)", new BigDecimal("0.1")), - - // Tinyint - F.asList("TINYINT", (byte)100, "DECIMAL(3)", new BigDecimal("100")), - F.asList("TINYINT", (byte)100, "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("TINYINT", (byte)100, "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("TINYINT", (byte)100, "DECIMAL(2, 0)", overflowErr), - - // Smallint - F.asList("SMALLINT", (short)100, "DECIMAL(3)", new BigDecimal("100")), - F.asList("SMALLINT", (short)100, "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("SMALLINT", (short)100, "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("SMALLINT", (short)100, "DECIMAL(2, 0)", overflowErr), - - // Integer - F.asList("INTEGER", 100, "DECIMAL(3)", new BigDecimal("100")), - F.asList("INTEGER", 100, "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("INTEGER", 100, "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("INTEGER", 100, "DECIMAL(2, 0)", overflowErr), - - // Bigint - F.asList("BIGINT", 100L, "DECIMAL(3)", new BigDecimal("100")), - F.asList("BIGINT", 100L, "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("BIGINT", 100L, "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("BIGINT", 100L, "DECIMAL(2, 0)", overflowErr), - - // Real - F.asList("REAL", 100.0f, "DECIMAL(3)", new BigDecimal("100")), - F.asList("REAL", 100.0f, "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("REAL", 100.0f, "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("REAL", 100.0f, "DECIMAL(2, 0)", overflowErr), - F.asList("REAL", 0.1f, "DECIMAL(1, 1)", new BigDecimal("0.1")), - F.asList("REAL", 0.1f, "DECIMAL(2, 2)", new BigDecimal("0.10")), - F.asList("REAL", 10.12f, "DECIMAL(2, 1)", overflowErr), - F.asList("REAL", 0.12f, "DECIMAL(1, 2)", overflowErr), - - // Double - F.asList("DOUBLE", 100.0d, "DECIMAL(3)", new BigDecimal("100")), - F.asList("DOUBLE", 100.0d, "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("DOUBLE", 100.0d, "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("DOUBLE", 100.0d, "DECIMAL(2, 0)", overflowErr), - F.asList("DOUBLE", 0.1d, "DECIMAL(1, 1)", new BigDecimal("0.1")), - F.asList("DOUBLE", 0.1d, "DECIMAL(2, 2)", new BigDecimal("0.10")), - F.asList("DOUBLE", 10.12d, "DECIMAL(2, 1)", overflowErr), - F.asList("DOUBLE", 0.12d, "DECIMAL(1, 2)", overflowErr), - - // Decimal - F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), "DECIMAL(1, 1)", new BigDecimal("0.1")), - F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(3)", new BigDecimal("100")), - F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(3, 0)", new BigDecimal("100")), - F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(4, 1)", new BigDecimal("100.0")), - F.asList("DECIMAL(3)", new BigDecimal("100"), "DECIMAL(2, 0)", overflowErr), - F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), "DECIMAL(2, 2)", new BigDecimal("0.10")), - F.asList("DECIMAL(4, 2)", new BigDecimal("10.12"), "DECIMAL(2, 1)", overflowErr), - F.asList("DECIMAL(2, 2)", new BigDecimal("0.12"), "DECIMAL(1, 2)", overflowErr), - F.asList("DECIMAL(1, 1)", new BigDecimal("0.1"), "DECIMAL(1, 1)", new BigDecimal("0.1")) - ); - } - /** */ @Test public void testFunctionArgsToNumericImplicitConversion() { - assumeNoTransactions(); + assumeTrue("Test use queries that doesn't touch any data. Skip for tx modes", sqlTxMode == SqlTransactionMode.NONE); assertQuery("select decode(?, 0, 0, 1, 1.0)").withParams(0).returns(new BigDecimal("0.0")).check(); assertQuery("select decode(?, 0, 0, 1, 1.0)").withParams(1).returns(new BigDecimal("1.0")).check(); @@ -891,7 +664,7 @@ public void testArithmeticOverflow() { /** */ @Test public void testCastDecimalOverflows() { - assumeNoTransactions(); + assumeTrue("Test use queries that doesn't touch any data. Skip for tx modes", sqlTxMode == SqlTransactionMode.NONE); // BIGINT assertQuery("SELECT CAST(9223372036854775807.1 AS BIGINT)").returns(9223372036854775807L).check(); @@ -970,9 +743,4 @@ public void testCastDecimalOverflows() { assertQuery("SELECT CAST('-128.1' AS TINYINT)").returns((byte)-128).check(); assertQuery("SELECT CAST('-128.9' AS TINYINT)").returns((byte)-128).check(); } - - /** */ - private void assumeNoTransactions() { - assumeTrue("Test use queries that doesn't touch any data. Skip for tx modes", sqlTxMode == SqlTransactionMode.NONE); - } } From 6f2816b46583c547de718772321ac42384376fd0 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Wed, 23 Oct 2024 20:55:46 +0300 Subject: [PATCH 17/43] alpha --- .../calcite/exec/exp/ExpressionFactoryImpl.java | 6 +----- .../query/calcite/prepare/IgniteSqlValidator.java | 11 +++++++---- .../query/calcite/prepare/PlannerHelper.java | 2 -- .../query/calcite/integration/DataTypesTest.java | 12 ------------ .../DynamicParametersIntegrationTest.java | 11 ++++++++++- 5 files changed, 18 insertions(+), 24 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java index 6ebb2ff5654e5..3b15b6831cc78 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/ExpressionFactoryImpl.java @@ -558,11 +558,7 @@ private Scalar compile(List nodes, RelDataType type, boolean biInParams Class clazz = biInParams ? BiScalar.class : SingleScalar.class; - String src = Expressions.toString(F.asList(decl), "\n", false); - - System.err.println("TEST | compile: \n" + src); - - return Commons.compile(clazz, src); + return Commons.compile(clazz, Expressions.toString(F.asList(decl), "\n", false)); } /** */ 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 c264467d9632e..d63e12a385bec 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 @@ -107,7 +107,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl { HUMAN_READABLE_ALIASES_FOR = Collections.unmodifiableSet(kinds); } - /** Validated dynamic parameters with passed values by indexes. */ + /** Validated dynamic parameters with passed values. */ @Nullable private Map dynParams; /** Detected dynamic parameter nodes. */ @@ -161,6 +161,7 @@ private void validateDynamicParameters() { return; } + // TODO IGNITE-23251 : validate number of actual against passed dynamic parameters. if (F.isEmpty(dynParamNodes)) return; @@ -169,7 +170,8 @@ private void validateDynamicParameters() { for (SqlDynamicParam node : dynParamNodes) { DynamicParameterHolder pHolder = dynParams.get(node.getIndex()); - assert pHolder != null && pHolder.type != null && pHolder.node != null : "Dynamic parameter has not been validated. Idx: " + node.getIndex(); + assert pHolder != null && pHolder.type != null && pHolder.node != null + : "Dynamic parameter has not been validated. Idx: " + node.getIndex(); if (pHolder.type == unknownType) { throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation( @@ -451,9 +453,10 @@ public boolean unspecified(SqlDynamicParam param) { return !dynamicParamater(param.getIndex()).hasValue; } - /** */ + /** Binds {@code node} and {@code type} of a dynamic parameter to {@code pHolder}. */ private void dynamicParameterType(DynamicParameterHolder pHolder, SqlDynamicParam node, RelDataType type) { - assert pHolder.node == null || node.equals(pHolder.node) : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); + assert pHolder.node == null || node.equals(pHolder.node) + : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); pHolder.type = type; diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java index 4cb794fb3b327..7618793255f03 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlannerHelper.java @@ -119,8 +119,6 @@ public static IgniteRel optimize(SqlNode sqlNode, IgnitePlanner planner, IgniteL if (sqlNode.isA(ImmutableSet.of(SqlKind.INSERT, SqlKind.UPDATE, SqlKind.MERGE))) igniteRel = new FixDependentModifyNodeShuttle().visit(igniteRel); - System.err.println("TEST | Plan:\n" + RelOptUtil.toString(igniteRel)); - return igniteRel; } catch (Throwable ex) { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 2d005ef7be2a3..d0a641afd7eb5 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -349,18 +349,6 @@ public void testUnsupportedTypes() { "'TIMESTAMP WITH LOCAL TIME ZONE' is not supported."); } - /** - * Test cases for decimal literals. - */ - @Test - public void testDecimalLiteral() { - sql("CREATE TABLE t(id integer primary key, int_col integer)"); - sql("insert into t values (1, 1)"); - - // Fails - assertQuery("SELECT id from t where int_col=CAST(? as INTEGER)").withParams('1').returns(1).check(); - } - /** Cache API - SQL API cross check. */ @Test public void testBinaryCache() { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index b3eea80c0f32f..4ab1dfc4ed001 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -60,6 +60,15 @@ public void testMetadataTypesForDynamicParameters() { } } + /** */ + @Test + public void testCasts() { + sql("CREATE TABLE t(id integer primary key, int_col integer)"); + sql("insert into t values (1, 1)"); + + assertQuery("SELECT id from t where int_col=CAST(? as INTEGER)").withParams('1').returns(1).check(); + } + /** */ @Test public void testDynamicParameters() { @@ -71,7 +80,7 @@ public void testDynamicParameters() { assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); - assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); + assertQuery("SELECT ? % ?").withParams(11, 10).returns(BigDecimal.ONE).check(); assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) .returns(Date.valueOf("2022-01-31")).check(); From d0fb31d46f7adb63c91092079b432799e5d4d349 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 24 Oct 2024 12:39:32 +0300 Subject: [PATCH 18/43] test fixes --- .../query/calcite/prepare/IgniteSqlValidator.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) 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 d63e12a385bec..bdbe702458df8 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 @@ -762,8 +762,6 @@ private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam nod if (SqlTypeUtil.equalSansNullability(valType, inferredType)) return; - assert !unknownType.equals(valType) : "Failed to guess type of a dynamic parameter."; - if (valType.getFamily().equals(inferredType.getFamily())) { RelDataType leastRestrictive = typeFactory().leastRestrictive(F.asList(inferredType, valType)); @@ -771,9 +769,13 @@ private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam nod if (inferredType == leastRestrictive) return; + + valType = leastRestrictive; } - else if (!unknownType.equals(inferredType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) + else if (!unknownType.equals(inferredType) && !unknownType.equals(valType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) return; + else + valType = inferredType; dynamicParameterType(pHolder, node, valType); } From b2f8939a3a17188cce27458b3090fc0724d99aa0 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 24 Oct 2024 16:29:33 +0300 Subject: [PATCH 19/43] test fixes --- .../calcite/prepare/IgniteSqlValidator.java | 142 +++++++++--------- .../DynamicParametersIntegrationTest.java | 22 +-- 2 files changed, 83 insertions(+), 81 deletions(-) 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 bdbe702458df8..f42baecbc8184 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,8 @@ package org.apache.ignite.internal.processors.query.calcite.prepare; import java.math.BigDecimal; +import java.util.AbstractList; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -27,7 +29,6 @@ import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.UUID; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.prepare.CalciteCatalogReader; import org.apache.calcite.prepare.Prepare; @@ -60,6 +61,7 @@ import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; +import org.apache.calcite.sql.util.SqlShuttle; import org.apache.calcite.sql.validate.SelectScope; import org.apache.calcite.sql.validate.SqlQualified; import org.apache.calcite.sql.validate.SqlValidator; @@ -201,18 +203,6 @@ else if (!Objects.equals(knownType, pHolder.type)) { } } - /** */ - private RelDataType deriveTypeFromValue(@Nullable Object val) { - IgniteTypeFactory tf = typeFactory(); - - if (val == null) - return tf.createSqlType(SqlTypeName.NULL); - else if (val instanceof UUID) - return tf.createCustomType(UUID.class); - else - return tf.toSql(tf.createType(val.getClass())); - } - /** {@inheritDoc} */ @Override public void validateInsert(SqlInsert insert) { validateTableModify(insert.getTargetTable()); @@ -351,31 +341,28 @@ private void checkIntegerLimit(SqlNode n, String nodeName) { } else if (n instanceof SqlDynamicParam) { SqlDynamicParam dynamicParam = (SqlDynamicParam)n; - RelDataType intType = typeFactory.createSqlType(INTEGER); - - if (!unspecified(dynamicParam)) { - Object param = dynamicParameterValue(dynamicParam); - - if (param instanceof Integer) { - if ((Integer)param < 0) - throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); - } - else { - String actualType = deriveDynamicParamType(dynamicParam).toString(); - - String expectedType = intType.toString(); - - String errMsg = String.format("Incorrect type of a dynamic parameter. Expected '%s' but got '%s'", - expectedType, actualType); - - throw newValidationError(n, IgniteResource.INSTANCE.dynamicParameterValidation(errMsg)); - } - } DynamicParameterHolder pHolder = dynamicParamater(dynamicParam.getIndex()); - // Dynamic parameters are nullable. - dynamicParameterType(pHolder, dynamicParam, typeFactory.createTypeWithNullability(intType, true)); + if (pHolder.hasValue && pHolder.val instanceof Integer && (Integer)pHolder.val < 0) + throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); + +// else { +// String actualType = deriveDynamicParamType(dynamicParam).toString(); +// +// String expectedType = intType.toString(); +// +// String errMsg = String.format("Incorrect type of a dynamic parameter. Expected '%s' but got '%s'", +// expectedType, actualType); +// +// throw newValidationError(n, IgniteResource.INSTANCE.dynamicParameterValidation(errMsg)); +// } +// } + +// DynamicParameterHolder pHolder = dynamicParamater(dynamicParam.getIndex()); +// +// // Dynamic parameters are nullable. +// dynamicParameterType(pHolder, dynamicParam, typeFactory.createTypeWithNullability(intType, true)); } } @@ -392,6 +379,39 @@ else if (n instanceof SqlDynamicParam) { return valHolder.val; } + /** {@inheritDoc} */ + @Override public RelDataType getParameterRowType(SqlNode sqlQry) { + // We do not use calcite' version since it is contains a bug, + // alreadyVisited visited uses object identity, but rewrites of NULLIF, COALESCE + // into dynamic parameters may place the same parameter into multiple positions + // in SQL tree. + List types = new ArrayList<>(); + + Set alreadyVisited = new HashSet<>(F.isEmpty(dynParams) ? 1 : dynParams.size()); + + sqlQry.accept( + new SqlShuttle() { + @Override public SqlNode visit(SqlDynamicParam param) { + if (alreadyVisited.add(param.getIndex())) + types.add(getValidatedNodeType(param)); + + return param; + } + }); + + return typeFactory.createStructType( + types, + new AbstractList<>() { + @Override public String get(int idx) { + return "?" + idx; + } + + @Override public int size() { + return types.size(); + } + }); + } + /** {@inheritDoc} */ @Override public void validateCall(SqlCall call, SqlValidatorScope scope) { if (call.getKind() == SqlKind.AS) { @@ -410,8 +430,8 @@ else if (call.getKind() == SqlKind.CAST) { /** {@inheritDoc} */ @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { - if (expr instanceof SqlDynamicParam) - return deriveDynamicParamType((SqlDynamicParam)expr).type; +// if (expr instanceof SqlDynamicParam) +// return deriveDynamicParamType((SqlDynamicParam)expr).type; return super.deriveType(scope, expr); } @@ -428,16 +448,22 @@ private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { DynamicParameterHolder pHolder = dynamicParamater(node.getIndex()); if (pHolder.type != null && pHolder.hasValue) { - assert dynParamNodes.contains(node); - assert node.equals(pHolder.node); + // TODO: IGNITE-23251 - Uncomment with this ticket. Passed parameters number should be validated. + //assert dynParamNodes.contains(node); + //assert node.equals(pHolder.node); setValidatedNodeType(node, pHolder.type); } else { dynParamNodes.add(node); - if (pHolder.hasValue) - dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(deriveTypeFromValue(pHolder.val), true)); + if (pHolder.hasValue) { + RelDataType type = pHolder.val == null + ? typeFactory().createSqlType(SqlTypeName.NULL) + : typeFactory().toSql(typeFactory().createType(pHolder.val.getClass())); + + dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(type, true)); + } else { RelDataType validatedNodeType = getValidatedNodeTypeIfKnown(node); @@ -448,14 +474,9 @@ private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { return pHolder; } - /** returns {@code True} if {@code param} has no value set. */ - public boolean unspecified(SqlDynamicParam param) { - return !dynamicParamater(param.getIndex()).hasValue; - } - /** Binds {@code node} and {@code type} of a dynamic parameter to {@code pHolder}. */ private void dynamicParameterType(DynamicParameterHolder pHolder, SqlDynamicParam node, RelDataType type) { - assert pHolder.node == null || node.equals(pHolder.node) + assert pHolder.node == null || node.getIndex() == pHolder.node.getIndex() : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); pHolder.type = type; @@ -750,34 +771,15 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** - * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index - * is actual to {@link #dynParams}. - */ + /** */ private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam node) { DynamicParameterHolder pHolder = deriveDynamicParamType(node); - RelDataType valType = pHolder.type; - - if (SqlTypeUtil.equalSansNullability(valType, inferredType)) + // Exit if the type is determined or is unknown. + if (inferredType.equals(unknownType) || SqlTypeUtil.equalSansNullability(typeFactory, inferredType, pHolder.type)) return; - if (valType.getFamily().equals(inferredType.getFamily())) { - RelDataType leastRestrictive = typeFactory().leastRestrictive(F.asList(inferredType, valType)); - - assert leastRestrictive != null; - - if (inferredType == leastRestrictive) - return; - - valType = leastRestrictive; - } - else if (!unknownType.equals(inferredType) && !unknownType.equals(valType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) - return; - else - valType = inferredType; - - dynamicParameterType(pHolder, node, valType); + dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(inferredType, true)); } /** {@inheritDoc} */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index 4ab1dfc4ed001..988f456fe6d27 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -80,7 +80,7 @@ public void testDynamicParameters() { assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); - assertQuery("SELECT ? % ?").withParams(11, 10).returns(BigDecimal.ONE).check(); + assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) .returns(Date.valueOf("2022-01-31")).check(); @@ -105,17 +105,17 @@ public void testDynamicParameters() { /** Tests the same query with different type of parameters to cover case with check right plans cache work. **/ @Test public void testWithDifferentParametersTypes() { - assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); - assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2.2, 2.2, "TeSt").returns(4.4, "test").check(); +// assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); +// assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2.2, 2.2, "TeSt").returns(4.4, "test").check(); - assertQuery("SELECT COALESCE(?, ?)").withParams(null, null).returns(NULL_RESULT).check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams(null, null).returns(NULL_RESULT).check(); assertQuery("SELECT COALESCE(?, ?)").withParams(null, 13).returns(13).check(); - assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); - assertQuery("SELECT COALESCE(?, ?)").withParams("a", "b").returns("a").check(); - assertQuery("SELECT COALESCE(?, ?)").withParams(22, 33).returns(22).check(); - assertQuery("SELECT COALESCE(?, ?)").withParams(12.2, "b").returns("12.2").check(); - assertQuery("SELECT COALESCE(?, ?)").withParams(12, "b").returns("12").check(); - assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1).returns("INTEGER").check(); - assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1d).returns("DOUBLE").check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams("a", "b").returns("a").check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams(22, 33).returns(22).check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams(12.2, "b").returns("12.2").check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams(12, "b").returns("12").check(); +// assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1).returns("INTEGER").check(); +// assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1d).returns("DOUBLE").check(); } } From f4870ec5e05412d82bdb55e04a2f6b50091f6b76 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 24 Oct 2024 21:40:52 +0300 Subject: [PATCH 20/43] merged master, commited tests --- .../calcite/prepare/IgniteSqlValidator.java | 51 ++------ .../calcite/integration/DataTypesTest.java | 5 +- .../testsuites/IgniteCalciteTestSuite.java | 38 +++--- .../testsuites/IntegrationTestSuite.java | 114 +++++++++--------- 4 files changed, 86 insertions(+), 122 deletions(-) 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 255bf3c659c6a..ed93202571327 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,8 +18,6 @@ package org.apache.ignite.internal.processors.query.calcite.prepare; import java.math.BigDecimal; -import java.util.AbstractList; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; @@ -80,7 +78,6 @@ import org.apache.ignite.internal.util.typedef.F; import org.jetbrains.annotations.Nullable; -import static org.apache.calcite.sql.type.SqlTypeName.INTEGER; import static org.apache.calcite.util.Static.RESOURCE; /** Validator. */ @@ -770,49 +767,15 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** - * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index - * is actual to {@link #parameters}. - * - * @return {@code True} if a new type was set. {@code False} otherwise. - */ - private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length) - return false; - - Object val = parameters[((SqlDynamicParam)node).getIndex()]; - - if (val == null) { - if (inferredType.equals(unknownType)) { - setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL)); - - return true; - } - - return false; - } - - RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass())); - - if (SqlTypeUtil.equalSansNullability(valType, inferredType)) - return false; - - assert !unknownType.equals(valType); - - if (valType.getFamily().equals(inferredType.getFamily())) { - RelDataType leastRestrictive = typeFactory().leastRestrictive(F.asList(inferredType, valType)); - - assert leastRestrictive != null; - - if (inferredType == leastRestrictive) - return false; - } - else if (!unknownType.equals(inferredType) && SqlTypeUtil.canCastFrom(valType, inferredType, true)) - return false; + /** */ + private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam node) { + DynamicParameterHolder pHolder = deriveDynamicParamType(node); - setValidatedNodeType(node, valType); + // Exit if the type is determined or is unknown. + if (inferredType.equals(unknownType) || SqlTypeUtil.equalSansNullability(typeFactory, inferredType, pHolder.type)) + return; - return true; + dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(inferredType, true)); } /** {@inheritDoc} */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index a577fa9f96e6e..46a7b08912b67 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -34,6 +34,7 @@ import org.apache.ignite.internal.processors.query.calcite.exec.exp.IgniteSqlFunctions; import org.apache.ignite.internal.processors.query.calcite.hint.HintDefinition; import org.apache.ignite.internal.util.typedef.F; +import org.junit.Ignore; import org.junit.Test; import static org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor.FRAMEWORK_CONFIG; @@ -447,8 +448,8 @@ public void testDecimalScale() { .check(); } - /** */ - @Test + /** TODO: incomment */ + @Ignore @Test public void testIsNotDistinctFromTypeConversion() { SqlTypeName[] numerics = new SqlTypeName[] {SqlTypeName.TINYINT, SqlTypeName.SMALLINT, SqlTypeName.INTEGER, SqlTypeName.BIGINT, SqlTypeName.DECIMAL, SqlTypeName.FLOAT, SqlTypeName.DOUBLE}; diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java index df909b46f17db..5c08c0a533562 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java +++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java @@ -38,28 +38,28 @@ */ @RunWith(Suite.class) @Suite.SuiteClasses({ - PlannerTestSuite.class, - ExecutionTestSuite.class, + //PlannerTestSuite.class, + //ExecutionTestSuite.class, IntegrationTestSuite.class, - ClosableIteratorsHolderTest.class, - MemoryTrackerTest.class, - QueryCheckerTest.class, - SqlCustomParserTest.class, - SqlReservedWordsTest.class, - IgniteSqlFunctionsTest.class, - LogicalRelImplementorTest.class, +// ClosableIteratorsHolderTest.class, +// MemoryTrackerTest.class, +// QueryCheckerTest.class, +// SqlCustomParserTest.class, +// SqlReservedWordsTest.class, +// IgniteSqlFunctionsTest.class, +// LogicalRelImplementorTest.class, - ScriptTestSuite.class, - CalciteCommunicationMessageSerializationTest.class, - - NumericTypesPrecisionsTest.class, - - KeyFilteringCursorTest.class, - SqlTransactionsIsolationTest.class, - SqlTransactionsUnsupportedModesTest.class, - - JdbcThinTransactionalSelfTest.class, +// ScriptTestSuite.class, +// CalciteCommunicationMessageSerializationTest.class, +// +// NumericTypesPrecisionsTest.class, +// +// KeyFilteringCursorTest.class, +// SqlTransactionsIsolationTest.class, +// SqlTransactionsUnsupportedModesTest.class, +// +// JdbcThinTransactionalSelfTest.class, }) public class IgniteCalciteTestSuite { } 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 dc27a636e7775..e191c7effbee9 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 @@ -84,65 +84,65 @@ */ @RunWith(Suite.class) @Suite.SuiteClasses({ - OrToUnionRuleTest.class, - ProjectScanMergeRuleTest.class, - CalciteQueryProcessorTest.class, - CalciteErrorHandlilngIntegrationTest.class, - JdbcQueryTest.class, - JdbcCrossEngineTest.class, - CalciteBasicSecondaryIndexIntegrationTest.class, - CancelTest.class, - DateTimeTest.class, - LimitOffsetIntegrationTest.class, - SqlFieldsQueryUsageTest.class, - AggregatesIntegrationTest.class, - MetadataIntegrationTest.class, - RunningQueriesIntegrationTest.class, - SqlDiagnosticIntegrationTest.class, - SortAggregateIntegrationTest.class, - TableDdlIntegrationTest.class, - IndexDdlIntegrationTest.class, - UserDdlIntegrationTest.class, - KillCommandDdlIntegrationTest.class, - KillQueryCommandDdlIntegrationTest.class, - StatisticsCommandDdlIntegrationTest.class, - FunctionsTest.class, - StdSqlOperatorsTest.class, - TableDmlIntegrationTest.class, +// OrToUnionRuleTest.class, +// ProjectScanMergeRuleTest.class, +// CalciteQueryProcessorTest.class, +// CalciteErrorHandlilngIntegrationTest.class, +// JdbcQueryTest.class, +// JdbcCrossEngineTest.class, +// CalciteBasicSecondaryIndexIntegrationTest.class, +// CancelTest.class, +// DateTimeTest.class, +// LimitOffsetIntegrationTest.class, +// SqlFieldsQueryUsageTest.class, +// AggregatesIntegrationTest.class, +// MetadataIntegrationTest.class, +// RunningQueriesIntegrationTest.class, +// SqlDiagnosticIntegrationTest.class, +// SortAggregateIntegrationTest.class, +// TableDdlIntegrationTest.class, +// IndexDdlIntegrationTest.class, +// UserDdlIntegrationTest.class, +// KillCommandDdlIntegrationTest.class, +// KillQueryCommandDdlIntegrationTest.class, +// StatisticsCommandDdlIntegrationTest.class, +// FunctionsTest.class, +// StdSqlOperatorsTest.class, +// TableDmlIntegrationTest.class, DataTypesTest.class, - IndexSpoolIntegrationTest.class, - HashSpoolIntegrationTest.class, - IndexScanlIntegrationTest.class, - IndexScanMultiNodeIntegrationTest.class, - SetOpIntegrationTest.class, - UnstableTopologyTest.class, - JoinCommuteRulesTest.class, - ServerStatisticsIntegrationTest.class, - JoinIntegrationTest.class, - IntervalTest.class, - UserDefinedFunctionsIntegrationTest.class, - UserDefinedFunctionsIntegrationTransactionalTest.class, - CorrelatesIntegrationTest.class, - SystemViewsIntegrationTest.class, - IndexRebuildIntegrationTest.class, - QueryEngineConfigurationIntegrationTest.class, - IndexMultiRangeScanIntegrationTest.class, - KeepBinaryIntegrationTest.class, - LocalQueryIntegrationTest.class, - QueryWithPartitionsIntegrationTest.class, - QueryMetadataIntegrationTest.class, - MemoryQuotasIntegrationTest.class, - LocalDateTimeSupportTest.class, +// IndexSpoolIntegrationTest.class, +// HashSpoolIntegrationTest.class, +// IndexScanlIntegrationTest.class, +// IndexScanMultiNodeIntegrationTest.class, +// SetOpIntegrationTest.class, +// UnstableTopologyTest.class, +// JoinCommuteRulesTest.class, +// ServerStatisticsIntegrationTest.class, +// JoinIntegrationTest.class, +// IntervalTest.class, +// UserDefinedFunctionsIntegrationTest.class, +// UserDefinedFunctionsIntegrationTransactionalTest.class, +// CorrelatesIntegrationTest.class, +// SystemViewsIntegrationTest.class, +// IndexRebuildIntegrationTest.class, +// QueryEngineConfigurationIntegrationTest.class, +// IndexMultiRangeScanIntegrationTest.class, +// KeepBinaryIntegrationTest.class, +// LocalQueryIntegrationTest.class, +// QueryWithPartitionsIntegrationTest.class, +// QueryMetadataIntegrationTest.class, +// MemoryQuotasIntegrationTest.class, +// LocalDateTimeSupportTest.class, DynamicParametersIntegrationTest.class, - ExpiredEntriesIntegrationTest.class, - TimeoutIntegrationTest.class, - PartitionPruneTest.class, - JoinRehashIntegrationTest.class, - IndexWithSameNameCalciteTest.class, - AuthorizationIntegrationTest.class, - DdlTransactionCalciteSelfTest.class, - MultiLineQueryTest.class, - ViewsIntegrationTest.class, +// ExpiredEntriesIntegrationTest.class, +// TimeoutIntegrationTest.class, +// PartitionPruneTest.class, +// JoinRehashIntegrationTest.class, +// IndexWithSameNameCalciteTest.class, +// AuthorizationIntegrationTest.class, +// DdlTransactionCalciteSelfTest.class, +// MultiLineQueryTest.class, +// ViewsIntegrationTest.class, }) public class IntegrationTestSuite { } From da8c97fe46527a07e99f0048b79240dfa59a55d8 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 25 Oct 2024 10:39:13 +0300 Subject: [PATCH 21/43] raw --- .../query/calcite/CalciteQueryProcessor.java | 7 +- .../processors/query/calcite/RootQuery.java | 4 +- .../query/calcite/prepare/IgnitePlanner.java | 2 +- .../calcite/prepare/IgniteSqlValidator.java | 171 +++++------------- .../calcite/prepare/PlanningContext.java | 19 ++ .../query/calcite/util/IgniteResource.java | 4 +- .../AbstractBasicIntegrationTest.java | 3 +- .../calcite/integration/DataTypesTest.java | 9 + .../DynamicParametersIntegrationTest.java | 92 ++++++---- .../testsuites/IntegrationTestSuite.java | 2 +- 10 files changed, 142 insertions(+), 171 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 a21ba720ced04..8cd78b077cd82 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 @@ -393,7 +393,7 @@ public ExecutionService executionService() { String sql, Object... params ) throws IgniteSQLException { - return parseAndProcessQuery(qryCtx, executionSvc::executePlan, schemaName, sql, params); + return parseAndProcessQuery(qryCtx, executionSvc::executePlan, schemaName, sql, true, params); } /** {@inheritDoc} */ @@ -402,7 +402,7 @@ public ExecutionService executionService() { String schemaName, String sql ) throws IgniteSQLException { - return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, true), schemaName, sql); + return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, true), schemaName, sql, false); } /** {@inheritDoc} */ @@ -411,7 +411,7 @@ public ExecutionService executionService() { String schemaName, String sql ) throws IgniteSQLException { - return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, false), schemaName, sql); + return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, false), schemaName, sql, false); } /** {@inheritDoc} */ @@ -487,6 +487,7 @@ private List parseAndProcessQuery( BiFunction, QueryPlan, T> action, @Nullable String schemaName, String sql, + boolean validateDynParams, Object... params ) throws IgniteSQLException { ensureTransactionModeSupported(qryCtx); diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java index 242e355d22c7e..74a0a07c3c9c3 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java @@ -293,7 +293,7 @@ public void run(ExecutionContext ctx, ExecutionPlan plan, FieldsMetadata m } /** */ - public PlanningContext planningContext() { + public PlanningContext planningContext(boolean validateParams) { synchronized (mux) { if (state == QueryState.CLOSED || state == QueryState.CLOSING) throw queryCanceledException(); @@ -323,7 +323,7 @@ public PlanningContext planningContext() { } } - return pctx; + return pctx.validateParameters(validateParams); } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java index a66f2564ef7c2..b7995e96dd579 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java @@ -413,7 +413,7 @@ public String dump() { /** */ private SqlValidator validator() { if (validator == null) - validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters()); + validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters(), ctx.validateParameters()); return validator; } 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 ed93202571327..5c788382f2f79 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 @@ -25,7 +25,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import org.apache.calcite.plan.RelOptTable; import org.apache.calcite.prepare.CalciteCatalogReader; @@ -58,7 +57,6 @@ 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.type.SqlTypeUtil; import org.apache.calcite.sql.validate.SelectScope; import org.apache.calcite.sql.validate.SqlQualified; import org.apache.calcite.sql.validate.SqlValidator; @@ -105,12 +103,15 @@ public class IgniteSqlValidator extends SqlValidatorImpl { HUMAN_READABLE_ALIASES_FOR = Collections.unmodifiableSet(kinds); } - /** Validated dynamic parameters with passed values. */ + /** Validated dynamic parameters with the passed values if any. */ @Nullable private Map dynParams; /** Detected dynamic parameter nodes. */ @Nullable private Set dynParamNodes; + /** Flag to enable validation of passed dynamic parameters. */ + private final boolean validateParams; + /** * Creates a validator. * @@ -119,11 +120,14 @@ public class IgniteSqlValidator extends SqlValidatorImpl { * @param typeFactory Type factory * @param config Config * @param parameters Dynamic parameters + * @param validateParams Flag to enable validation of passed dynamic parameters. */ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogReader, - IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters) { + IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters, boolean validateParams) { super(opTab, catalogReader, typeFactory, config); + this.validateParams = validateParams; + if (!F.isEmpty(parameters)) { dynParams = new HashMap<>(parameters.length); @@ -132,15 +136,6 @@ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogRe } } - /** {@inheritDoc} */ - @Override public SqlNode validate(SqlNode topNode) { - SqlNode node = super.validate(topNode); - - validateDynamicParameters(); - - return node; - } - /** */ @Nullable private DynamicParameterHolder dynamicParamater(int idx) { assert idx >= 0; @@ -151,54 +146,6 @@ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogRe return dynParams.computeIfAbsent(idx, idx0 -> new DynamicParameterHolder()); } - /** */ - private void validateDynamicParameters() { - if (F.isEmpty(dynParams)) { - assert F.isEmpty(dynParamNodes); - - return; - } - - // TODO IGNITE-23251 : validate number of actual against passed dynamic parameters. - if (F.isEmpty(dynParamNodes)) - return; - - String errMsg = null; - - for (SqlDynamicParam node : dynParamNodes) { - DynamicParameterHolder pHolder = dynParams.get(node.getIndex()); - - assert pHolder != null && pHolder.type != null && pHolder.node != null - : "Dynamic parameter has not been validated. Idx: " + node.getIndex(); - - if (pHolder.type == unknownType) { - throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation( - "Unable to determine type of a dynamic parameter #" + (node.getIndex() + 1))); - } - - if (!pHolder.hasValue) - continue; - - RelDataType knownType = getValidatedNodeTypeIfKnown(node); - - if (!SqlTypeUtil.equalSansNullability(knownType, pHolder.type)) { - errMsg = String.format( - "Type of dynamic parameter #%d value type does not match. Expected: %s, derived: %s", - node.getIndex(), pHolder.type.getFullTypeString(), knownType.getFullTypeString() - ); - } - else if (!Objects.equals(knownType, pHolder.type)) { - errMsg = String.format( - "Type of dynamic parameter node #%d does not match. Expected: %s derived: %s", - node.getIndex(), pHolder.type.getFullTypeString(), knownType != null ? knownType.getFullTypeString() : null - ); - } - - if (errMsg != null) - throw newValidationError(node, IgniteResource.INSTANCE.dynamicParameterValidation(errMsg)); - } - } - /** {@inheritDoc} */ @Override public void validateInsert(SqlInsert insert) { validateTableModify(insert.getTargetTable()); @@ -338,76 +285,13 @@ private void checkIntegerLimit(SqlNode n, String nodeName) { else if (n instanceof SqlDynamicParam) { SqlDynamicParam dynamicParam = (SqlDynamicParam)n; - DynamicParameterHolder pHolder = dynamicParamater(dynamicParam.getIndex()); + DynamicParameterHolder pHolder = deriveDynamicParamType(dynamicParam); if (pHolder.hasValue && pHolder.val instanceof Integer && (Integer)pHolder.val < 0) throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); - -// else { -// String actualType = deriveDynamicParamType(dynamicParam).toString(); -// -// String expectedType = intType.toString(); -// -// String errMsg = String.format("Incorrect type of a dynamic parameter. Expected '%s' but got '%s'", -// expectedType, actualType); -// -// throw newValidationError(n, IgniteResource.INSTANCE.dynamicParameterValidation(errMsg)); -// } -// } - -// DynamicParameterHolder pHolder = dynamicParamater(dynamicParam.getIndex()); -// -// // Dynamic parameters are nullable. -// dynamicParameterType(pHolder, dynamicParam, typeFactory.createTypeWithNullability(intType, true)); } } - /** - * @returns Value of the dynamic parameter. - * @throws {@link IllegalArgumentException} if the value is not specified. - */ - @Nullable private Object dynamicParameterValue(SqlDynamicParam dynamicParam) { - DynamicParameterHolder valHolder = dynamicParamater(dynamicParam.getIndex()); - - if (!valHolder.hasValue) - throw new IllegalArgumentException(String.format("Value of dynamic parameter#%d is not specified", dynamicParam.getIndex())); - - return valHolder.val; - } - -// /** {@inheritDoc} */ -// @Override public RelDataType getParameterRowType(SqlNode sqlQry) { -// // We do not use calcite' version since it is contains a bug, -// // alreadyVisited visited uses object identity, but rewrites of NULLIF, COALESCE -// // into dynamic parameters may place the same parameter into multiple positions -// // in SQL tree. -// List types = new ArrayList<>(); -// -// Set alreadyVisited = new HashSet<>(F.isEmpty(dynParams) ? 1 : dynParams.size()); -// -// sqlQry.accept( -// new SqlShuttle() { -// @Override public SqlNode visit(SqlDynamicParam param) { -// if (alreadyVisited.add(param.getIndex())) -// types.add(getValidatedNodeType(param)); -// -// return param; -// } -// }); -// -// return typeFactory.createStructType( -// types, -// new AbstractList<>() { -// @Override public String get(int idx) { -// return "?" + idx; -// } -// -// @Override public int size() { -// return types.size(); -// } -// }); -// } - /** {@inheritDoc} */ @Override public void validateCall(SqlCall call, SqlValidatorScope scope) { if (call.getKind() == SqlKind.AS) { @@ -426,8 +310,8 @@ else if (call.getKind() == SqlKind.CAST) { /** {@inheritDoc} */ @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { -// if (expr instanceof SqlDynamicParam) -// return deriveDynamicParamType((SqlDynamicParam)expr).type; + if (expr instanceof SqlDynamicParam) + return deriveDynamicParamType((SqlDynamicParam)expr).type; return super.deriveType(scope, expr); } @@ -600,6 +484,29 @@ private SqlNode rewriteTableToQuery(SqlNode from) { return isSystemFieldName(field.getName()); } + /** {@inheritDoc} */ + @Override public SqlNode validate(SqlNode topNode) { + SqlNode res = super.validate(topNode); + + if (validateParams) + validateDynamicParameters(); + + return res; + } + + /** TODO: IGNITE-23251 - validate number of query's dynamic parameters and the passed values. */ + private void validateDynamicParameters() { + if (F.isEmpty(dynParams)) + return; + + dynParams.forEach((idx, pHolder) -> { + assert pHolder.type != null : "No type inferred for dynamic parameter #" + idx; + + if (unknownType.equals(pHolder.type) || !pHolder.hasValue) + throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation(idx + 1)); + }); + } + /** */ private void validateAggregateFunction(SqlCall call, SqlAggFunction aggFunction) { if (!SqlKind.AGGREGATE.contains(aggFunction.kind)) @@ -724,12 +631,18 @@ private boolean isSystemFieldName(String alias) { if (node instanceof SqlDynamicParam) inferDynamicParamType(inferredType, (SqlDynamicParam)node); else if (node instanceof SqlCall) { + final SqlCall call = (SqlCall)node; + + // SqlStdOperatorTable::IS_NULL and SqlStdOperatorTable::IS_NOT_NULL overrides with VARCHAR_1024 for argument type inference. + if ((node.getKind() == SqlKind.IS_NULL || node.getKind() == SqlKind.IS_NOT_NULL) + && call.getOperandList().get(0) instanceof SqlDynamicParam) + return; + 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); @@ -772,7 +685,7 @@ private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam nod DynamicParameterHolder pHolder = deriveDynamicParamType(node); // Exit if the type is determined or is unknown. - if (inferredType.equals(unknownType) || SqlTypeUtil.equalSansNullability(typeFactory, inferredType, pHolder.type)) + if (inferredType.equals(unknownType) || inferredType.getFamily().equals(pHolder.type.getFamily())) return; dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(inferredType, true)); diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java index 2f222083c6a61..3417a9f21c969 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java @@ -47,6 +47,9 @@ public final class PlanningContext implements Context { /** */ private final Object[] parameters; + /** */ + private boolean validateParams; + /** */ private final CancelFlag cancelFlag = new CancelFlag(new AtomicBoolean()); @@ -94,6 +97,22 @@ public Object[] parameters() { return parameters; } + + /** + * @param validateParams Flag to enable validation of passed dynamic parameters. + */ + public PlanningContext validateParameters(boolean validateParams) { + this.validateParams = validateParams; + + return this; + } + /** + * @return Flag to enable validation of passed dynamic parameters. + */ + public boolean validateParameters() { + return validateParams; + } + // Helper methods /** * @return Sql conformance. diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java index 4a0fba5967929..71c8b6f10ed01 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java @@ -82,6 +82,6 @@ public interface IgniteResource { Resources.ExInst invalidCastParameters(); /** */ - @Resources.BaseMessage("Dynamic parameter validation error. ''{0}''") - Resources.ExInst dynamicParameterValidation(String value); + @Resources.BaseMessage("No value passed for dynamic parameter {0} or its type is unknown.") + Resources.ExInst dynamicParameterValidation(int value); } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java index 62be28c666968..bbfadff7a46a8 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java @@ -204,7 +204,8 @@ protected IgniteCache createAndPopulateTable(Ignite ignite, i put(ignite, person, idx++, new Employer(null, 15d)); put(ignite, person, idx++, new Employer("Ilya", 15d)); put(ignite, person, idx++, new Employer("Roma", 10d)); - put(ignite, person, idx, new Employer("Roma", 10d)); + put(ignite, person, idx++, new Employer("Roma", 10d)); + put(ignite, person, idx, new Employer("15", 15d)); return person; } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 46a7b08912b67..99af69516190e 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -18,6 +18,8 @@ package org.apache.ignite.internal.processors.query.calcite.integration; import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -36,6 +38,7 @@ import org.apache.ignite.internal.util.typedef.F; import org.junit.Ignore; import org.junit.Test; +import org.junit.runners.Parameterized; import static org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor.FRAMEWORK_CONFIG; import static org.junit.Assume.assumeTrue; @@ -44,6 +47,12 @@ * Test SQL data types. */ public class DataTypesTest extends AbstractBasicIntegrationTransactionalTest { + /** @return Test parameters. */ + @Parameterized.Parameters(name = "sqlTxMode={0}") + public static Collection parameters() { + return Arrays.asList(SqlTransactionMode.NONE); + } + /** Tests Other type. */ @Test public void testOtherType() { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index 988f456fe6d27..f337b3e6f79ba 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -26,6 +26,7 @@ import java.time.Period; import java.util.List; import java.util.UUID; +import org.apache.calcite.sql.validate.SqlValidatorException; import org.apache.ignite.internal.util.typedef.F; import org.junit.Test; @@ -60,40 +61,67 @@ public void testMetadataTypesForDynamicParameters() { } } + /** */ + @Test + public void testMissedValue() { + assertThrows("SELECT ?", SqlValidatorException.class, "No value passed for dynamic parameter 1 or its type is unknown."); + + assertThrows("SELECT ?, ?", SqlValidatorException.class, "No value passed for dynamic parameter 2 or its type is unknown.", "arg0"); + } + /** */ @Test public void testCasts() { - sql("CREATE TABLE t(id integer primary key, int_col integer)"); - sql("insert into t values (1, 1)"); + assertQuery("SELECT CAST(? as INTEGER)").withParams('1').returns(1).check(); + assertQuery("SELECT ?::INTEGER").withParams('1').returns(1).check(); + assertQuery("SELECT ?::VARCHAR").withParams(1).returns("1").check(); + assertQuery("SELECT CAST(? as VARCHAR)").withParams(1).returns("1").check(); + + createAndPopulateTable(); + + assertQuery("SELECT name FROM Person WHERE id=?::INTEGER").withParams("2").returns("Ilya").check(); + assertQuery("SELECT name FROM Person WHERE id=CAST(? as INTEGER)").withParams("2").returns("Ilya").check(); + + assertQuery("SELECT id FROM Person WHERE name=CAST(? as VARCHAR)").withParams(15).returns(5).check(); + assertQuery("SELECT id FROM Person WHERE name IN (?::VARCHAR)").withParams(15).returns(5).check(); + assertQuery("SELECT name FROM Person WHERE id IN (?::INTEGER)").withParams("2").returns("Ilya").check(); + assertQuery("SELECT name FROM Person WHERE id IN (?::INTEGER, ?::INTEGER)").withParams("2", "3") + .returns("Ilya").returns("Roma").check(); + + assertQuery("SELECT count(*) FROM Person WHERE ? IS NOT NULL").withParams(1).returns(6L).check(); + assertQuery("SELECT count(*) FROM Person WHERE ? IS NOT NULL").withParams("abc").returns(6L).check(); + assertQuery("SELECT count(*) FROM Person WHERE ? IS NOT NULL").withParams(new Object[] { null }).returns(0L).check(); - assertQuery("SELECT id from t where int_col=CAST(? as INTEGER)").withParams('1').returns(1).check(); + assertQuery("SELECT count(*) FROM Person WHERE ? IS NULL").withParams(1).returns(0L).check(); + assertQuery("SELECT count(*) FROM Person WHERE ? IS NULL").withParams("abc").returns(0L).check(); + assertQuery("SELECT count(*) FROM Person WHERE ? IS NULL").withParams(new Object[] {null}).returns(6L).check(); } /** */ @Test public void testDynamicParameters() { - assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); - assertQuery("SELECT COALESCE(null, ?)").withParams(13).returns(13).check(); - assertQuery("SELECT LOWER(?)").withParams("ASD").returns("asd").check(); - assertQuery("SELECT ?").withParams("asd").returns("asd").check(); - assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); - assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); - assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); - assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); - assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); - - assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) - .returns(Date.valueOf("2022-01-31")).check(); - assertQuery("SELECT LAST_DAY(?)").withParams(LocalDate.parse("2022-01-01")) - .returns(Date.valueOf("2022-01-31")).check(); +// assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); +// assertQuery("SELECT COALESCE(null, ?)").withParams(13).returns(13).check(); +// assertQuery("SELECT LOWER(?)").withParams("ASD").returns("asd").check(); +// assertQuery("SELECT ?").withParams("asd").returns("asd").check(); +// assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); +// assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); +// assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); +// assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); +// assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); +// +// assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) +// .returns(Date.valueOf("2022-01-31")).check(); +// assertQuery("SELECT LAST_DAY(?)").withParams(LocalDate.parse("2022-01-01")) +// .returns(Date.valueOf("2022-01-31")).check(); createAndPopulateTable(); - assertQuery("SELECT name LIKE '%' || ? || '%' FROM person where name is not null").withParams("go") - .returns(true).returns(false).returns(false).returns(false).check(); - - assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ?").withParams("I%", 1) - .returns(0).check(); +// assertQuery("SELECT name LIKE '%' || ? || '%' FROM person where name is not null").withParams("go") +// .returns(true).returns(false).returns(false).returns(false).returns(false).check(); +// +// assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ?").withParams("I%", 1) +// .returns(0).check(); assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ? OFFSET ?").withParams("I%", 1, 1) .returns(2).check(); @@ -105,17 +133,17 @@ public void testDynamicParameters() { /** Tests the same query with different type of parameters to cover case with check right plans cache work. **/ @Test public void testWithDifferentParametersTypes() { -// assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); -// assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2.2, 2.2, "TeSt").returns(4.4, "test").check(); + assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); + assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2.2, 2.2, "TeSt").returns(4.4, "test").check(); -// assertQuery("SELECT COALESCE(?, ?)").withParams(null, null).returns(NULL_RESULT).check(); + assertQuery("SELECT COALESCE(?, ?)").withParams(null, null).returns(NULL_RESULT).check(); assertQuery("SELECT COALESCE(?, ?)").withParams(null, 13).returns(13).check(); -// assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); -// assertQuery("SELECT COALESCE(?, ?)").withParams("a", "b").returns("a").check(); -// assertQuery("SELECT COALESCE(?, ?)").withParams(22, 33).returns(22).check(); -// assertQuery("SELECT COALESCE(?, ?)").withParams(12.2, "b").returns("12.2").check(); -// assertQuery("SELECT COALESCE(?, ?)").withParams(12, "b").returns("12").check(); -// assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1).returns("INTEGER").check(); -// assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1d).returns("DOUBLE").check(); + assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); + assertQuery("SELECT COALESCE(?, ?)").withParams("a", "b").returns("a").check(); + assertQuery("SELECT COALESCE(?, ?)").withParams(22, 33).returns(22).check(); + assertQuery("SELECT COALESCE(?, ?)").withParams(12.2, "b").returns("12.2").check(); + assertQuery("SELECT COALESCE(?, ?)").withParams(12, "b").returns("12").check(); + assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1).returns("INTEGER").check(); + assertQuery("SELECT UPPER(TYPEOF(?))").withParams(1d).returns("DOUBLE").check(); } } 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 e191c7effbee9..a13d4b5f59b91 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 @@ -88,7 +88,7 @@ // ProjectScanMergeRuleTest.class, // CalciteQueryProcessorTest.class, // CalciteErrorHandlilngIntegrationTest.class, -// JdbcQueryTest.class, + JdbcQueryTest.class, // JdbcCrossEngineTest.class, // CalciteBasicSecondaryIndexIntegrationTest.class, // CancelTest.class, From 1cb9423e7e5f9e2c157a1ab2c4159bf333682694 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 25 Oct 2024 15:30:03 +0300 Subject: [PATCH 22/43] before full tests --- .../query/calcite/CalciteQueryProcessor.java | 6 +-- .../calcite/exec/ExecutionServiceImpl.java | 2 +- .../query/calcite/prepare/IgnitePlanner.java | 4 +- .../calcite/prepare/IgniteSqlValidator.java | 24 +++++------- .../calcite/prepare/PlanningContext.java | 10 ++--- .../calcite/integration/DataTypesTest.java | 4 +- .../DynamicParametersIntegrationTest.java | 38 +++++++++---------- 7 files changed, 42 insertions(+), 46 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 8cd78b077cd82..5d5c6fa341c6b 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 @@ -457,7 +457,7 @@ public ExecutionService executionService() { () -> { miss.set(true); - return prepareSvc.prepareSingle(qryNode, qry.planningContext()); + return prepareSvc.prepareSingle(qryNode, qry.planningContext(true)); }); if (miss.get()) @@ -527,11 +527,11 @@ private List parseAndProcessQuery( plan0 = queryPlanCache().queryPlan( // Use source SQL to avoid redundant parsing next time. new CacheKey(schema.getName(), sql, contextKey(qryCtx), params), - () -> prepareSvc.prepareSingle(sqlNode, qry.planningContext()) + () -> prepareSvc.prepareSingle(sqlNode, qry.planningContext(validateDynParams)) ); } else - plan0 = prepareSvc.prepareSingle(sqlNode, qry.planningContext()); + plan0 = prepareSvc.prepareSingle(sqlNode, qry.planningContext(validateDynParams)); return action.apply(qry, plan0); }, schema.getName(), removeSensitive(sqlNode), qrys, params); diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java index 252358a870f52..6117796dc2ad9 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java @@ -541,7 +541,7 @@ private FieldsQueryCursor> executeDdl(RootQuery qry, DdlPlan plan) SqlInsert insertStmt = ((CreateTableCommand)plan.command()).insertStatement(); - QueryPlan dmlPlan = prepareSvc.prepareSingle(insertStmt, insQry.planningContext()); + QueryPlan dmlPlan = prepareSvc.prepareSingle(insertStmt, insQry.planningContext(false)); return executePlan(insQry, dmlPlan); } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java index b7995e96dd579..a218b34028337 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java @@ -357,7 +357,7 @@ private static boolean isAsCall(SqlNode node) { } CalciteCatalogReader catalogReader = this.catalogReader.withSchemaPath(schemaPath); - SqlValidator validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters()); + SqlValidator validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx); SqlToRelConverter sqlToRelConverter = sqlToRelConverter(validator, catalogReader, sqlToRelConverterCfg); RelRoot root = sqlToRelConverter.convertQuery(sqlNode, true, false); root = root.withRel(sqlToRelConverter.decorrelate(sqlNode, root.rel)); @@ -413,7 +413,7 @@ public String dump() { /** */ private SqlValidator validator() { if (validator == null) - validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters(), ctx.validateParameters()); + validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx); return validator; } 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 5c788382f2f79..67e7974b43d9d 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 @@ -119,19 +119,20 @@ public class IgniteSqlValidator extends SqlValidatorImpl { * @param catalogReader Catalog reader * @param typeFactory Type factory * @param config Config - * @param parameters Dynamic parameters - * @param validateParams Flag to enable validation of passed dynamic parameters. + * @param ctx Planning context */ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogReader, - IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters, boolean validateParams) { + IgniteTypeFactory typeFactory, SqlValidator.Config config, PlanningContext ctx) { super(opTab, catalogReader, typeFactory, config); - this.validateParams = validateParams; + this.validateParams = ctx.validateParameters(); + + Object[] parameters = ctx.parameters(); if (!F.isEmpty(parameters)) { dynParams = new HashMap<>(parameters.length); - for (int i = 0; i < parameters.length; ++i) + for (int i = 0; i < ctx.parameters().length; ++i) dynParams.put(i, new DynamicParameterHolder(parameters[i])); } } @@ -317,9 +318,9 @@ else if (call.getKind() == SqlKind.CAST) { } /** - * Derives the type of the given dynamic parameter. + * Derives type of the passed dynamic parameter. * - * @return {@link DynamicParameterHolder} related to {@code node} with the derived type. + * @return A dynamic parameter holder related to {@code node} with the derived type. */ private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { if (dynParamNodes == null) @@ -327,13 +328,8 @@ private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { DynamicParameterHolder pHolder = dynamicParamater(node.getIndex()); - if (pHolder.type != null && pHolder.hasValue) { - // TODO: IGNITE-23251 - Uncomment with this ticket. Passed parameters number should be validated. - //assert dynParamNodes.contains(node); - //assert node.equals(pHolder.node); - + if (pHolder.type != null && pHolder.hasValue) setValidatedNodeType(node, pHolder.type); - } else { dynParamNodes.add(node); @@ -354,7 +350,7 @@ private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { return pHolder; } - /** Binds {@code node} and {@code type} of a dynamic parameter to {@code pHolder}. */ + /** Sets {@code node} and {@code type} to {@code pHolder}. */ private void dynamicParameterType(DynamicParameterHolder pHolder, SqlDynamicParam node, RelDataType type) { assert pHolder.node == null || node.getIndex() == pHolder.node.getIndex() : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java index 3417a9f21c969..69e8488c33e37 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java @@ -48,7 +48,7 @@ public final class PlanningContext implements Context { private final Object[] parameters; /** */ - private boolean validateParams; + private boolean validateParameters; /** */ private final CancelFlag cancelFlag = new CancelFlag(new AtomicBoolean()); @@ -99,10 +99,10 @@ public Object[] parameters() { /** - * @param validateParams Flag to enable validation of passed dynamic parameters. + * @param validateParameters Flag to enable validation of passed dynamic parameters. */ - public PlanningContext validateParameters(boolean validateParams) { - this.validateParams = validateParams; + public PlanningContext validateParameters(boolean validateParameters) { + this.validateParameters = validateParameters; return this; } @@ -110,7 +110,7 @@ public PlanningContext validateParameters(boolean validateParams) { * @return Flag to enable validation of passed dynamic parameters. */ public boolean validateParameters() { - return validateParams; + return validateParameters; } // Helper methods diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 99af69516190e..6420f742d88e4 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -457,8 +457,8 @@ public void testDecimalScale() { .check(); } - /** TODO: incomment */ - @Ignore @Test + /** */ + @Test public void testIsNotDistinctFromTypeConversion() { SqlTypeName[] numerics = new SqlTypeName[] {SqlTypeName.TINYINT, SqlTypeName.SMALLINT, SqlTypeName.INTEGER, SqlTypeName.BIGINT, SqlTypeName.DECIMAL, SqlTypeName.FLOAT, SqlTypeName.DOUBLE}; diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index f337b3e6f79ba..3596296b355ea 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -100,28 +100,28 @@ public void testCasts() { /** */ @Test public void testDynamicParameters() { -// assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); -// assertQuery("SELECT COALESCE(null, ?)").withParams(13).returns(13).check(); -// assertQuery("SELECT LOWER(?)").withParams("ASD").returns("asd").check(); -// assertQuery("SELECT ?").withParams("asd").returns("asd").check(); -// assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); -// assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); -// assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); -// assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); -// assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); -// -// assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) -// .returns(Date.valueOf("2022-01-31")).check(); -// assertQuery("SELECT LAST_DAY(?)").withParams(LocalDate.parse("2022-01-01")) -// .returns(Date.valueOf("2022-01-31")).check(); + assertQuery("SELECT COALESCE(?, ?)").withParams("a", 10).returns("a").check(); + assertQuery("SELECT COALESCE(null, ?)").withParams(13).returns(13).check(); + assertQuery("SELECT LOWER(?)").withParams("ASD").returns("asd").check(); + assertQuery("SELECT ?").withParams("asd").returns("asd").check(); + assertQuery("SELECT ? + ?, LOWER(?) ").withParams(2, 2, "TeSt").returns(4, "test").check(); + assertQuery("SELECT LOWER(?), ? + ? ").withParams("TeSt", 2, 2).returns("test", 4).check(); + assertQuery("SELECT POWER(?, ?)").withParams(2, 3).returns(8d).check(); + assertQuery("SELECT SQRT(?)").withParams(4d).returns(2d).check(); + assertQuery("SELECT ? % ?").withParams(11, 10).returns(1).check(); + + assertQuery("SELECT LAST_DAY(?)").withParams(Date.valueOf("2022-01-01")) + .returns(Date.valueOf("2022-01-31")).check(); + assertQuery("SELECT LAST_DAY(?)").withParams(LocalDate.parse("2022-01-01")) + .returns(Date.valueOf("2022-01-31")).check(); createAndPopulateTable(); -// assertQuery("SELECT name LIKE '%' || ? || '%' FROM person where name is not null").withParams("go") -// .returns(true).returns(false).returns(false).returns(false).returns(false).check(); -// -// assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ?").withParams("I%", 1) -// .returns(0).check(); + assertQuery("SELECT name LIKE '%' || ? || '%' FROM person where name is not null").withParams("go") + .returns(true).returns(false).returns(false).returns(false).returns(false).check(); + + assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ?").withParams("I%", 1) + .returns(0).check(); assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ? OFFSET ?").withParams("I%", 1, 1) .returns(2).check(); From e1910163e221c91f11ea8d74888c72ac332d170e Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 25 Oct 2024 15:58:09 +0300 Subject: [PATCH 23/43] uncommited some tests --- .../AbstractBasicIntegrationTest.java | 1 - .../DynamicParametersIntegrationTest.java | 7 +- .../QueryWithPartitionsIntegrationTest.java | 18 +-- .../testsuites/IgniteCalciteTestSuite.java | 32 ++--- .../testsuites/IntegrationTestSuite.java | 112 +++++++++--------- 5 files changed, 88 insertions(+), 82 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java index bbfadff7a46a8..c337d2a8985a6 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java @@ -205,7 +205,6 @@ protected IgniteCache createAndPopulateTable(Ignite ignite, i put(ignite, person, idx++, new Employer("Ilya", 15d)); put(ignite, person, idx++, new Employer("Roma", 10d)); put(ignite, person, idx++, new Employer("Roma", 10d)); - put(ignite, person, idx, new Employer("15", 15d)); return person; } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index 3596296b355ea..f5ccf4924aeba 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.UUID; import org.apache.calcite.sql.validate.SqlValidatorException; +import org.apache.ignite.IgniteCache; import org.apache.ignite.internal.util.typedef.F; import org.junit.Test; @@ -77,7 +78,9 @@ public void testCasts() { assertQuery("SELECT ?::VARCHAR").withParams(1).returns("1").check(); assertQuery("SELECT CAST(? as VARCHAR)").withParams(1).returns("1").check(); - createAndPopulateTable(); + IgniteCache cache = createAndPopulateTable(); + + cache.put(cache.size(), new Employer("15", 15d)); assertQuery("SELECT name FROM Person WHERE id=?::INTEGER").withParams("2").returns("Ilya").check(); assertQuery("SELECT name FROM Person WHERE id=CAST(? as INTEGER)").withParams("2").returns("Ilya").check(); @@ -118,7 +121,7 @@ public void testDynamicParameters() { createAndPopulateTable(); assertQuery("SELECT name LIKE '%' || ? || '%' FROM person where name is not null").withParams("go") - .returns(true).returns(false).returns(false).returns(false).returns(false).check(); + .returns(true).returns(false).returns(false).returns(false).check(); assertQuery("SELECT id FROM person WHERE name LIKE ? ORDER BY id LIMIT ?").withParams("I%", 1) .returns(0).check(); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java index 660ee91dc3eb9..e5e6833497477 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java @@ -34,6 +34,7 @@ import org.apache.ignite.internal.processors.query.QueryContext; import org.apache.ignite.internal.processors.query.calcite.QueryChecker; import org.apache.ignite.internal.util.typedef.F; +import org.apache.ignite.internal.util.typedef.X; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -96,6 +97,9 @@ public static List parameters() { /** {@inheritDoc} */ @Override protected List> sql(String sql, Object... params) { + if (params.length == 1 && params[0] == X.EMPTY_OBJECT_ARRAY) + params = X.EMPTY_OBJECT_ARRAY; + return sql(local ? grid(0) : client, sql, params); } @@ -138,7 +142,7 @@ public static List parameters() { /** */ @Test public void testSingle() { - Stream.of(Pair.of("SELECT * FROM T1", null), + Stream.of(Pair.of("SELECT * FROM T1", X.EMPTY_OBJECT_ARRAY), Pair.of("SELECT * FROM T1 WHERE ID < ?", ENTRIES_COUNT)) .forEach(query -> { long cnt = sql(query.left, query.right).size(); @@ -146,7 +150,7 @@ public void testSingle() { assertEquals(cacheSize("T1_CACHE", parts), cnt); }); - Stream.of(Pair.of("SELECT count(*) FROM T1", null), + Stream.of(Pair.of("SELECT count(*) FROM T1", X.EMPTY_OBJECT_ARRAY), Pair.of("SELECT count(*) FROM T1 WHERE ID < ?", ENTRIES_COUNT)) .forEach(query -> { Long cnt = (Long)sql(query.left, query.right).get(0).get(0); @@ -158,7 +162,7 @@ public void testSingle() { /** */ @Test public void testReplicated() { - Stream.of(Pair.of("select * from DICT", null), + Stream.of(Pair.of("select * from DICT", X.EMPTY_OBJECT_ARRAY), Pair.of("select * from DICT where id < ?", ENTRIES_COUNT)) .forEach(query -> { List> res = sql(query.left, query.right); @@ -166,7 +170,7 @@ public void testReplicated() { assertEquals(res.size(), cacheSize("DICT_CACHE")); }); - Stream.of(Pair.of("select count(*) from DICT", null), + Stream.of(Pair.of("select count(*) from DICT", X.EMPTY_OBJECT_ARRAY), Pair.of("select count(*) from DICT where id < ?", ENTRIES_COUNT)) .forEach(query -> { Long size = (Long)sql(query.left, query.right).get(0).get(0); @@ -201,7 +205,7 @@ private void testJoin(String table1, String table2, String joinCol) { @Test public void testInsertFromSelect() { Stream.of(Pair.of("SELECT ID, IDX_VAL, VAL FROM T1 WHERE ID < ?", ENTRIES_COUNT), - Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", null)) + Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", X.EMPTY_OBJECT_ARRAY)) .forEach(query -> { try { sql("CREATE TABLE T3(ID INT PRIMARY KEY, IDX_VAL VARCHAR, VAL VARCHAR) WITH cache_name=t3_cache,backups=1"); @@ -219,7 +223,7 @@ public void testInsertFromSelect() { /** */ @Test public void testDelete() { - Stream.of(Pair.of("DELETE FROM T3 WHERE ID < ?", ENTRIES_COUNT), Pair.of("DELETE FROM T3", null)) + Stream.of(Pair.of("DELETE FROM T3 WHERE ID < ?", ENTRIES_COUNT), Pair.of("DELETE FROM T3", X.EMPTY_OBJECT_ARRAY)) .forEach(query -> { try { sql("CREATE TABLE T3(ID INT PRIMARY KEY, IDX_VAL VARCHAR, VAL VARCHAR) WITH cache_name=t3_cache,backups=1"); @@ -245,7 +249,7 @@ public void testDelete() { @Test public void testCreateTableAsSelect() { Stream.of(Pair.of("SELECT ID, IDX_VAL, VAL FROM T1 WHERE ID < ?", ENTRIES_COUNT), - Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", null)) + Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", X.EMPTY_OBJECT_ARRAY)) .forEach(query -> { try { sql("CREATE TABLE T3(ID, IDX_VAL, VAL) WITH cache_name=t3_cache,backups=1 AS " + query.left, query.right); diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java index 5c08c0a533562..4dc08f9a75841 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java +++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java @@ -42,24 +42,24 @@ //ExecutionTestSuite.class, IntegrationTestSuite.class, -// ClosableIteratorsHolderTest.class, -// MemoryTrackerTest.class, -// QueryCheckerTest.class, -// SqlCustomParserTest.class, -// SqlReservedWordsTest.class, -// IgniteSqlFunctionsTest.class, -// LogicalRelImplementorTest.class, + ClosableIteratorsHolderTest.class, + MemoryTrackerTest.class, + QueryCheckerTest.class, + SqlCustomParserTest.class, + SqlReservedWordsTest.class, + IgniteSqlFunctionsTest.class, + LogicalRelImplementorTest.class, // ScriptTestSuite.class, -// CalciteCommunicationMessageSerializationTest.class, -// -// NumericTypesPrecisionsTest.class, -// -// KeyFilteringCursorTest.class, -// SqlTransactionsIsolationTest.class, -// SqlTransactionsUnsupportedModesTest.class, -// -// JdbcThinTransactionalSelfTest.class, + CalciteCommunicationMessageSerializationTest.class, + + NumericTypesPrecisionsTest.class, + + KeyFilteringCursorTest.class, + SqlTransactionsIsolationTest.class, + SqlTransactionsUnsupportedModesTest.class, + + JdbcThinTransactionalSelfTest.class, }) public class IgniteCalciteTestSuite { } 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 a13d4b5f59b91..dc27a636e7775 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 @@ -84,65 +84,65 @@ */ @RunWith(Suite.class) @Suite.SuiteClasses({ -// OrToUnionRuleTest.class, -// ProjectScanMergeRuleTest.class, -// CalciteQueryProcessorTest.class, -// CalciteErrorHandlilngIntegrationTest.class, + OrToUnionRuleTest.class, + ProjectScanMergeRuleTest.class, + CalciteQueryProcessorTest.class, + CalciteErrorHandlilngIntegrationTest.class, JdbcQueryTest.class, -// JdbcCrossEngineTest.class, -// CalciteBasicSecondaryIndexIntegrationTest.class, -// CancelTest.class, -// DateTimeTest.class, -// LimitOffsetIntegrationTest.class, -// SqlFieldsQueryUsageTest.class, -// AggregatesIntegrationTest.class, -// MetadataIntegrationTest.class, -// RunningQueriesIntegrationTest.class, -// SqlDiagnosticIntegrationTest.class, -// SortAggregateIntegrationTest.class, -// TableDdlIntegrationTest.class, -// IndexDdlIntegrationTest.class, -// UserDdlIntegrationTest.class, -// KillCommandDdlIntegrationTest.class, -// KillQueryCommandDdlIntegrationTest.class, -// StatisticsCommandDdlIntegrationTest.class, -// FunctionsTest.class, -// StdSqlOperatorsTest.class, -// TableDmlIntegrationTest.class, + JdbcCrossEngineTest.class, + CalciteBasicSecondaryIndexIntegrationTest.class, + CancelTest.class, + DateTimeTest.class, + LimitOffsetIntegrationTest.class, + SqlFieldsQueryUsageTest.class, + AggregatesIntegrationTest.class, + MetadataIntegrationTest.class, + RunningQueriesIntegrationTest.class, + SqlDiagnosticIntegrationTest.class, + SortAggregateIntegrationTest.class, + TableDdlIntegrationTest.class, + IndexDdlIntegrationTest.class, + UserDdlIntegrationTest.class, + KillCommandDdlIntegrationTest.class, + KillQueryCommandDdlIntegrationTest.class, + StatisticsCommandDdlIntegrationTest.class, + FunctionsTest.class, + StdSqlOperatorsTest.class, + TableDmlIntegrationTest.class, DataTypesTest.class, -// IndexSpoolIntegrationTest.class, -// HashSpoolIntegrationTest.class, -// IndexScanlIntegrationTest.class, -// IndexScanMultiNodeIntegrationTest.class, -// SetOpIntegrationTest.class, -// UnstableTopologyTest.class, -// JoinCommuteRulesTest.class, -// ServerStatisticsIntegrationTest.class, -// JoinIntegrationTest.class, -// IntervalTest.class, -// UserDefinedFunctionsIntegrationTest.class, -// UserDefinedFunctionsIntegrationTransactionalTest.class, -// CorrelatesIntegrationTest.class, -// SystemViewsIntegrationTest.class, -// IndexRebuildIntegrationTest.class, -// QueryEngineConfigurationIntegrationTest.class, -// IndexMultiRangeScanIntegrationTest.class, -// KeepBinaryIntegrationTest.class, -// LocalQueryIntegrationTest.class, -// QueryWithPartitionsIntegrationTest.class, -// QueryMetadataIntegrationTest.class, -// MemoryQuotasIntegrationTest.class, -// LocalDateTimeSupportTest.class, + IndexSpoolIntegrationTest.class, + HashSpoolIntegrationTest.class, + IndexScanlIntegrationTest.class, + IndexScanMultiNodeIntegrationTest.class, + SetOpIntegrationTest.class, + UnstableTopologyTest.class, + JoinCommuteRulesTest.class, + ServerStatisticsIntegrationTest.class, + JoinIntegrationTest.class, + IntervalTest.class, + UserDefinedFunctionsIntegrationTest.class, + UserDefinedFunctionsIntegrationTransactionalTest.class, + CorrelatesIntegrationTest.class, + SystemViewsIntegrationTest.class, + IndexRebuildIntegrationTest.class, + QueryEngineConfigurationIntegrationTest.class, + IndexMultiRangeScanIntegrationTest.class, + KeepBinaryIntegrationTest.class, + LocalQueryIntegrationTest.class, + QueryWithPartitionsIntegrationTest.class, + QueryMetadataIntegrationTest.class, + MemoryQuotasIntegrationTest.class, + LocalDateTimeSupportTest.class, DynamicParametersIntegrationTest.class, -// ExpiredEntriesIntegrationTest.class, -// TimeoutIntegrationTest.class, -// PartitionPruneTest.class, -// JoinRehashIntegrationTest.class, -// IndexWithSameNameCalciteTest.class, -// AuthorizationIntegrationTest.class, -// DdlTransactionCalciteSelfTest.class, -// MultiLineQueryTest.class, -// ViewsIntegrationTest.class, + ExpiredEntriesIntegrationTest.class, + TimeoutIntegrationTest.class, + PartitionPruneTest.class, + JoinRehashIntegrationTest.class, + IndexWithSameNameCalciteTest.class, + AuthorizationIntegrationTest.class, + DdlTransactionCalciteSelfTest.class, + MultiLineQueryTest.class, + ViewsIntegrationTest.class, }) public class IntegrationTestSuite { } From 791f9e3f643b73f8f1dbace84a0a39861cc3ff3c Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Fri, 25 Oct 2024 16:18:26 +0300 Subject: [PATCH 24/43] removed params check, restored tests --- .../query/calcite/CalciteQueryProcessor.java | 13 ++--- .../processors/query/calcite/RootQuery.java | 4 +- .../calcite/exec/ExecutionServiceImpl.java | 2 +- .../query/calcite/prepare/IgnitePlanner.java | 4 +- .../calcite/prepare/IgniteSqlValidator.java | 55 +++++-------------- .../calcite/prepare/PlanningContext.java | 19 ------- .../query/calcite/util/IgniteResource.java | 4 -- .../AbstractBasicIntegrationTest.java | 2 +- .../calcite/integration/DataTypesTest.java | 10 ---- .../DynamicParametersIntegrationTest.java | 6 +- .../QueryWithPartitionsIntegrationTest.java | 18 +++--- .../testsuites/IgniteCalciteTestSuite.java | 6 +- 12 files changed, 39 insertions(+), 104 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 5d5c6fa341c6b..a21ba720ced04 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 @@ -393,7 +393,7 @@ public ExecutionService executionService() { String sql, Object... params ) throws IgniteSQLException { - return parseAndProcessQuery(qryCtx, executionSvc::executePlan, schemaName, sql, true, params); + return parseAndProcessQuery(qryCtx, executionSvc::executePlan, schemaName, sql, params); } /** {@inheritDoc} */ @@ -402,7 +402,7 @@ public ExecutionService executionService() { String schemaName, String sql ) throws IgniteSQLException { - return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, true), schemaName, sql, false); + return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, true), schemaName, sql); } /** {@inheritDoc} */ @@ -411,7 +411,7 @@ public ExecutionService executionService() { String schemaName, String sql ) throws IgniteSQLException { - return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, false), schemaName, sql, false); + return parseAndProcessQuery(ctx, (qry, plan) -> fieldsMeta(plan, false), schemaName, sql); } /** {@inheritDoc} */ @@ -457,7 +457,7 @@ public ExecutionService executionService() { () -> { miss.set(true); - return prepareSvc.prepareSingle(qryNode, qry.planningContext(true)); + return prepareSvc.prepareSingle(qryNode, qry.planningContext()); }); if (miss.get()) @@ -487,7 +487,6 @@ private List parseAndProcessQuery( BiFunction, QueryPlan, T> action, @Nullable String schemaName, String sql, - boolean validateDynParams, Object... params ) throws IgniteSQLException { ensureTransactionModeSupported(qryCtx); @@ -527,11 +526,11 @@ private List parseAndProcessQuery( plan0 = queryPlanCache().queryPlan( // Use source SQL to avoid redundant parsing next time. new CacheKey(schema.getName(), sql, contextKey(qryCtx), params), - () -> prepareSvc.prepareSingle(sqlNode, qry.planningContext(validateDynParams)) + () -> prepareSvc.prepareSingle(sqlNode, qry.planningContext()) ); } else - plan0 = prepareSvc.prepareSingle(sqlNode, qry.planningContext(validateDynParams)); + plan0 = prepareSvc.prepareSingle(sqlNode, qry.planningContext()); return action.apply(qry, plan0); }, schema.getName(), removeSensitive(sqlNode), qrys, params); diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java index 74a0a07c3c9c3..242e355d22c7e 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/RootQuery.java @@ -293,7 +293,7 @@ public void run(ExecutionContext ctx, ExecutionPlan plan, FieldsMetadata m } /** */ - public PlanningContext planningContext(boolean validateParams) { + public PlanningContext planningContext() { synchronized (mux) { if (state == QueryState.CLOSED || state == QueryState.CLOSING) throw queryCanceledException(); @@ -323,7 +323,7 @@ public PlanningContext planningContext(boolean validateParams) { } } - return pctx.validateParameters(validateParams); + return pctx; } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java index 6117796dc2ad9..252358a870f52 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java @@ -541,7 +541,7 @@ private FieldsQueryCursor> executeDdl(RootQuery qry, DdlPlan plan) SqlInsert insertStmt = ((CreateTableCommand)plan.command()).insertStatement(); - QueryPlan dmlPlan = prepareSvc.prepareSingle(insertStmt, insQry.planningContext(false)); + QueryPlan dmlPlan = prepareSvc.prepareSingle(insertStmt, insQry.planningContext()); return executePlan(insQry, dmlPlan); } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java index a218b34028337..a66f2564ef7c2 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgnitePlanner.java @@ -357,7 +357,7 @@ private static boolean isAsCall(SqlNode node) { } CalciteCatalogReader catalogReader = this.catalogReader.withSchemaPath(schemaPath); - SqlValidator validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx); + SqlValidator validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters()); SqlToRelConverter sqlToRelConverter = sqlToRelConverter(validator, catalogReader, sqlToRelConverterCfg); RelRoot root = sqlToRelConverter.convertQuery(sqlNode, true, false); root = root.withRel(sqlToRelConverter.decorrelate(sqlNode, root.rel)); @@ -413,7 +413,7 @@ public String dump() { /** */ private SqlValidator validator() { if (validator == null) - validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx); + validator = new IgniteSqlValidator(operatorTbl, catalogReader, typeFactory, validatorCfg, ctx.parameters()); return validator; } 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 67e7974b43d9d..f4966881b620c 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 @@ -109,9 +109,6 @@ public class IgniteSqlValidator extends SqlValidatorImpl { /** Detected dynamic parameter nodes. */ @Nullable private Set dynParamNodes; - /** Flag to enable validation of passed dynamic parameters. */ - private final boolean validateParams; - /** * Creates a validator. * @@ -119,20 +116,16 @@ public class IgniteSqlValidator extends SqlValidatorImpl { * @param catalogReader Catalog reader * @param typeFactory Type factory * @param config Config - * @param ctx Planning context + * @param parameters Dynamic parameters */ public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogReader, - IgniteTypeFactory typeFactory, SqlValidator.Config config, PlanningContext ctx) { + IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters) { super(opTab, catalogReader, typeFactory, config); - this.validateParams = ctx.validateParameters(); - - Object[] parameters = ctx.parameters(); - if (!F.isEmpty(parameters)) { dynParams = new HashMap<>(parameters.length); - for (int i = 0; i < ctx.parameters().length; ++i) + for (int i = 0; i < parameters.length; ++i) dynParams.put(i, new DynamicParameterHolder(parameters[i])); } } @@ -480,29 +473,6 @@ private SqlNode rewriteTableToQuery(SqlNode from) { return isSystemFieldName(field.getName()); } - /** {@inheritDoc} */ - @Override public SqlNode validate(SqlNode topNode) { - SqlNode res = super.validate(topNode); - - if (validateParams) - validateDynamicParameters(); - - return res; - } - - /** TODO: IGNITE-23251 - validate number of query's dynamic parameters and the passed values. */ - private void validateDynamicParameters() { - if (F.isEmpty(dynParams)) - return; - - dynParams.forEach((idx, pHolder) -> { - assert pHolder.type != null : "No type inferred for dynamic parameter #" + idx; - - if (unknownType.equals(pHolder.type) || !pHolder.hasValue) - throw newValidationError(pHolder.node, IgniteResource.INSTANCE.dynamicParameterValidation(idx + 1)); - }); - } - /** */ private void validateAggregateFunction(SqlCall call, SqlAggFunction aggFunction) { if (!SqlKind.AGGREGATE.contains(aggFunction.kind)) @@ -624,8 +594,9 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (node instanceof SqlDynamicParam) - inferDynamicParamType(inferredType, (SqlDynamicParam)node); + if (node instanceof SqlDynamicParam && inferDynamicParamType(inferredType, (SqlDynamicParam)node)) { + // No-op. + } else if (node instanceof SqlCall) { final SqlCall call = (SqlCall)node; @@ -676,15 +647,17 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** */ - private void inferDynamicParamType(RelDataType inferredType, SqlDynamicParam node) { + /** @return {@code True} if type was successfully set. {@code False} if type is not determined. */ + private boolean inferDynamicParamType(RelDataType inferredType, SqlDynamicParam node) { DynamicParameterHolder pHolder = deriveDynamicParamType(node); - // Exit if the type is determined or is unknown. - if (inferredType.equals(unknownType) || inferredType.getFamily().equals(pHolder.type.getFamily())) - return; + if (unknownType.equals(inferredType) && unknownType.equals(pHolder.type)) + return false; + + if (!inferredType.equals(unknownType) && !inferredType.getFamily().equals(pHolder.type.getFamily())) + dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(inferredType, true)); - dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(inferredType, true)); + return true; } /** {@inheritDoc} */ diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java index 69e8488c33e37..2f222083c6a61 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java @@ -47,9 +47,6 @@ public final class PlanningContext implements Context { /** */ private final Object[] parameters; - /** */ - private boolean validateParameters; - /** */ private final CancelFlag cancelFlag = new CancelFlag(new AtomicBoolean()); @@ -97,22 +94,6 @@ public Object[] parameters() { return parameters; } - - /** - * @param validateParameters Flag to enable validation of passed dynamic parameters. - */ - public PlanningContext validateParameters(boolean validateParameters) { - this.validateParameters = validateParameters; - - return this; - } - /** - * @return Flag to enable validation of passed dynamic parameters. - */ - public boolean validateParameters() { - return validateParameters; - } - // Helper methods /** * @return Sql conformance. diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java index 71c8b6f10ed01..f4d7181c6fd3a 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/util/IgniteResource.java @@ -80,8 +80,4 @@ public interface IgniteResource { /** */ @Resources.BaseMessage("Operator ''CAST'' supports only the parameters: value and target type.") Resources.ExInst invalidCastParameters(); - - /** */ - @Resources.BaseMessage("No value passed for dynamic parameter {0} or its type is unknown.") - Resources.ExInst dynamicParameterValidation(int value); } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java index c337d2a8985a6..62be28c666968 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/AbstractBasicIntegrationTest.java @@ -204,7 +204,7 @@ protected IgniteCache createAndPopulateTable(Ignite ignite, i put(ignite, person, idx++, new Employer(null, 15d)); put(ignite, person, idx++, new Employer("Ilya", 15d)); put(ignite, person, idx++, new Employer("Roma", 10d)); - put(ignite, person, idx++, new Employer("Roma", 10d)); + put(ignite, person, idx, new Employer("Roma", 10d)); return person; } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java index 6420f742d88e4..a577fa9f96e6e 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DataTypesTest.java @@ -18,8 +18,6 @@ package org.apache.ignite.internal.processors.query.calcite.integration; import java.math.BigDecimal; -import java.util.Arrays; -import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.UUID; @@ -36,9 +34,7 @@ import org.apache.ignite.internal.processors.query.calcite.exec.exp.IgniteSqlFunctions; import org.apache.ignite.internal.processors.query.calcite.hint.HintDefinition; import org.apache.ignite.internal.util.typedef.F; -import org.junit.Ignore; import org.junit.Test; -import org.junit.runners.Parameterized; import static org.apache.ignite.internal.processors.query.calcite.CalciteQueryProcessor.FRAMEWORK_CONFIG; import static org.junit.Assume.assumeTrue; @@ -47,12 +43,6 @@ * Test SQL data types. */ public class DataTypesTest extends AbstractBasicIntegrationTransactionalTest { - /** @return Test parameters. */ - @Parameterized.Parameters(name = "sqlTxMode={0}") - public static Collection parameters() { - return Arrays.asList(SqlTransactionMode.NONE); - } - /** Tests Other type. */ @Test public void testOtherType() { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index f5ccf4924aeba..78d825a9e3dcc 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -26,8 +26,8 @@ import java.time.Period; import java.util.List; import java.util.UUID; -import org.apache.calcite.sql.validate.SqlValidatorException; import org.apache.ignite.IgniteCache; +import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.util.typedef.F; import org.junit.Test; @@ -65,9 +65,9 @@ public void testMetadataTypesForDynamicParameters() { /** */ @Test public void testMissedValue() { - assertThrows("SELECT ?", SqlValidatorException.class, "No value passed for dynamic parameter 1 or its type is unknown."); + assertThrows("SELECT ?", IgniteSQLException.class, "Illegal use of dynamic parameter"); - assertThrows("SELECT ?, ?", SqlValidatorException.class, "No value passed for dynamic parameter 2 or its type is unknown.", "arg0"); + assertThrows("SELECT ?, ?", IgniteSQLException.class, "Illegal use of dynamic parameter", "arg0"); } /** */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java index e5e6833497477..660ee91dc3eb9 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/QueryWithPartitionsIntegrationTest.java @@ -34,7 +34,6 @@ import org.apache.ignite.internal.processors.query.QueryContext; import org.apache.ignite.internal.processors.query.calcite.QueryChecker; import org.apache.ignite.internal.util.typedef.F; -import org.apache.ignite.internal.util.typedef.X; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -97,9 +96,6 @@ public static List parameters() { /** {@inheritDoc} */ @Override protected List> sql(String sql, Object... params) { - if (params.length == 1 && params[0] == X.EMPTY_OBJECT_ARRAY) - params = X.EMPTY_OBJECT_ARRAY; - return sql(local ? grid(0) : client, sql, params); } @@ -142,7 +138,7 @@ public static List parameters() { /** */ @Test public void testSingle() { - Stream.of(Pair.of("SELECT * FROM T1", X.EMPTY_OBJECT_ARRAY), + Stream.of(Pair.of("SELECT * FROM T1", null), Pair.of("SELECT * FROM T1 WHERE ID < ?", ENTRIES_COUNT)) .forEach(query -> { long cnt = sql(query.left, query.right).size(); @@ -150,7 +146,7 @@ public void testSingle() { assertEquals(cacheSize("T1_CACHE", parts), cnt); }); - Stream.of(Pair.of("SELECT count(*) FROM T1", X.EMPTY_OBJECT_ARRAY), + Stream.of(Pair.of("SELECT count(*) FROM T1", null), Pair.of("SELECT count(*) FROM T1 WHERE ID < ?", ENTRIES_COUNT)) .forEach(query -> { Long cnt = (Long)sql(query.left, query.right).get(0).get(0); @@ -162,7 +158,7 @@ public void testSingle() { /** */ @Test public void testReplicated() { - Stream.of(Pair.of("select * from DICT", X.EMPTY_OBJECT_ARRAY), + Stream.of(Pair.of("select * from DICT", null), Pair.of("select * from DICT where id < ?", ENTRIES_COUNT)) .forEach(query -> { List> res = sql(query.left, query.right); @@ -170,7 +166,7 @@ public void testReplicated() { assertEquals(res.size(), cacheSize("DICT_CACHE")); }); - Stream.of(Pair.of("select count(*) from DICT", X.EMPTY_OBJECT_ARRAY), + Stream.of(Pair.of("select count(*) from DICT", null), Pair.of("select count(*) from DICT where id < ?", ENTRIES_COUNT)) .forEach(query -> { Long size = (Long)sql(query.left, query.right).get(0).get(0); @@ -205,7 +201,7 @@ private void testJoin(String table1, String table2, String joinCol) { @Test public void testInsertFromSelect() { Stream.of(Pair.of("SELECT ID, IDX_VAL, VAL FROM T1 WHERE ID < ?", ENTRIES_COUNT), - Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", X.EMPTY_OBJECT_ARRAY)) + Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", null)) .forEach(query -> { try { sql("CREATE TABLE T3(ID INT PRIMARY KEY, IDX_VAL VARCHAR, VAL VARCHAR) WITH cache_name=t3_cache,backups=1"); @@ -223,7 +219,7 @@ public void testInsertFromSelect() { /** */ @Test public void testDelete() { - Stream.of(Pair.of("DELETE FROM T3 WHERE ID < ?", ENTRIES_COUNT), Pair.of("DELETE FROM T3", X.EMPTY_OBJECT_ARRAY)) + Stream.of(Pair.of("DELETE FROM T3 WHERE ID < ?", ENTRIES_COUNT), Pair.of("DELETE FROM T3", null)) .forEach(query -> { try { sql("CREATE TABLE T3(ID INT PRIMARY KEY, IDX_VAL VARCHAR, VAL VARCHAR) WITH cache_name=t3_cache,backups=1"); @@ -249,7 +245,7 @@ public void testDelete() { @Test public void testCreateTableAsSelect() { Stream.of(Pair.of("SELECT ID, IDX_VAL, VAL FROM T1 WHERE ID < ?", ENTRIES_COUNT), - Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", X.EMPTY_OBJECT_ARRAY)) + Pair.of("SELECT ID, IDX_VAL, VAL FROM T1", null)) .forEach(query -> { try { sql("CREATE TABLE T3(ID, IDX_VAL, VAL) WITH cache_name=t3_cache,backups=1 AS " + query.left, query.right); diff --git a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java index 4dc08f9a75841..df909b46f17db 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java +++ b/modules/calcite/src/test/java/org/apache/ignite/testsuites/IgniteCalciteTestSuite.java @@ -38,8 +38,8 @@ */ @RunWith(Suite.class) @Suite.SuiteClasses({ - //PlannerTestSuite.class, - //ExecutionTestSuite.class, + PlannerTestSuite.class, + ExecutionTestSuite.class, IntegrationTestSuite.class, ClosableIteratorsHolderTest.class, @@ -50,7 +50,7 @@ IgniteSqlFunctionsTest.class, LogicalRelImplementorTest.class, -// ScriptTestSuite.class, + ScriptTestSuite.class, CalciteCommunicationMessageSerializationTest.class, NumericTypesPrecisionsTest.class, From 36e752b308bb63c5cd07241e5f0aaa82d4b0fcc4 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Tue, 29 Oct 2024 14:02:01 +0300 Subject: [PATCH 25/43] merged master --- .../calcite/prepare/IgniteSqlValidator.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) 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 f4966881b620c..a7d13904a2aa2 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 @@ -594,10 +594,10 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (node instanceof SqlDynamicParam && inferDynamicParamType(inferredType, (SqlDynamicParam)node)) { - // No-op. - } - else if (node instanceof SqlCall) { + if (inferDynamicParamType(inferredType, node)) + return; + + if (node instanceof SqlCall) { final SqlCall call = (SqlCall)node; // SqlStdOperatorTable::IS_NULL and SqlStdOperatorTable::IS_NOT_NULL overrides with VARCHAR_1024 for argument type inference. @@ -647,15 +647,20 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** @return {@code True} if type was successfully set. {@code False} if type is not determined. */ - private boolean inferDynamicParamType(RelDataType inferredType, SqlDynamicParam node) { - DynamicParameterHolder pHolder = deriveDynamicParamType(node); + /** @return {@code True} if a type was successfully set to {@code node}. {@code False} if type is not determined. */ + private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { + if (!(node instanceof SqlDynamicParam)) + return false; + + SqlDynamicParam dpNode = (SqlDynamicParam)node; + + DynamicParameterHolder pHolder = deriveDynamicParamType(dpNode); if (unknownType.equals(inferredType) && unknownType.equals(pHolder.type)) return false; if (!inferredType.equals(unknownType) && !inferredType.getFamily().equals(pHolder.type.getFamily())) - dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(inferredType, true)); + dynamicParameterType(pHolder, dpNode, typeFactory.createTypeWithNullability(inferredType, true)); return true; } From b15aa3b885da6f8f043d898f300280bd75d3ae9f Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sat, 28 Dec 2024 13:34:48 +0300 Subject: [PATCH 26/43] impl --- .../calcite/prepare/IgniteSqlValidator.java | 200 +++++++----------- .../calcite/prepare/PlanningContext.java | 7 +- 2 files changed, 77 insertions(+), 130 deletions(-) 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 d250b9260de7e..2a837c1e6924d 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 @@ -21,8 +21,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -103,11 +101,8 @@ public class IgniteSqlValidator extends SqlValidatorImpl { HUMAN_READABLE_ALIASES_FOR = Collections.unmodifiableSet(kinds); } - /** Validated dynamic parameters with the passed values if any. */ - @Nullable private Map dynParams; - - /** Detected dynamic parameter nodes. */ - @Nullable private Set dynParamNodes; + /** Dynamic parameters. */ + private final @Nullable Object[] parameters; /** * Creates a validator. @@ -115,29 +110,19 @@ public class IgniteSqlValidator extends SqlValidatorImpl { * @param opTab Operator table * @param catalogReader Catalog reader * @param typeFactory Type factory - * @param config Config + * @param cfg Config * @param parameters Dynamic parameters */ - public IgniteSqlValidator(SqlOperatorTable opTab, CalciteCatalogReader catalogReader, - IgniteTypeFactory typeFactory, SqlValidator.Config config, Object[] parameters) { - super(opTab, catalogReader, typeFactory, config); - - if (!F.isEmpty(parameters)) { - dynParams = new HashMap<>(parameters.length); - - for (int i = 0; i < parameters.length; ++i) - dynParams.put(i, new DynamicParameterHolder(parameters[i])); - } - } - - /** */ - @Nullable private DynamicParameterHolder dynamicParamater(int idx) { - assert idx >= 0; - - if (dynParams == null) - dynParams = new HashMap<>(); + public IgniteSqlValidator( + SqlOperatorTable opTab, + CalciteCatalogReader catalogReader, + IgniteTypeFactory typeFactory, + SqlValidator.Config cfg, + @Nullable Object[] parameters + ) { + super(opTab, catalogReader, typeFactory, cfg); - return dynParams.computeIfAbsent(idx, idx0 -> new DynamicParameterHolder()); + this.parameters = parameters; } /** {@inheritDoc} */ @@ -277,12 +262,19 @@ private void checkIntegerLimit(SqlNode n, String nodeName) { throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); } else if (n instanceof SqlDynamicParam) { - SqlDynamicParam dynamicParam = (SqlDynamicParam)n; + // will fail in params check. + if (F.isEmpty(parameters)) + return; - DynamicParameterHolder pHolder = deriveDynamicParamType(dynamicParam); + int idx = ((SqlDynamicParam)n).getIndex(); - if (pHolder.hasValue && pHolder.val instanceof Integer && (Integer)pHolder.val < 0) - throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); + if (idx < parameters.length) { + Object param = parameters[idx]; + if (parameters[idx] instanceof Integer) { + if ((Integer)param < 0) + throw newValidationError(n, IgniteResource.INSTANCE.correctIntegerLimit(nodeName)); + } + } } } @@ -302,59 +294,6 @@ else if (call.getKind() == SqlKind.CAST) { super.validateCall(call, scope); } - /** {@inheritDoc} */ - @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { - if (expr instanceof SqlDynamicParam) - return deriveDynamicParamType((SqlDynamicParam)expr).type; - - return super.deriveType(scope, expr); - } - - /** - * Derives type of the passed dynamic parameter. - * - * @return A dynamic parameter holder related to {@code node} with the derived type. - */ - private DynamicParameterHolder deriveDynamicParamType(SqlDynamicParam node) { - if (dynParamNodes == null) - dynParamNodes = new HashSet<>(); - - DynamicParameterHolder pHolder = dynamicParamater(node.getIndex()); - - if (pHolder.type != null && pHolder.hasValue) - setValidatedNodeType(node, pHolder.type); - else { - dynParamNodes.add(node); - - if (pHolder.hasValue) { - RelDataType type = pHolder.val == null - ? typeFactory().createSqlType(SqlTypeName.NULL) - : typeFactory().toSql(typeFactory().createType(pHolder.val.getClass())); - - dynamicParameterType(pHolder, node, typeFactory.createTypeWithNullability(type, true)); - } - else { - RelDataType validatedNodeType = getValidatedNodeTypeIfKnown(node); - - dynamicParameterType(pHolder, node, validatedNodeType == null ? unknownType : validatedNodeType); - } - } - - return pHolder; - } - - /** Sets {@code node} and {@code type} to {@code pHolder}. */ - private void dynamicParameterType(DynamicParameterHolder pHolder, SqlDynamicParam node, RelDataType type) { - assert pHolder.node == null || node.getIndex() == pHolder.node.getIndex() - : "Node is already set for a dynamic parameter #" + pHolder.node.getIndex(); - - pHolder.type = type; - - pHolder.node = node; - - setValidatedNodeType(node, type); - } - /** {@inheritDoc} */ @Override public String deriveAlias(SqlNode node, int ordinal) { if (node.isA(HUMAN_READABLE_ALIASES_FOR)) { @@ -606,18 +545,12 @@ private boolean isSystemFieldName(String alias) { return; if (node instanceof SqlCall) { - final SqlCall call = (SqlCall)node; - - // SqlStdOperatorTable::IS_NULL and SqlStdOperatorTable::IS_NOT_NULL overrides with VARCHAR_1024 for argument type inference. - if ((node.getKind() == SqlKind.IS_NULL || node.getKind() == SqlKind.IS_NOT_NULL) - && call.getOperandList().get(0) instanceof SqlDynamicParam) - return; - 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); @@ -655,20 +588,62 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** @return {@code True} if a type was successfully set to {@code node}. {@code False} if type is not determined. */ + /** {@inheritDoc} */ + @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { + if (expr instanceof SqlDynamicParam && parameters != null) + return deriveDynamicParamType((SqlDynamicParam)expr); + else + return super.deriveType(scope, expr); + } + + /** */ + private RelDataType deriveDynamicParamType(SqlDynamicParam node) { + assert parameters != null; + + if (node.getIndex() >= parameters.length) + return unknownType; + + Object val = parameters[node.getIndex()]; + + RelDataType type = val == null + ? typeFactory.createSqlType(SqlTypeName.NULL) + : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); + + setValidatedNodeType(node, type); + + return type; + } + + /** + * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index + * is actual to {@link #parameters}. + * + * @return {@code True} if a new type was set. {@code False} otherwise. + */ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - if (!(node instanceof SqlDynamicParam)) + if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length) return false; - SqlDynamicParam dpNode = (SqlDynamicParam)node; + Object val = parameters[((SqlDynamicParam)node).getIndex()]; + + if (val == null) { + if (inferredType.equals(unknownType)) { + setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL)); - DynamicParameterHolder pHolder = deriveDynamicParamType(dpNode); + return true; + } - if (unknownType.equals(inferredType) && unknownType.equals(pHolder.type)) return false; + } + + RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass())); - if (!inferredType.equals(unknownType) && !inferredType.getFamily().equals(pHolder.type.getFamily())) - dynamicParameterType(pHolder, dpNode, typeFactory.createTypeWithNullability(inferredType, true)); + assert !unknownType.equals(valType); + + if (unknownType.equals(inferredType) || valType.getFamily().equals(inferredType.getFamily())) + setValidatedNodeType(node, valType); + else + setValidatedNodeType(node, inferredType); return true; } @@ -690,33 +665,4 @@ private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { return super.resolveLiteral(literal); } - - /** */ - private static final class DynamicParameterHolder { - /** */ - @Nullable private final Object val; - - /** */ - private final boolean hasValue; - - /** */ - private SqlDynamicParam node; - - /** */ - private RelDataType type; - - /** */ - private DynamicParameterHolder(@Nullable Object val) { - this.val = val; - - this.hasValue = true; - } - - /** */ - private DynamicParameterHolder() { - this.val = null; - - this.hasValue = false; - } - } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java index 2f222083c6a61..d50c2f227604f 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/PlanningContext.java @@ -33,6 +33,7 @@ import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.internal.util.typedef.internal.U; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * Planning context. @@ -45,7 +46,7 @@ public final class PlanningContext implements Context { private final String qry; /** */ - private final Object[] parameters; + @Nullable private final Object[] parameters; /** */ private final CancelFlag cancelFlag = new CancelFlag(new AtomicBoolean()); @@ -68,7 +69,7 @@ public final class PlanningContext implements Context { private PlanningContext( Context parentCtx, String qry, - Object[] parameters, + @Nullable Object[] parameters, long plannerTimeout ) { this.qry = qry; @@ -90,7 +91,7 @@ public String query() { * @return Query parameters. */ @SuppressWarnings("AssignmentOrReturnOfFieldWithMutableType") - public Object[] parameters() { + @Nullable public Object[] parameters() { return parameters; } From dfaa670078d1a36c2f5df823cb5e01743de5dbc7 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sat, 28 Dec 2024 14:32:00 +0300 Subject: [PATCH 27/43] fix --- .../calcite/prepare/IgniteSqlValidator.java | 58 +++++-------------- 1 file changed, 13 insertions(+), 45 deletions(-) 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 2a837c1e6924d..764c809d8ff3a 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 @@ -102,7 +102,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl { } /** Dynamic parameters. */ - private final @Nullable Object[] parameters; + @Nullable private final Object[] parameters; /** * Creates a validator. @@ -541,7 +541,7 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (inferDynamicParamType(inferredType, node)) + if (node instanceof SqlDynamicParam && deriveDynamicParameterType((SqlDynamicParam)node) != null) return; if (node instanceof SqlCall) { @@ -590,18 +590,20 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { /** {@inheritDoc} */ @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { - if (expr instanceof SqlDynamicParam && parameters != null) - return deriveDynamicParamType((SqlDynamicParam)expr); - else - return super.deriveType(scope, expr); + if (expr instanceof SqlDynamicParam) { + RelDataType type = deriveDynamicParameterType((SqlDynamicParam)expr); + + if (type != null) + return type; + } + + return super.deriveType(scope, expr); } /** */ - private RelDataType deriveDynamicParamType(SqlDynamicParam node) { - assert parameters != null; - - if (node.getIndex() >= parameters.length) - return unknownType; + @Nullable private RelDataType deriveDynamicParameterType(SqlDynamicParam node) { + if (parameters == null || node.getIndex() >= parameters.length) + return null; Object val = parameters[node.getIndex()]; @@ -614,40 +616,6 @@ private RelDataType deriveDynamicParamType(SqlDynamicParam node) { return type; } - /** - * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index - * is actual to {@link #parameters}. - * - * @return {@code True} if a new type was set. {@code False} otherwise. - */ - private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length) - return false; - - Object val = parameters[((SqlDynamicParam)node).getIndex()]; - - if (val == null) { - if (inferredType.equals(unknownType)) { - setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL)); - - return true; - } - - return false; - } - - RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass())); - - assert !unknownType.equals(valType); - - if (unknownType.equals(inferredType) || valType.getFamily().equals(inferredType.getFamily())) - setValidatedNodeType(node, valType); - else - setValidatedNodeType(node, inferredType); - - return true; - } - /** {@inheritDoc} */ @Override public SqlLiteral resolveLiteral(SqlLiteral literal) { if (literal instanceof SqlNumericLiteral && literal.createSqlType(typeFactory).getSqlTypeName() == SqlTypeName.BIGINT) { From d8933a72a431d175770c2dca423e3e0e66664112 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sat, 28 Dec 2024 14:32:00 +0300 Subject: [PATCH 28/43] fix --- .../calcite/prepare/IgniteSqlValidator.java | 99 +++++++------------ .../query/calcite/jdbc/JdbcQueryTest.java | 31 +++--- 2 files changed, 53 insertions(+), 77 deletions(-) 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 2a837c1e6924d..38fe3d386a566 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 @@ -70,6 +70,7 @@ import org.apache.ignite.internal.processors.query.calcite.schema.IgniteTable; import org.apache.ignite.internal.processors.query.calcite.sql.IgniteSqlDecimalLiteral; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; +import org.apache.ignite.internal.processors.query.calcite.type.OtherType; import org.apache.ignite.internal.processors.query.calcite.util.IgniteResource; import org.apache.ignite.internal.util.typedef.F; import org.jetbrains.annotations.Nullable; @@ -102,7 +103,7 @@ public class IgniteSqlValidator extends SqlValidatorImpl { } /** Dynamic parameters. */ - private final @Nullable Object[] parameters; + @Nullable private final Object[] parameters; /** * Creates a validator. @@ -539,9 +540,43 @@ private boolean isSystemFieldName(String alias) { || QueryUtils.VAL_FIELD_NAME.equalsIgnoreCase(alias); } + /** {@inheritDoc} */ + @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { + if (expr instanceof SqlDynamicParam) { + RelDataType type = deriveDynamicParameterType((SqlDynamicParam)expr); + + if (type != null) + return type; + } + + return super.deriveType(scope, expr); + } + + /** @return A derived type or {@code null} if unale to determine. */ + @Nullable private RelDataType deriveDynamicParameterType(SqlDynamicParam node) { + RelDataType type = getValidatedNodeTypeIfKnown(node); + + if (type instanceof OtherType) + return type; + + if (parameters == null || node.getIndex() >= parameters.length) + return null; + + Object val = parameters[node.getIndex()]; + + type = val == null + ? typeFactory.createSqlType(SqlTypeName.NULL) + : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); + + setValidatedNodeType(node, type); + + return type; + } + + /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (inferDynamicParamType(inferredType, node)) + if (node instanceof SqlDynamicParam && unknownType.equals(inferredType) && deriveDynamicParameterType((SqlDynamicParam)node) != null) return; if (node instanceof SqlCall) { @@ -588,66 +623,6 @@ else if (operandTypeChecker instanceof FamilyOperandTypeChecker) { super.inferUnknownTypes(inferredType, scope, node); } - /** {@inheritDoc} */ - @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { - if (expr instanceof SqlDynamicParam && parameters != null) - return deriveDynamicParamType((SqlDynamicParam)expr); - else - return super.deriveType(scope, expr); - } - - /** */ - private RelDataType deriveDynamicParamType(SqlDynamicParam node) { - assert parameters != null; - - if (node.getIndex() >= parameters.length) - return unknownType; - - Object val = parameters[node.getIndex()]; - - RelDataType type = val == null - ? typeFactory.createSqlType(SqlTypeName.NULL) - : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); - - setValidatedNodeType(node, type); - - return type; - } - - /** - * Tries to set actual type of dynamic parameter if {@code node} is a {@link SqlDynamicParam} and if its index - * is actual to {@link #parameters}. - * - * @return {@code True} if a new type was set. {@code False} otherwise. - */ - private boolean inferDynamicParamType(RelDataType inferredType, SqlNode node) { - if (parameters == null || !(node instanceof SqlDynamicParam) || ((SqlDynamicParam)node).getIndex() >= parameters.length) - return false; - - Object val = parameters[((SqlDynamicParam)node).getIndex()]; - - if (val == null) { - if (inferredType.equals(unknownType)) { - setValidatedNodeType(node, typeFactory().createSqlType(SqlTypeName.NULL)); - - return true; - } - - return false; - } - - RelDataType valType = typeFactory().toSql(typeFactory().createType(val.getClass())); - - assert !unknownType.equals(valType); - - if (unknownType.equals(inferredType) || valType.getFamily().equals(inferredType.getFamily())) - setValidatedNodeType(node, valType); - else - setValidatedNodeType(node, inferredType); - - return true; - } - /** {@inheritDoc} */ @Override public SqlLiteral resolveLiteral(SqlLiteral literal) { if (literal instanceof SqlNumericLiteral && literal.createSqlType(typeFactory).getSqlTypeName() == SqlTypeName.BIGINT) { diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java index cd457f62a38f0..6f19e0df5ea63 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java @@ -120,22 +120,23 @@ private void connect(String url) throws Exception { public void testOtherType() throws Exception { List values = new ArrayList<>(); - values.add("str"); - values.add(11); - values.add(2); - values.add(5); - values.add(7); - values.add(101.1); - values.add(202.2d); - values.add(new byte[] {1, 2, 3}); - values.add(UUID.randomUUID()); - values.add(new ObjectToStore(1, "noname", 22.2)); +// values.add("str"); +// values.add(11); +// values.add(2); +// values.add(5); +// values.add(7); +// values.add(101.1); +// values.add(202.2d); +// values.add(new byte[] {1, 2, 3}); +// values.add(UUID.randomUUID()); +// values.add(new ObjectToStore(1, "noname", 22.2)); Map map = new HashMap<>(); - map.put("a", "bb"); - map.put("vvv", "zzz"); - map.put("111", "222"); - map.put("lst", Stream.of("abc", 1, null, 20.f).collect(Collectors.toSet())); +// map.put("a", "bb"); +// map.put("vvv", "zzz"); +// map.put("111", "222"); +// map.put("lst", Stream.of("abc", 1, null, 20.f).collect(Collectors.toSet())); + map.put("lst", Stream.of().collect(Collectors.toSet())); values.add(map); List lst = new ArrayList<>(); @@ -144,7 +145,7 @@ public void testOtherType() throws Exception { lst.add(3.3d); lst.add("str"); lst.add(map); - values.add(lst); + //values.add(lst); stmt.execute("CREATE TABLE tbl(id INT, oth OTHER, primary key(id))"); From 26696229abcb78980623026fae578552359bd008 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sat, 28 Dec 2024 15:34:02 +0300 Subject: [PATCH 29/43] revert test --- .../query/calcite/jdbc/JdbcQueryTest.java | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java index 6f19e0df5ea63..cd457f62a38f0 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/jdbc/JdbcQueryTest.java @@ -120,23 +120,22 @@ private void connect(String url) throws Exception { public void testOtherType() throws Exception { List values = new ArrayList<>(); -// values.add("str"); -// values.add(11); -// values.add(2); -// values.add(5); -// values.add(7); -// values.add(101.1); -// values.add(202.2d); -// values.add(new byte[] {1, 2, 3}); -// values.add(UUID.randomUUID()); -// values.add(new ObjectToStore(1, "noname", 22.2)); + values.add("str"); + values.add(11); + values.add(2); + values.add(5); + values.add(7); + values.add(101.1); + values.add(202.2d); + values.add(new byte[] {1, 2, 3}); + values.add(UUID.randomUUID()); + values.add(new ObjectToStore(1, "noname", 22.2)); Map map = new HashMap<>(); -// map.put("a", "bb"); -// map.put("vvv", "zzz"); -// map.put("111", "222"); -// map.put("lst", Stream.of("abc", 1, null, 20.f).collect(Collectors.toSet())); - map.put("lst", Stream.of().collect(Collectors.toSet())); + map.put("a", "bb"); + map.put("vvv", "zzz"); + map.put("111", "222"); + map.put("lst", Stream.of("abc", 1, null, 20.f).collect(Collectors.toSet())); values.add(map); List lst = new ArrayList<>(); @@ -145,7 +144,7 @@ public void testOtherType() throws Exception { lst.add(3.3d); lst.add("str"); lst.add(map); - //values.add(lst); + values.add(lst); stmt.execute("CREATE TABLE tbl(id INT, oth OTHER, primary key(id))"); From c499415361824a174889bdeb6dc993288f3f41a9 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sat, 28 Dec 2024 15:41:03 +0300 Subject: [PATCH 30/43] trivial comment --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 1 + 1 file changed, 1 insertion(+) 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 38fe3d386a566..41e1e917211cb 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 @@ -556,6 +556,7 @@ private boolean isSystemFieldName(String alias) { @Nullable private RelDataType deriveDynamicParameterType(SqlDynamicParam node) { RelDataType type = getValidatedNodeTypeIfKnown(node); + // Do not clarify the widest type for any value. if (type instanceof OtherType) return type; From 0826926edb3e53b431036a2588a02c708d49b11c Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sat, 28 Dec 2024 16:01:13 +0300 Subject: [PATCH 31/43] spell fix --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 41e1e917211cb..c02b512643fbe 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 @@ -552,7 +552,7 @@ private boolean isSystemFieldName(String alias) { return super.deriveType(scope, expr); } - /** @return A derived type or {@code null} if unale to determine. */ + /** @return A derived type or {@code null} if unable to determine. */ @Nullable private RelDataType deriveDynamicParameterType(SqlDynamicParam node) { RelDataType type = getValidatedNodeTypeIfKnown(node); From 4310202606d9f2c77eac2bcf06f68cb139b65ee3 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Sun, 29 Dec 2024 18:54:06 +0300 Subject: [PATCH 32/43] checkstyle fix --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 c02b512643fbe..7da4e9a9ef1fd 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 @@ -577,7 +577,8 @@ private boolean isSystemFieldName(String alias) { /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { - if (node instanceof SqlDynamicParam && unknownType.equals(inferredType) && deriveDynamicParameterType((SqlDynamicParam)node) != null) + if (node instanceof SqlDynamicParam && unknownType.equals(inferredType) + && deriveDynamicParameterType((SqlDynamicParam)node) != null) return; if (node instanceof SqlCall) { From 227b162d099854c606de7d573d817d3e1002faa8 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Mon, 30 Dec 2024 21:37:53 +0300 Subject: [PATCH 33/43] test fix 1 --- .../query/calcite/integration/JoinRehashIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java index 2370eea664f0c..928ca43049e5f 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/JoinRehashIntegrationTest.java @@ -49,7 +49,7 @@ public void testResourceCleanup() throws Exception { // AbstractBasicIntegrationTest.afterTest method. GridTestUtils.runMultiThreaded(() -> { for (int i = 0; i < 100; i++) - sql(sql, i % 10); + sql(sql, "region" + (i % 10)); }, 10, "query_starter"); } From 74300fc28a0aee2173e0749391bf21a07663bc87 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 14:19:01 +0300 Subject: [PATCH 34/43] research --- .../calcite/exec/ExecutionServiceImpl.java | 4 + .../calcite/metadata/ColocationGroup.java | 4 + .../calcite/prepare/IgniteSqlValidator.java | 2 + .../integration/PartitionPruneTest.java | 87 ++++++++++--------- 4 files changed, 58 insertions(+), 39 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java index c985dd6f4ae11..f3fb98f5f3c75 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java @@ -653,6 +653,8 @@ private ListFieldsQueryCursor mapAndExecutePlan( .flatMap(f -> f.mapping().nodeIds().stream()) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + System.err.println("TEST | fragments cnt: " + fragments.size()); + // Start remote execution. for (int i = 1; i < fragments.size(); i++) { fragment = fragments.get(i); @@ -662,6 +664,8 @@ private ListFieldsQueryCursor mapAndExecutePlan( execPlan.target(fragment), execPlan.remotes(fragment)); + System.err.println("TEST | nodes cnt of fragment idx" + i + ": " + fragmentDesc.nodeIds().size()); + Throwable ex = null; byte[] parametersMarshalled = null; diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java index af9fadc572363..933602f2df124 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java @@ -228,6 +228,8 @@ public ColocationGroup colocate(ColocationGroup other) throws ColocationMappingE /** */ public ColocationGroup finalizeMapping() { + System.err.println("TEST | finalizeMapping, assign. cnt: " + (assignments==null ? 0 : assignments.size())); + if (assignments == null) return this; @@ -241,6 +243,8 @@ public ColocationGroup finalizeMapping() { assignments.add(first != null ? Collections.singletonList(first) : Collections.emptyList()); } + System.err.println("TEST | finalizeMapping, assign. cnt: " + (assignments==null ? 0 : assignments.size())); + return new ColocationGroup(sourceIds, new ArrayList<>(nodes), assignments, primaryAssignment); } 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 7da4e9a9ef1fd..bb57abde4d36b 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 @@ -565,6 +565,8 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; + //if (val == null) return null; + type = val == null ? typeFactory.createSqlType(SqlTypeName.NULL) : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java index ac6fc2c47adee..0ccad7598fff7 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java @@ -217,39 +217,15 @@ public void testSimple() { /** */ @Test public void testNullsInCondition() { - execute("select * from T1 where T1.ID is NULL", - res -> { - assertPartitions(); - assertNodes(); - - assertTrue(res.isEmpty()); - }); +// execute("select * from T1 where T1.ID is NULL", +// res -> { +// assertPartitions(); +// assertNodes(); +// +// assertTrue(res.isEmpty()); +// }); execute("select * from T1 where T1.ID = ?", - res -> { - assertPartitions(); - assertNodes(); - - assertTrue(res.isEmpty()); - }, new Object[]{ null }); - - execute("select * from T1 where T1.ID is NULL and T1.ID = ?", - res -> { - assertPartitions(); - assertNodes(); - - assertTrue(res.isEmpty()); - }, 123); - - execute("select * from T1 where T1.ID = ? and T1.ID = ?", - res -> { - assertPartitions(); - assertNodes(); - - assertTrue(res.isEmpty()); - }, null, 123); - - execute("select * from T1 where T1.ID is NULL or T1.ID = ?", res -> { assertPartitions(partition("T1_CACHE", 123)); assertNodes(node("T1_CACHE", 123)); @@ -258,14 +234,47 @@ public void testNullsInCondition() { assertEquals("name_123", res.get(0).get(1)); }, 123); - execute("select * from T1 where T1.ID = ? or T1.ID = ?", - res -> { - assertPartitions(partition("T1_CACHE", 123)); - assertNodes(node("T1_CACHE", 123)); - - assertEquals(1, res.size()); - assertEquals("name_123", res.get(0).get(1)); - }, null, 123); +// execute("select * from T1 where T1.ID = ?", +// res -> { +// assertPartitions(); +// assertNodes(); +// +// assertTrue(res.isEmpty()); +// }, new Object[]{ null }); + +// execute("select * from T1 where T1.ID is NULL and T1.ID = ?", +// res -> { +// assertPartitions(); +// assertNodes(); +// +// assertTrue(res.isEmpty()); +// }, 123); +// +// execute("select * from T1 where T1.ID = ? and T1.ID = ?", +// res -> { +// assertPartitions(); +// assertNodes(); +// +// assertTrue(res.isEmpty()); +// }, null, 123); +// +// execute("select * from T1 where T1.ID is NULL or T1.ID = ?", +// res -> { +// assertPartitions(partition("T1_CACHE", 123)); +// assertNodes(node("T1_CACHE", 123)); +// +// assertEquals(1, res.size()); +// assertEquals("name_123", res.get(0).get(1)); +// }, 123); +// +// execute("select * from T1 where T1.ID = ? or T1.ID = ?", +// res -> { +// assertPartitions(partition("T1_CACHE", 123)); +// assertNodes(node("T1_CACHE", 123)); +// +// assertEquals(1, res.size()); +// assertEquals("name_123", res.get(0).get(1)); +// }, null, 123); } /** */ From 0c6eda74897c07a42f67ac8de3126f969d4862b2 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 14:48:26 +0300 Subject: [PATCH 35/43] test fixes --- .../calcite/exec/ExecutionServiceImpl.java | 4 - .../calcite/metadata/ColocationGroup.java | 4 - .../calcite/prepare/IgniteSqlValidator.java | 2 - .../integration/PartitionPruneTest.java | 87 +++++++++---------- 4 files changed, 39 insertions(+), 58 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java index f3fb98f5f3c75..c985dd6f4ae11 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/ExecutionServiceImpl.java @@ -653,8 +653,6 @@ private ListFieldsQueryCursor mapAndExecutePlan( .flatMap(f -> f.mapping().nodeIds().stream()) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); - System.err.println("TEST | fragments cnt: " + fragments.size()); - // Start remote execution. for (int i = 1; i < fragments.size(); i++) { fragment = fragments.get(i); @@ -664,8 +662,6 @@ private ListFieldsQueryCursor mapAndExecutePlan( execPlan.target(fragment), execPlan.remotes(fragment)); - System.err.println("TEST | nodes cnt of fragment idx" + i + ": " + fragmentDesc.nodeIds().size()); - Throwable ex = null; byte[] parametersMarshalled = null; diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java index 933602f2df124..af9fadc572363 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/metadata/ColocationGroup.java @@ -228,8 +228,6 @@ public ColocationGroup colocate(ColocationGroup other) throws ColocationMappingE /** */ public ColocationGroup finalizeMapping() { - System.err.println("TEST | finalizeMapping, assign. cnt: " + (assignments==null ? 0 : assignments.size())); - if (assignments == null) return this; @@ -243,8 +241,6 @@ public ColocationGroup finalizeMapping() { assignments.add(first != null ? Collections.singletonList(first) : Collections.emptyList()); } - System.err.println("TEST | finalizeMapping, assign. cnt: " + (assignments==null ? 0 : assignments.size())); - return new ColocationGroup(sourceIds, new ArrayList<>(nodes), assignments, primaryAssignment); } 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 bb57abde4d36b..7da4e9a9ef1fd 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 @@ -565,8 +565,6 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; - //if (val == null) return null; - type = val == null ? typeFactory.createSqlType(SqlTypeName.NULL) : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java index 0ccad7598fff7..ac6fc2c47adee 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/PartitionPruneTest.java @@ -217,15 +217,39 @@ public void testSimple() { /** */ @Test public void testNullsInCondition() { -// execute("select * from T1 where T1.ID is NULL", -// res -> { -// assertPartitions(); -// assertNodes(); -// -// assertTrue(res.isEmpty()); -// }); + execute("select * from T1 where T1.ID is NULL", + res -> { + assertPartitions(); + assertNodes(); + + assertTrue(res.isEmpty()); + }); execute("select * from T1 where T1.ID = ?", + res -> { + assertPartitions(); + assertNodes(); + + assertTrue(res.isEmpty()); + }, new Object[]{ null }); + + execute("select * from T1 where T1.ID is NULL and T1.ID = ?", + res -> { + assertPartitions(); + assertNodes(); + + assertTrue(res.isEmpty()); + }, 123); + + execute("select * from T1 where T1.ID = ? and T1.ID = ?", + res -> { + assertPartitions(); + assertNodes(); + + assertTrue(res.isEmpty()); + }, null, 123); + + execute("select * from T1 where T1.ID is NULL or T1.ID = ?", res -> { assertPartitions(partition("T1_CACHE", 123)); assertNodes(node("T1_CACHE", 123)); @@ -234,47 +258,14 @@ public void testNullsInCondition() { assertEquals("name_123", res.get(0).get(1)); }, 123); -// execute("select * from T1 where T1.ID = ?", -// res -> { -// assertPartitions(); -// assertNodes(); -// -// assertTrue(res.isEmpty()); -// }, new Object[]{ null }); - -// execute("select * from T1 where T1.ID is NULL and T1.ID = ?", -// res -> { -// assertPartitions(); -// assertNodes(); -// -// assertTrue(res.isEmpty()); -// }, 123); -// -// execute("select * from T1 where T1.ID = ? and T1.ID = ?", -// res -> { -// assertPartitions(); -// assertNodes(); -// -// assertTrue(res.isEmpty()); -// }, null, 123); -// -// execute("select * from T1 where T1.ID is NULL or T1.ID = ?", -// res -> { -// assertPartitions(partition("T1_CACHE", 123)); -// assertNodes(node("T1_CACHE", 123)); -// -// assertEquals(1, res.size()); -// assertEquals("name_123", res.get(0).get(1)); -// }, 123); -// -// execute("select * from T1 where T1.ID = ? or T1.ID = ?", -// res -> { -// assertPartitions(partition("T1_CACHE", 123)); -// assertNodes(node("T1_CACHE", 123)); -// -// assertEquals(1, res.size()); -// assertEquals("name_123", res.get(0).get(1)); -// }, null, 123); + execute("select * from T1 where T1.ID = ? or T1.ID = ?", + res -> { + assertPartitions(partition("T1_CACHE", 123)); + assertNodes(node("T1_CACHE", 123)); + + assertEquals(1, res.size()); + assertEquals("name_123", res.get(0).get(1)); + }, null, 123); } /** */ From 2356ffed478c004b8c256ad8662c8e3271bfcbfc Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 15:07:19 +0300 Subject: [PATCH 36/43] revert research --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 2 ++ 1 file changed, 2 insertions(+) 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 7da4e9a9ef1fd..2897a2d89e11b 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 @@ -565,6 +565,8 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; + if(val==null) return null; + type = val == null ? typeFactory.createSqlType(SqlTypeName.NULL) : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); From 175f0c32f5cc56d4be93ba7c42ef7b20ea78b436 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 16:18:31 +0300 Subject: [PATCH 37/43] revert test --- .../query/calcite/prepare/IgniteSqlValidator.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 2897a2d89e11b..16d073faf4cae 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 @@ -565,11 +565,10 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; - if(val==null) return null; + if (val == null) + return null; - type = val == null - ? typeFactory.createSqlType(SqlTypeName.NULL) - : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); + type = typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); setValidatedNodeType(node, type); From 6c709919d21446673606b0c71294488c246227ff Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 16:25:38 +0300 Subject: [PATCH 38/43] revert --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 3 --- 1 file changed, 3 deletions(-) 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 16d073faf4cae..1ca292461d410 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 @@ -565,9 +565,6 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; - if (val == null) - return null; - type = typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); setValidatedNodeType(node, type); From 5e05aaabf913507f5ed2ae1bc5a2d60281726c54 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 16:28:08 +0300 Subject: [PATCH 39/43] revert --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 1ca292461d410..7da4e9a9ef1fd 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 @@ -565,7 +565,9 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; - type = typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); + type = val == null + ? typeFactory.createSqlType(SqlTypeName.NULL) + : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); setValidatedNodeType(node, type); From 1de9435f051f1b5a16ad612a0178fc391b16445b Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 17:24:25 +0300 Subject: [PATCH 40/43] Fix --- .../query/calcite/exec/exp/IgniteRexBuilder.java | 12 ++++++++++++ .../query/calcite/prepare/IgniteTypeCoercion.java | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java index d22c9079fe11c..e3f16014231e2 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java @@ -23,7 +23,9 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexLiteral; +import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.type.IntervalSqlType; +import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.ignite.internal.processors.query.IgniteSQLException; @@ -64,4 +66,14 @@ public IgniteRexBuilder(RelDataTypeFactory typeFactory) { return super.makeLiteral(o, type, typeName); } + + /** {@inheritDoc} */ + @Override public RexNode ensureType(RelDataType type, RexNode node, boolean matchNullability) { + // Do not create additional CAST node for NULL'ed node's value. Any type may get NULL value if is nullable + // or if the nullability matching is not required. + if (node.getType().getFamily() == SqlTypeFamily.NULL && (type.isNullable() || !matchNullability)) + return node; + + return super.ensureType(type, node, matchNullability); + } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java index 8cba041988535..8acf945196191 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java @@ -72,7 +72,8 @@ public IgniteTypeCoercion(RelDataTypeFactory typeFactory, SqlValidator validator RelDataType leftType = validator.deriveType(scope, call.operand(0)); RelDataType rightType = validator.deriveType(scope, call.operand(1)); - if (leftType.equals(rightType)) + // Do not create additional CAST node for NULL'ed right value. Any type may get NULL value if is nullable. + if (leftType.equals(rightType) || (rightType.getFamily() == SqlTypeFamily.NULL && leftType.isNullable())) return super.binaryComparisonCoercion(binding); else { // Find the least restrictive type among the operand types From 813244bc54a4e62bffca97bc6816094a7ee36f9d Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 9 Jan 2025 17:25:32 +0300 Subject: [PATCH 41/43] Revert "Fix" This reverts commit 1de9435f051f1b5a16ad612a0178fc391b16445b. --- .../query/calcite/exec/exp/IgniteRexBuilder.java | 12 ------------ .../query/calcite/prepare/IgniteTypeCoercion.java | 3 +-- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java index e3f16014231e2..d22c9079fe11c 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/exec/exp/IgniteRexBuilder.java @@ -23,9 +23,7 @@ import org.apache.calcite.rel.type.RelDataTypeFactory; import org.apache.calcite.rex.RexBuilder; import org.apache.calcite.rex.RexLiteral; -import org.apache.calcite.rex.RexNode; import org.apache.calcite.sql.type.IntervalSqlType; -import org.apache.calcite.sql.type.SqlTypeFamily; import org.apache.calcite.sql.type.SqlTypeName; import org.apache.calcite.sql.type.SqlTypeUtil; import org.apache.ignite.internal.processors.query.IgniteSQLException; @@ -66,14 +64,4 @@ public IgniteRexBuilder(RelDataTypeFactory typeFactory) { return super.makeLiteral(o, type, typeName); } - - /** {@inheritDoc} */ - @Override public RexNode ensureType(RelDataType type, RexNode node, boolean matchNullability) { - // Do not create additional CAST node for NULL'ed node's value. Any type may get NULL value if is nullable - // or if the nullability matching is not required. - if (node.getType().getFamily() == SqlTypeFamily.NULL && (type.isNullable() || !matchNullability)) - return node; - - return super.ensureType(type, node, matchNullability); - } } diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java index 8acf945196191..8cba041988535 100644 --- a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/prepare/IgniteTypeCoercion.java @@ -72,8 +72,7 @@ public IgniteTypeCoercion(RelDataTypeFactory typeFactory, SqlValidator validator RelDataType leftType = validator.deriveType(scope, call.operand(0)); RelDataType rightType = validator.deriveType(scope, call.operand(1)); - // Do not create additional CAST node for NULL'ed right value. Any type may get NULL value if is nullable. - if (leftType.equals(rightType) || (rightType.getFamily() == SqlTypeFamily.NULL && leftType.isNullable())) + if (leftType.equals(rightType)) return super.binaryComparisonCoercion(binding); else { // Find the least restrictive type among the operand types From a827fc494c5ccc23bfb5b3ff1a269077ca0eb541 Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Tue, 14 Jan 2025 14:55:05 +0300 Subject: [PATCH 42/43] checkstyle --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 1 - 1 file changed, 1 deletion(-) 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 7da4e9a9ef1fd..b6be1242034bf 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 @@ -574,7 +574,6 @@ private boolean isSystemFieldName(String alias) { return type; } - /** {@inheritDoc} */ @Override protected void inferUnknownTypes(RelDataType inferredType, SqlValidatorScope scope, SqlNode node) { if (node instanceof SqlDynamicParam && unknownType.equals(inferredType) From d8b92cdf94e7894f8475bde57ddeab7bd54cfa2e Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Thu, 30 Jan 2025 18:21:13 +0300 Subject: [PATCH 43/43] + master, + fix --- .../processors/query/calcite/prepare/IgniteSqlValidator.java | 3 +++ .../calcite/integration/DynamicParametersIntegrationTest.java | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) 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 b6be1242034bf..91fe45db4b382 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 @@ -565,6 +565,9 @@ private boolean isSystemFieldName(String alias) { Object val = parameters[node.getIndex()]; + if (val == null && type != null) + return type; + type = val == null ? typeFactory.createSqlType(SqlTypeName.NULL) : typeFactory().createTypeWithNullability(typeFactory().toSql(typeFactory().createType(val.getClass())), true); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java index 78d825a9e3dcc..cbfc657123ecb 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DynamicParametersIntegrationTest.java @@ -78,7 +78,9 @@ public void testCasts() { assertQuery("SELECT ?::VARCHAR").withParams(1).returns("1").check(); assertQuery("SELECT CAST(? as VARCHAR)").withParams(1).returns("1").check(); - IgniteCache cache = createAndPopulateTable(); + createAndPopulateTable(); + + IgniteCache cache = client.getOrCreateCache(TABLE_NAME); cache.put(cache.size(), new Employer("15", 15d));