From 1c098e201f8800e8283d4113222d7f26c0146d69 Mon Sep 17 00:00:00 2001 From: Vladislav Novikov <47698772+vladnovoren@users.noreply.github.com> Date: Wed, 30 Oct 2024 18:26:39 +0300 Subject: [PATCH 1/2] test fixes --- .../query/calcite/CalciteQueryProcessor.java | 3 + .../calcite/prepare/IgniteSqlValidator.java | 57 ++++ .../type/IgniteTypeAssignmentsRules.java | 271 ++++++++++++++++++ .../query/calcite/util/IgniteResource.java | 8 + .../calcite/integration/DateTimeTest.java | 6 + .../calcite/integration/FunctionsTest.java | 44 +-- .../calcite/integration/IntervalTest.java | 13 +- .../integration/StdSqlOperatorsTest.java | 7 +- .../src/test/sql/cast/test_boolean_cast.test | 26 +- .../ignite/internal/util/IgniteUtils.java | 2 +- .../ignite/internal/util/lang/GridFunc.java | 6 + 11 files changed, 400 insertions(+), 43 deletions(-) create mode 100644 modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeAssignmentsRules.java 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..3c44afd006cd0 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 @@ -43,6 +43,7 @@ import org.apache.calcite.sql.SqlNodeList; import org.apache.calcite.sql.parser.SqlParser; import org.apache.calcite.sql.parser.SqlParserPos; +import org.apache.calcite.sql.type.SqlTypeCoercionRule; import org.apache.calcite.sql.util.SqlOperatorTables; import org.apache.calcite.sql.util.SqlShuttle; import org.apache.calcite.sql.validate.SqlValidator; @@ -112,6 +113,7 @@ import org.apache.ignite.internal.processors.query.calcite.trait.CorrelationTraitDef; import org.apache.ignite.internal.processors.query.calcite.trait.DistributionTraitDef; import org.apache.ignite.internal.processors.query.calcite.trait.RewindabilityTraitDef; +import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeAssignmentsRules; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeFactory; import org.apache.ignite.internal.processors.query.calcite.type.IgniteTypeSystem; import org.apache.ignite.internal.processors.query.calcite.util.Commons; @@ -163,6 +165,7 @@ public class CalciteQueryProcessor extends GridProcessorAdapter implements Query .withIdentifierExpansion(true) .withDefaultNullCollation(NullCollation.LOW) .withSqlConformance(IgniteSqlConformance.INSTANCE) + .withTypeCoercionRules(SqlTypeCoercionRule.instance(IgniteTypeAssignmentsRules.INSTANCE.getTypeMapping())) .withTypeCoercionFactory(IgniteTypeCoercion::new)) // Dialects support. .operatorTable(SqlOperatorTables.chain(IgniteStdSqlOperatorTable.INSTANCE, IgniteOwnSqlOperatorTable.instance())) 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 93cd94d1decf5..54dd7da20230e 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 @@ -31,6 +31,7 @@ import org.apache.calcite.rel.type.RelDataTypeField; import org.apache.calcite.sql.JoinConditionType; import org.apache.calcite.sql.SqlAggFunction; +import org.apache.calcite.sql.SqlBasicCall; import org.apache.calcite.sql.SqlCall; import org.apache.calcite.sql.SqlCallBinding; import org.apache.calcite.sql.SqlDelete; @@ -44,6 +45,7 @@ import org.apache.calcite.sql.SqlNode; import org.apache.calcite.sql.SqlNodeList; import org.apache.calcite.sql.SqlNumericLiteral; +import org.apache.calcite.sql.SqlOperator; import org.apache.calcite.sql.SqlOperatorTable; import org.apache.calcite.sql.SqlSelect; import org.apache.calcite.sql.SqlUpdate; @@ -55,6 +57,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; @@ -74,6 +77,7 @@ import org.apache.ignite.internal.util.typedef.F; import org.jetbrains.annotations.Nullable; +import static org.apache.calcite.sql.type.SqlTypeUtil.isNull; import static org.apache.calcite.util.Static.RESOURCE; /** Validator. */ @@ -289,6 +293,59 @@ else if (call.getKind() == SqlKind.CAST) { super.validateCall(call, scope); } + /** {@inheritDoc} */ + @Override public RelDataType deriveType(SqlValidatorScope scope, SqlNode expr) { + checkTypesInteroperability(scope, expr); + + return super.deriveType(scope, expr); + } + + /** Check appropriate type cast availability. */ + private void checkTypesInteroperability(SqlValidatorScope scope, SqlNode expr) { + boolean castOp = expr.getKind() == SqlKind.CAST; + + if (castOp || SqlKind.BINARY_COMPARISON.contains(expr.getKind())) { + SqlBasicCall expr0 = (SqlBasicCall)expr; + SqlNode first = expr0.getOperandList().get(0); + SqlNode ret = expr0.getOperandList().get(1); + + RelDataType returnType = super.deriveType(scope, ret); + + if (returnType.isStruct()) + throw newValidationError(expr, IgniteResource.INSTANCE.dataTypeIsNotSupported(returnType.getSqlTypeName().getName())); + + //TODO: https://issues.apache.org/jira/browse/IGNITE-23414 - fix cast of dynamic params. + RelDataType firstType = super.deriveType(scope, first); + + boolean nullType = isNull(returnType) || isNull(firstType); + + // propagate null type validation + if (nullType) + return; + + boolean check = SqlTypeUtil.canCastFrom(returnType, firstType, true); + + if (!check) { + if (castOp) + throw newValidationError(expr, RESOURCE.cannotCastValue(firstType.toString(), returnType.toString())); + else { + SqlBasicCall call = (SqlBasicCall)expr; + SqlOperator operator = call.getOperator(); + + throw SqlUtil.newContextException(expr.getParserPosition(), RESOURCE.incompatibleValueType(operator.getName())); + } + } + + if (castOp) { + if (SqlTypeUtil.isString(returnType) && returnType.getPrecision() == 0) { + String typeName = returnType.getSqlTypeName().getSpaceName(); + + throw newValidationError(expr, IgniteResource.INSTANCE.invalidStringLength(typeName)); + } + } + } + } + /** {@inheritDoc} */ @Override public String deriveAlias(SqlNode node, int ordinal) { if (node.isA(HUMAN_READABLE_ALIASES_FOR)) { diff --git a/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeAssignmentsRules.java b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeAssignmentsRules.java new file mode 100644 index 0000000000000..a042d0927172c --- /dev/null +++ b/modules/calcite/src/main/java/org/apache/ignite/internal/processors/query/calcite/type/IgniteTypeAssignmentsRules.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.ignite.internal.processors.query.calcite.type; + +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; +import com.google.common.util.concurrent.UncheckedExecutionException; +import org.apache.calcite.sql.type.SqlTypeAssignmentRule; +import org.apache.calcite.sql.type.SqlTypeCoercionRule; +import org.apache.calcite.sql.type.SqlTypeMappingRule; +import org.apache.calcite.sql.type.SqlTypeMappingRules; +import org.apache.calcite.sql.type.SqlTypeName; +import org.apache.calcite.util.Util; + +import static org.apache.calcite.sql.type.SqlTypeName.APPROX_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.BINARY_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.BOOLEAN; +import static org.apache.calcite.sql.type.SqlTypeName.CHAR_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.DAY_INTERVAL_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.EXACT_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.FRACTIONAL_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_DAY; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_DAY_HOUR; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_DAY_MINUTE; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_DAY_SECOND; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_HOUR; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_HOUR_MINUTE; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_HOUR_SECOND; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_MINUTE; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_MINUTE_SECOND; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_MONTH; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_SECOND; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_YEAR; +import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_YEAR_MONTH; +import static org.apache.calcite.sql.type.SqlTypeName.INT_TYPES; +import static org.apache.calcite.sql.type.SqlTypeName.YEAR_INTERVAL_TYPES; + +/** + * Sets rules to explicit CAST. Calcite's {@link SqlTypeCoercionRule} and {@link SqlTypeAssignmentRule} do not satisfy + * SQL standard. + */ +public class IgniteTypeAssignmentsRules implements SqlTypeMappingRule { + /** */ + public static final IgniteTypeAssignmentsRules INSTANCE; + + static { + IgniteTypeAssignmentsRules.Builder rules = new Builder(); + + Set rule = EnumSet.noneOf(SqlTypeName.class); + + // MULTISET is assignable from... + rules.add(SqlTypeName.MULTISET, EnumSet.of(SqlTypeName.MULTISET)); + + rule.clear(); + rule.addAll(EXACT_TYPES); + rule.addAll(FRACTIONAL_TYPES); + rule.addAll(CHAR_TYPES); + + // FLOAT (up to 64 bit floating point) is assignable from... + // REAL (32 bit floating point) is assignable from... + // DOUBLE is assignable from... + // DECIMAL is assignable from... + for (SqlTypeName type : FRACTIONAL_TYPES) + rules.add(type, rule); + + rule.add(INTERVAL_YEAR); + rule.add(INTERVAL_MONTH); + rule.add(INTERVAL_DAY); + rule.add(INTERVAL_HOUR); + rule.add(INTERVAL_MINUTE); + rule.add(INTERVAL_SECOND); + + // TINYINT is assignable from... + // SMALLINT is assignable from... + // INTEGER is assignable from... + // BIGINT is assignable from... + for (SqlTypeName type : EXACT_TYPES) + rules.add(type, rule); + + // BINARY, VARBINARY is assignable from... + rule.clear(); + rule.addAll(BINARY_TYPES); + rule.addAll(CHAR_TYPES); + for (SqlTypeName type : BINARY_TYPES) + rules.add(type, rule); + + // CHAR is assignable from... + // VARCHAR is assignable from... + rule.clear(); + rule.addAll(CHAR_TYPES); + rule.addAll(EXACT_TYPES); + rule.addAll(APPROX_TYPES); + rule.addAll(DAY_INTERVAL_TYPES); + rule.addAll(YEAR_INTERVAL_TYPES); + rule.add(BOOLEAN); + rule.add(SqlTypeName.DATE); + rule.add(SqlTypeName.TIME); + rule.add(SqlTypeName.TIMESTAMP); + rule.addAll(BINARY_TYPES); + + rules.add(SqlTypeName.CHAR, rule); + rules.add(SqlTypeName.VARCHAR, rule); + + // BOOLEAN is assignable from... + rule.clear(); + rule.addAll(INT_TYPES); + rule.addAll(CHAR_TYPES); + rule.add(BOOLEAN); + rules.add(BOOLEAN, rule); + + // DATE is assignable from... + rule.clear(); + rule.add(SqlTypeName.DATE); + rule.addAll(CHAR_TYPES); + rule.add(SqlTypeName.TIMESTAMP); + rules.add(SqlTypeName.DATE, rule); + + // TIME is assignable from... + rule.clear(); + rule.add(SqlTypeName.TIME); + rule.addAll(CHAR_TYPES); + rule.add(SqlTypeName.TIMESTAMP); + rules.add(SqlTypeName.TIME, rule); + + // TIMESTAMP is assignable from ... + rule.clear(); + rule.add(SqlTypeName.TIMESTAMP); + rule.addAll(CHAR_TYPES); + rule.add(SqlTypeName.TIME); + rule.add(SqlTypeName.DATE); + rules.add(SqlTypeName.TIMESTAMP, rule); + + // TIMESTAMP WITH LOCAL TIME ZONE is assignable from... + rule.clear(); + rule.add(SqlTypeName.TIMESTAMP); + rule.addAll(CHAR_TYPES); + rule.add(SqlTypeName.TIME); + rule.add(SqlTypeName.DATE); + + // GEOMETRY is assignable from ... + rule.clear(); + rule.add(SqlTypeName.GEOMETRY); + rule.addAll(CHAR_TYPES); + rules.add(SqlTypeName.GEOMETRY, rule); + + rule.clear(); + rule.addAll(CHAR_TYPES); + rule.addAll(YEAR_INTERVAL_TYPES); + + // IntervalYearMonth is assignable from... + rules.add(INTERVAL_YEAR_MONTH, rule); + + rule.clear(); + rule.addAll(CHAR_TYPES); + rule.addAll(DAY_INTERVAL_TYPES); + + List multiIntervals = List.of(INTERVAL_DAY_HOUR, INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND, + INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND); + + // IntervalDayHourMinuteSecond is assignable from... + for (SqlTypeName type : multiIntervals) + rules.add(type, rule); + + rule.clear(); + rule.addAll(CHAR_TYPES); + rule.addAll(EXACT_TYPES); + rule.addAll(YEAR_INTERVAL_TYPES); + + List singleYearIntervals = List.of(INTERVAL_YEAR, INTERVAL_MONTH); + + for (SqlTypeName type : singleYearIntervals) + rules.add(type, rule); + + rule.removeAll(YEAR_INTERVAL_TYPES); + rule.addAll(DAY_INTERVAL_TYPES); + + List singleDayIntervals = List.of(INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE, INTERVAL_SECOND); + + for (SqlTypeName type : singleDayIntervals) + rules.add(type, rule); + + // ARRAY is assignable from ... + rules.add(SqlTypeName.ARRAY, EnumSet.of(SqlTypeName.ARRAY)); + + // MAP is assignable from ... + rules.add(SqlTypeName.MAP, EnumSet.of(SqlTypeName.MAP)); + + // SYMBOL is assignable from ... + rules.add(SqlTypeName.SYMBOL, EnumSet.of(SqlTypeName.SYMBOL)); + + // ANY is assignable from ... + rule.clear(); + rule.add(SqlTypeName.TINYINT); + rule.add(SqlTypeName.SMALLINT); + rule.add(SqlTypeName.INTEGER); + rule.add(SqlTypeName.BIGINT); + rule.add(SqlTypeName.DECIMAL); + rule.add(SqlTypeName.FLOAT); + rule.add(SqlTypeName.REAL); + rule.add(SqlTypeName.TIME); + rule.add(SqlTypeName.DATE); + rule.add(SqlTypeName.TIMESTAMP); + rules.add(SqlTypeName.ANY, rule); + + INSTANCE = new IgniteTypeAssignmentsRules(rules.map); + } + + /** */ + private final Map> map; + + /** */ + private IgniteTypeAssignmentsRules(Map> map) { + this.map = ImmutableMap.copyOf(map); + } + + /** {@inheritDoc} */ + @Override public Map> getTypeMapping() { + return map; + } + + /** Keeps state while building the type mappings. */ + private static class Builder { + /** */ + final Map> map; + + /** */ + final LoadingCache, ImmutableSet> sets; + + /** Creates an empty {@link SqlTypeMappingRules.Builder}. */ + private Builder() { + map = new EnumMap<>(SqlTypeName.class); + + sets = CacheBuilder.newBuilder().build(CacheLoader.from(Sets::immutableEnumSet)); + } + + /** Add a map entry to the existing {@link SqlTypeMappingRules.Builder} mapping. */ + void add(SqlTypeName fromType, Set toTypes) { + try { + map.put(fromType, sets.get(toTypes)); + } + catch (UncheckedExecutionException | ExecutionException e) { + throw Util.throwAsRuntime("populating SqlTypeAssignmentRules", Util.causeOrSelf(e)); + } + } + } +} 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..3479f94a5aa3e 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,12 @@ public interface IgniteResource { /** */ @Resources.BaseMessage("Operator ''CAST'' supports only the parameters: value and target type.") Resources.ExInst invalidCastParameters(); + + /** */ + @Resources.BaseMessage("{0} datatype is not supported'") + Resources.ExInst dataTypeIsNotSupported(String a0); + + /** */ + @Resources.BaseMessage("Length for type {0} must be at least 1") + Resources.ExInst invalidStringLength(String typeName); } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DateTimeTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DateTimeTest.java index a2d23d8b18a6a..19ca4c980e4ba 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DateTimeTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/DateTimeTest.java @@ -22,6 +22,7 @@ import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimeZone; +import org.apache.calcite.sql.validate.SqlValidatorException; import org.apache.ignite.IgniteCache; import org.apache.ignite.cache.CacheMode; import org.apache.ignite.cache.QueryEntity; @@ -228,6 +229,11 @@ public void testDateTimeCast() throws Exception { assertThrows("SELECT CAST('2021-01-02' AS DATE FORMAT 'DD-MM-YY')", IgniteSQLException.class, "Operator 'CAST' supports only the parameters: value and target type."); + + assertThrows("SELECT CAST(timestamp '1992-09-26 02:30:00' AS BIGINT)", SqlValidatorException.class, + "Cast function cannot convert value "); + assertThrows("SELECT CAST(timestamp '1992-09-26 02:30:00' AS TINYINT)", SqlValidatorException.class, + "Cast function cannot convert value "); } /** */ diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java index c7eaa9499496f..fc259894cd6d7 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/FunctionsTest.java @@ -17,7 +17,6 @@ package org.apache.ignite.internal.processors.query.calcite.integration; -import java.math.BigDecimal; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; @@ -278,25 +277,28 @@ public void testRegex() { /** */ @Test public void testCastToBoolean() { - assertQuery("SELECT CAST(CAST(null AS DOUBLE) AS BOOLEAN)").returns(NULL_RESULT).check(); - assertQuery("SELECT CAST(CAST('1' AS DOUBLE) AS BOOLEAN)").returns(true).check(); - assertQuery("SELECT CAST(1.0 AS BOOLEAN)").returns(true).check(); - assertQuery("SELECT CAST(0.1 AS BOOLEAN)").returns(true).check(); - assertQuery("SELECT CAST(1 AS BOOLEAN)").returns(true).check(); - assertQuery("SELECT CAST(CAST('0' AS DOUBLE) AS BOOLEAN)").returns(false).check(); - assertQuery("SELECT CAST(0.0 AS BOOLEAN)").returns(false).check(); - assertQuery("SELECT CAST(0 AS BOOLEAN)").returns(false).check(); - assertQuery("SELECT CAST(CAST(? AS INT) AS BOOLEAN)").withParams(0).returns(false).check(); - assertQuery("SELECT CAST(CAST(? AS INT) AS BOOLEAN)").withParams(1).returns(true).check(); - assertQuery("SELECT CAST(CAST(? AS INT) AS BOOLEAN)").withParams(NULL_RESULT).returns(NULL_RESULT).check(); - assertQuery("SELECT CAST(CAST(? AS DOUBLE) AS BOOLEAN)").withParams(0.0d).returns(false).check(); - assertQuery("SELECT CAST(CAST(? AS DOUBLE) AS BOOLEAN)").withParams(1.0d).returns(true).check(); - assertQuery("SELECT CAST(CAST(? AS DOUBLE) AS BOOLEAN)").withParams(NULL_RESULT).returns(NULL_RESULT).check(); - assertQuery("SELECT CAST(CAST(? AS DECIMAL(2, 1)) AS BOOLEAN)") - .withParams(BigDecimal.valueOf(0, 1)).returns(false).check(); - assertQuery("SELECT CAST(CAST(? AS DECIMAL(2, 1)) AS BOOLEAN)") - .withParams(BigDecimal.valueOf(10, 1)).returns(true).check(); - assertQuery("SELECT CAST(CAST(? AS DECIMAL(2, 1)) AS BOOLEAN)") - .withParams(NULL_RESULT).returns(NULL_RESULT).check(); + assertQuery("SELECT 'TruE'::BOOLEAN").returns(true).check(); + assertQuery("SELECT 'false'::BOOLEAN").returns(false).check(); + assertQuery("SELECT 'FalsE'::BOOLEAN").returns(false).check(); + assertQuery("SELECT NULL::CHAR::BOOLEAN").returns(NULL_RESULT).check(); + assertQuery("SELECT ?::CHAR::BOOLEAN").withParams(NULL_RESULT).returns(NULL_RESULT).check(); + + String errStr = "Cast function cannot convert value"; + + assertThrows("SELECT 1.0::BOOLEAN", SqlValidatorException.class, errStr); + assertThrows("SELECT 1::DECIMAL::BOOLEAN", SqlValidatorException.class, errStr); + assertThrows("SELECT 1::DECIMAL(1)::BOOLEAN", SqlValidatorException.class, errStr); + assertThrows("SELECT 1::DECIMAL(2,1)::BOOLEAN", SqlValidatorException.class, errStr); + + assertQuery("SELECT 1::BOOLEAN").returns(true).check(); + assertQuery("SELECT 1::TINYINT::BOOLEAN").returns(true).check(); + assertQuery("SELECT 1::SMALLINT::BOOLEAN").returns(true).check(); + assertQuery("SELECT 0::BOOLEAN").returns(false).check(); + // TODO: fix dynamics + //assertThrows("SELECT ?::BOOLEAN", SqlValidatorException.class, errStr, 1); + assertThrows("SELECT 1.0::BOOLEAN", SqlValidatorException.class, errStr); + // TODO: fix dynamics + //assertThrows("SELECT ?::BOOLEAN", SqlValidatorException.class, errStr, 1.0); + //assertThrows("SELECT ?::BOOLEAN", SqlValidatorException.class, errStr, "1"); } } diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java index 11f9c66605cd5..e9c3c576052b9 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/IntervalTest.java @@ -22,6 +22,7 @@ import java.sql.Timestamp; import java.time.Duration; import java.time.Period; +import org.apache.calcite.sql.validate.SqlValidatorException; import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.junit.Test; @@ -159,8 +160,8 @@ public void testDml() { .returns(null, null) .check(); - assertThrows("SELECT * FROM test WHERE ym = INTERVAL 6 DAYS", IgniteSQLException.class, "Cannot apply"); - assertThrows("SELECT * FROM test WHERE dt = INTERVAL 6 YEARS", IgniteSQLException.class, "Cannot apply"); + assertThrows("SELECT * FROM test WHERE ym = INTERVAL 6 DAYS", SqlValidatorException.class, "operator must have compatible types"); + assertThrows("SELECT * FROM test WHERE dt = INTERVAL 6 YEARS", SqlValidatorException.class, "operator must have compatible types"); executeSql("UPDATE test SET dt = INTERVAL 3 DAYS WHERE ym = INTERVAL 1 MONTH"); executeSql("UPDATE test SET ym = INTERVAL 5 YEARS WHERE dt = INTERVAL 4 HOURS"); @@ -170,8 +171,8 @@ public void testDml() { assertThrows("UPDATE test SET dt = INTERVAL 5 YEARS WHERE ym = INTERVAL 1 MONTH", IgniteSQLException.class, "Cannot assign"); - assertThrows("UPDATE test SET ym = INTERVAL 8 YEARS WHERE dt = INTERVAL 1 MONTH", IgniteSQLException.class, - "Cannot apply"); + assertThrows("UPDATE test SET ym = INTERVAL 8 YEARS WHERE dt = INTERVAL 1 MONTH", SqlValidatorException.class, + "operator must have compatible types"); assertQuery("SELECT * FROM test") .returns(Period.ofMonths(1), Duration.ofDays(3)) @@ -180,8 +181,8 @@ public void testDml() { .returns(null, null) .check(); - assertThrows("DELETE FROM test WHERE ym = INTERVAL 6 DAYS", IgniteSQLException.class, "Cannot apply"); - assertThrows("DELETE FROM test WHERE dt = INTERVAL 6 YEARS", IgniteSQLException.class, "Cannot apply"); + assertThrows("DELETE FROM test WHERE ym = INTERVAL 6 DAYS", IgniteSQLException.class, "operator must have compatible types"); + assertThrows("DELETE FROM test WHERE dt = INTERVAL 6 YEARS", IgniteSQLException.class, "operator must have compatible types"); executeSql("DELETE FROM test WHERE ym = INTERVAL 1 MONTH"); executeSql("DELETE FROM test WHERE dt = INTERVAL 4 HOURS"); diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java index 98da886d3950a..f5bcc42e3d70c 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.stream.Collectors; import java.util.stream.IntStream; +import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.calcite.QueryChecker; import org.apache.ignite.internal.processors.query.calcite.sql.fun.IgniteStdSqlOperatorTable; import org.apache.ignite.internal.util.typedef.F; @@ -158,7 +159,7 @@ public void testStringFunctions() { assertExpression("LOWER('aA')").returns("aa").check(); assertExpression("INITCAP('aA')").returns("Aa").check(); assertExpression("TO_BASE64('aA')").returns("YUE=").check(); - assertExpression("FROM_BASE64('YUE=')::VARCHAR").returns("aA").check(); + assertExpression("FROM_BASE64('YUE=')").returns(new byte[] {(byte)97, (byte)65}).check(); assertExpression("MD5('aa')").returns("4124bc0a9335c27f086f24ba207a4912").check(); assertExpression("SHA1('aa')").returns("e0c9035898dd52fc65c41454cec9c4d2611bfb37").check(); assertExpression("SUBSTRING('aAaA', 2, 2)").returns("Aa").check(); @@ -297,7 +298,9 @@ public void testOtherFunctions() { assertExpression("DECODE(1, 1, 1, 2)").returns(1).check(); assertExpression("LEAST('a', 'b')").returns("a").check(); assertExpression("GREATEST('a', 'b')").returns("b").check(); - assertExpression("COMPRESS('')::VARCHAR").returns("").check(); + assertThrows("SELECT COMPRESS('')::VARCHAR", IgniteSQLException.class, + "Cast function cannot convert value of type VARBINARY to type VARCHAR"); + assertExpression("COMPRESS('')").returns(new byte[]{}).check(); assertExpression("OCTET_LENGTH(x'01')").returns(1).check(); assertExpression("CAST(INTERVAL 1 SECONDS AS INT)").returns(1).check(); // Converted to REINTERPRED. } diff --git a/modules/calcite/src/test/sql/cast/test_boolean_cast.test b/modules/calcite/src/test/sql/cast/test_boolean_cast.test index 5f79699099b48..4bfde7451cf63 100644 --- a/modules/calcite/src/test/sql/cast/test_boolean_cast.test +++ b/modules/calcite/src/test/sql/cast/test_boolean_cast.test @@ -39,7 +39,7 @@ false statement error SELECT CAST('12345' AS BOOLEAN) -query T +statement error SELECT CAST(CAST('12345' AS INTEGER) AS BOOLEAN) ---- true @@ -89,62 +89,62 @@ SELECT CAST(CAST('0' AS bigint) AS BOOLEAN) ---- false -query T +statement error SELECT CAST(CAST('1' AS decimal) AS BOOLEAN) ---- true -query T +statement error SELECT CAST(CAST('0' AS decimal) AS BOOLEAN) ---- false -query T +statement error SELECT CAST(CAST('1' AS decimal(1,0)) AS BOOLEAN) ---- true -query T +statement error SELECT CAST(CAST('0' AS decimal(1,0)) AS BOOLEAN) ---- false -query T +statement error SELECT CAST(CAST('1' AS decimal(9,0)) AS BOOLEAN) ---- true -query T +statement error SELECT CAST(CAST('0' AS decimal(9,0)) AS BOOLEAN) ---- false -query T +statement error SELECT CAST(CAST('1' AS decimal(38,0)) AS BOOLEAN) ---- true -query T +statement error SELECT CAST(CAST('0' AS decimal(38,0)) AS BOOLEAN) ---- false -query T +statement error SELECT CAST(CAST('1' AS float) AS BOOLEAN) ---- true -query T +statement error SELECT CAST(CAST('0' AS float) AS BOOLEAN) ---- false -query T +statement error SELECT CAST(CAST('1' AS double) AS BOOLEAN) ---- true -query T +statement error SELECT CAST(CAST('0' AS double) AS BOOLEAN) ---- false diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java index 8f2fedeb1ef49..824701999351b 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/IgniteUtils.java @@ -4362,7 +4362,7 @@ public static void close(@Nullable AutoCloseable rsrc, @Nullable IgniteLogger lo * @param log Logger to log possible checked exception with (optional). */ public static void close(@Nullable Socket sock, @Nullable IgniteLogger log) { - if (sock == null) + if (sock == null || sock.isClosed()) return; try { diff --git a/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java b/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java index 8270636dc07bb..0bb830bb103d7 100755 --- a/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java +++ b/modules/core/src/main/java/org/apache/ignite/internal/util/lang/GridFunc.java @@ -2872,6 +2872,12 @@ public static boolean contains(long[] arr, long val) { * @return Returns {@code true} if the specified arguments are equal, or both {@code null}. */ public static boolean eq(@Nullable Object o1, @Nullable Object o2) { + if (o1 instanceof Collection && o2 instanceof Collection) + return eqNotOrdered((Collection)o1, (Collection)o2); + + if (o1 != null && o2 != null && o1.getClass().isArray() && o2.getClass().isArray()) + return arrayEq(o1, o2); + return o1 == null ? o2 == null : o2 != null && (o1 == o2 || o1.equals(o2)); } From 23742985fcb1ea036e318c78592d5b433b3d98da Mon Sep 17 00:00:00 2001 From: Vladimir Steshin Date: Tue, 5 Nov 2024 18:07:56 +0300 Subject: [PATCH 2/2] fix --- .../query/calcite/integration/StdSqlOperatorsTest.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java index f5bcc42e3d70c..77202e6cb9945 100644 --- a/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java +++ b/modules/calcite/src/test/java/org/apache/ignite/internal/processors/query/calcite/integration/StdSqlOperatorsTest.java @@ -26,7 +26,6 @@ import java.util.Collections; import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.apache.ignite.internal.processors.query.IgniteSQLException; import org.apache.ignite.internal.processors.query.calcite.QueryChecker; import org.apache.ignite.internal.processors.query.calcite.sql.fun.IgniteStdSqlOperatorTable; import org.apache.ignite.internal.util.typedef.F; @@ -298,9 +297,7 @@ public void testOtherFunctions() { assertExpression("DECODE(1, 1, 1, 2)").returns(1).check(); assertExpression("LEAST('a', 'b')").returns("a").check(); assertExpression("GREATEST('a', 'b')").returns("b").check(); - assertThrows("SELECT COMPRESS('')::VARCHAR", IgniteSQLException.class, - "Cast function cannot convert value of type VARBINARY to type VARCHAR"); - assertExpression("COMPRESS('')").returns(new byte[]{}).check(); + assertExpression("COMPRESS('')::VARCHAR").returns("").check(); assertExpression("OCTET_LENGTH(x'01')").returns(1).check(); assertExpression("CAST(INTERVAL 1 SECONDS AS INT)").returns(1).check(); // Converted to REINTERPRED. }