You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by zs...@apache.org on 2023/08/23 12:40:40 UTC
[ignite-3] branch main updated: IGNITE-20185 Sql. Fix missed casting rules (#2431)
This is an automated email from the ASF dual-hosted git repository.
zstan pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 19f158ceff IGNITE-20185 Sql. Fix missed casting rules (#2431)
19f158ceff is described below
commit 19f158ceff6ac9bf71113edc3464b43771c55404
Author: Evgeniy Stanilovskiy <st...@gmail.com>
AuthorDate: Wed Aug 23 15:40:34 2023 +0300
IGNITE-20185 Sql. Fix missed casting rules (#2431)
---
.../ignite/internal/sql/engine/ItIntervalTest.java | 36 +--
.../internal/sql/engine/ItSqlOperatorsTest.java | 6 +-
.../sql/engine/datatypes/DataTypeTestSpecs.java | 10 +-
.../datatypes/tests/BaseDmlDataTypeTest.java | 21 ++
.../tests/BaseExpressionDataTypeTest.java | 11 +
.../datatypes/tests/BaseQueryDataTypeTest.java | 6 +
.../engine/datatypes/tests/TestTypeArguments.java | 2 +
.../varbinary/ItVarBinaryExpressionTest.java | 12 -
.../integrationTest/sql/types/blob/test_blob.test | 34 +--
.../sql/types/blob/test_blob_cast.test | 20 +-
.../sql/types/blob/test_blob_function.test | 27 +-
.../sql/types/blob/test_blob_operator.test | 45 ++-
.../sql/types/blob/test_blob_string.test | 2 +-
.../internal/sql/engine/exec/row/BaseTypeSpec.java | 2 +-
.../engine/util/IgniteCustomAssigmentsRules.java | 104 ++++---
.../ignite/internal/sql/engine/util/TypeUtils.java | 2 -
.../sql/engine/planner/CastResolutionTest.java | 311 +++++++++++++++------
17 files changed, 391 insertions(+), 260 deletions(-)
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java
index d01c3ce46f..c6b9ca5ac1 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItIntervalTest.java
@@ -70,29 +70,29 @@ public class ItIntervalTest extends ClusterPerClassIntegrationTest {
*/
@Test
public void testIntervalIntCast() {
- assertThrows(IgniteException.class, () -> eval("CAST(NULL::INTERVAL SECONDS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(NULL::INTERVAL MONTHS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL 1 SECONDS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL 2 MINUTES AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL 3 HOURS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL 4 DAYS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL -4 DAYS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL 5 MONTHS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL 6 YEARS AS INT)"));
- assertThrows(IgniteException.class, () -> eval("CAST(INTERVAL -6 YEARS AS INT)"));
+ assertNull(eval("CAST(NULL::INTERVAL SECONDS AS INT)"));
+ assertNull(eval("CAST(NULL::INTERVAL MONTHS AS INT)"));
+ assertEquals(1, eval("CAST(INTERVAL 1 SECONDS AS INT)"));
+ assertEquals(2, eval("CAST(INTERVAL 2 MINUTES AS INT)"));
+ assertEquals(3, eval("CAST(INTERVAL 3 HOURS AS INT)"));
+ assertEquals(4, eval("CAST(INTERVAL 4 DAYS AS INT)"));
+ assertEquals(-4, eval("CAST(INTERVAL -4 DAYS AS INT)"));
+ assertEquals(5, eval("CAST(INTERVAL 5 MONTHS AS INT)"));
+ assertEquals(6, eval("CAST(INTERVAL 6 YEARS AS INT)"));
+ assertEquals(-6, eval("CAST(INTERVAL -6 YEARS AS INT)"));
assertEquals("+6", eval("CAST(INTERVAL 6 YEARS AS VARCHAR)"));
assertEquals("+1", eval("CAST(INTERVAL 1 HOUR AS VARCHAR)"));
assertEquals("+7.000000", eval("CAST(INTERVAL 7 SECONDS AS VARCHAR)"));
- assertThrows(IgniteException.class, () -> eval("CAST(NULL::INT AS INTERVAL SECONDS)"));
- assertThrows(IgniteException.class, () -> eval("CAST(NULL::INT AS INTERVAL MONTHS)"));
- assertThrows(IgniteException.class, () -> eval("CAST(1 AS INTERVAL SECONDS)"));
- assertThrows(IgniteException.class, () -> eval("CAST(2 AS INTERVAL MINUTES)"));
- assertThrows(IgniteException.class, () -> eval("CAST(3 AS INTERVAL HOURS)"));
- assertThrows(IgniteException.class, () -> eval("CAST(4 AS INTERVAL DAYS)"));
- assertThrows(IgniteException.class, () -> eval("CAST(5 AS INTERVAL MONTHS)"));
- assertThrows(IgniteException.class, () -> eval("CAST(6 AS INTERVAL YEARS)"));
+ assertNull(eval("CAST(NULL::INT AS INTERVAL SECONDS)"));
+ assertNull(eval("CAST(NULL::INT AS INTERVAL MONTHS)"));
+ assertEquals(Duration.ofSeconds(1), eval("CAST(1 AS INTERVAL SECONDS)"));
+ assertEquals(Duration.ofMinutes(2), eval("CAST(2 AS INTERVAL MINUTES)"));
+ assertEquals(Duration.ofHours(3), eval("CAST(3 AS INTERVAL HOURS)"));
+ assertEquals(Duration.ofDays(4), eval("CAST(4 AS INTERVAL DAYS)"));
+ assertEquals(Period.ofMonths(5), eval("CAST(5 AS INTERVAL MONTHS)"));
+ assertEquals(Period.ofYears(6), eval("CAST(6 AS INTERVAL YEARS)"));
// Compound interval types cannot be cast.
assertThrowsEx("SELECT CAST(INTERVAL '1-2' YEAR TO MONTH AS INT)", IgniteException.class, "cannot convert");
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
index cba6d99d9a..e3e8018f57 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItSqlOperatorsTest.java
@@ -144,7 +144,7 @@ public class ItSqlOperatorsTest extends ClusterPerClassIntegrationTest {
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();
@@ -275,8 +275,10 @@ public class ItSqlOperatorsTest extends ClusterPerClassIntegrationTest {
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();
+ 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.
+ assertExpression("CAST(INTERVAL 1 DAY AS INT)").returns(1).check(); // Converted to REINTERPRED.
}
@Test
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/DataTypeTestSpecs.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/DataTypeTestSpecs.java
index 0b1811fa65..3e654d4a55 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/DataTypeTestSpecs.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/DataTypeTestSpecs.java
@@ -17,7 +17,6 @@
package org.apache.ignite.internal.sql.engine.datatypes;
-import static org.apache.ignite.internal.sql.engine.util.VarBinary.fromUtf8String;
import static org.apache.ignite.internal.sql.engine.util.VarBinary.varBinary;
import static org.apache.ignite.lang.IgniteStringFormatter.format;
@@ -101,7 +100,7 @@ public final class DataTypeTestSpecs {
@Override
public String toValueExpr(VarBinary value) {
- return format("'{}'::VARBINARY", value.asString(StandardCharsets.UTF_8));
+ return toLiteral(value);
}
/** {@inheritDoc} */
@@ -120,12 +119,13 @@ public final class DataTypeTestSpecs {
@Override
public TestDataSamples<VarBinary> createSamples(IgniteTypeFactory typeFactory) {
List<VarBinary> values = List.of(
- fromUtf8String("1"), fromUtf8String("2"), fromUtf8String("3"));
+ VarBinary.fromBytes(new byte[] {(byte) 1}),
+ VarBinary.fromBytes(new byte[] {(byte) 2}),
+ VarBinary.fromBytes(new byte[] {(byte) 3}));
TestDataSamples.Builder<VarBinary> samples = TestDataSamples.builder();
- samples.add(values, SqlTypeName.VARCHAR, b -> b.asString(StandardCharsets.UTF_8));
- samples.add(values, SqlTypeName.CHAR, b -> b.asString(StandardCharsets.UTF_8));
+ samples.add(values, SqlTypeName.BINARY, b -> b.asString(StandardCharsets.UTF_8).getBytes(StandardCharsets.UTF_8));
return samples.build();
}
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseDmlDataTypeTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseDmlDataTypeTest.java
index 79a61a5f57..f5f2246304 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseDmlDataTypeTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseDmlDataTypeTest.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.datatypes.tests;
+import static org.apache.ignite.internal.sql.engine.datatypes.DataTypeTestSpecs.VARBINARY_TYPE;
import static org.apache.ignite.lang.IgniteStringFormatter.format;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@@ -48,6 +49,11 @@ public abstract class BaseDmlDataTypeTest<T extends Comparable<T>> extends BaseD
@ParameterizedTest
@MethodSource("convertedFrom")
public void testInsertFromDynamicParameterFromConvertible(TestTypeArguments<T> arguments) {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
var t = assertThrows(IgniteException.class, () -> {
runSql("INSERT INTO t VALUES (1, ?)", arguments.argValue(0));
});
@@ -104,6 +110,11 @@ public abstract class BaseDmlDataTypeTest<T extends Comparable<T>> extends BaseD
@ParameterizedTest
@MethodSource("convertedFrom")
public void testUpdateFromDynamicParameterFromConvertible(TestTypeArguments<T> arguments) {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
String insert = format("INSERT INTO t VALUES (1, {})", arguments.valueExpr(0));
runSql(insert);
@@ -124,6 +135,11 @@ public abstract class BaseDmlDataTypeTest<T extends Comparable<T>> extends BaseD
@ParameterizedTest
@MethodSource("convertedFrom")
public void testDisallowMismatchTypesOnInsert(TestTypeArguments<T> arguments) {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
var query = format("INSERT INTO t (id, test_key) VALUES (10, null), (20, {})", arguments.valueExpr(0));
var t = assertThrows(IgniteException.class, () -> runSql(query));
@@ -136,6 +152,11 @@ public abstract class BaseDmlDataTypeTest<T extends Comparable<T>> extends BaseD
@ParameterizedTest
@MethodSource("convertedFrom")
public void testDisallowMismatchTypesOnInsertDynamicParam(TestTypeArguments<T> arguments) {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
Object value1 = arguments.argValue(0);
var query = "INSERT INTO t (id, test_key) VALUES (1, null), (2, ?)";
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseExpressionDataTypeTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseExpressionDataTypeTest.java
index 60adb2705d..b7dc86924d 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseExpressionDataTypeTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseExpressionDataTypeTest.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.datatypes.tests;
+import static org.apache.ignite.internal.sql.engine.datatypes.DataTypeTestSpecs.VARBINARY_TYPE;
import static org.apache.ignite.lang.IgniteStringFormatter.format;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@@ -127,6 +128,11 @@ public abstract class BaseExpressionDataTypeTest<T extends Comparable<T>> extend
@ParameterizedTest
@MethodSource("convertedFrom")
public void testCoalesceMissingTypesIsIllegal(TestTypeArguments arguments) {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
IgniteException t = assertThrows(IgniteException.class, () -> {
checkQuery(format("SELECT COALESCE($0, {})", arguments.valueExpr(0))).check();
});
@@ -219,6 +225,11 @@ public abstract class BaseExpressionDataTypeTest<T extends Comparable<T>> extend
/** Data type from string. **/
@Test
public void testCastFromString() {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
T value = dataSamples.values().get(0);
String stringValue = testTypeSpec.toStringValue(value);
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseQueryDataTypeTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseQueryDataTypeTest.java
index 4e1a5162a7..b5e543f1c0 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseQueryDataTypeTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/BaseQueryDataTypeTest.java
@@ -17,6 +17,7 @@
package org.apache.ignite.internal.sql.engine.datatypes.tests;
+import static org.apache.ignite.internal.sql.engine.datatypes.DataTypeTestSpecs.VARBINARY_TYPE;
import static org.apache.ignite.lang.IgniteStringFormatter.format;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
@@ -103,6 +104,11 @@ public abstract class BaseQueryDataTypeTest<T extends Comparable<T>> extends Bas
@ParameterizedTest
@MethodSource("convertedFrom")
public void testEqConditionWithDynamicParameters(TestTypeArguments<T> arguments) {
+ // TODO: fix in scope of https://issues.apache.org/jira/browse/IGNITE-20226
+ if (testTypeSpec == VARBINARY_TYPE) {
+ return;
+ }
+
T value1 = values.get(0);
runSql("INSERT INTO t VALUES(1, ?)", value1);
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/TestTypeArguments.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/TestTypeArguments.java
index 286132a6eb..4a17be11eb 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/TestTypeArguments.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/tests/TestTypeArguments.java
@@ -408,6 +408,8 @@ public final class TestTypeArguments<T extends Comparable<T>> {
}
} else if (argument instanceof String) {
sqlLiteral = SqlLiteral.createCharString(argument.toString(), "UTF-8", SqlParserPos.ZERO);
+ } else if (argument instanceof byte[]) {
+ sqlLiteral = SqlLiteral.createBinaryString((byte[]) argument, SqlParserPos.ZERO);
} else if (argument instanceof Boolean) {
sqlLiteral = SqlLiteral.createBoolean((Boolean) argument, SqlParserPos.ZERO);
} else {
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/varbinary/ItVarBinaryExpressionTest.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/varbinary/ItVarBinaryExpressionTest.java
index 2e91db6bb3..d4ae9c70c7 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/varbinary/ItVarBinaryExpressionTest.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/sql/engine/datatypes/varbinary/ItVarBinaryExpressionTest.java
@@ -117,18 +117,6 @@ public class ItVarBinaryExpressionTest extends BaseExpressionDataTypeTest<VarBin
*/
@Test
public void testCastToDifferentLengths() {
- checkQuery("SELECT CAST('123' AS VARBINARY(2))")
- .returns(VarBinary.fromUtf8String("12"))
- .check();
-
- checkQuery("SELECT CAST('123' AS VARBINARY(100))")
- .returns((VarBinary.fromUtf8String("123")))
- .check();
-
- checkQuery("SELECT CAST('123' AS VARBINARY)")
- .returns((VarBinary.fromUtf8String("123")))
- .check();
-
checkQuery("SELECT CAST(X'ffffff' AS VARBINARY(2))")
.returns((varBinary(new byte[]{(byte) 0xfff, (byte) 0xff})))
.check();
diff --git a/modules/runner/src/integrationTest/sql/types/blob/test_blob.test b/modules/runner/src/integrationTest/sql/types/blob/test_blob.test
index ea38858017..b2ed31e2d0 100644
--- a/modules/runner/src/integrationTest/sql/types/blob/test_blob.test
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob.test
@@ -62,46 +62,16 @@ statement ok
DELETE FROM blobs
# Implicit cast
-statement ok
+statement error
INSERT INTO blobs VALUES('blablabla')
-# BINARY with “non-printable” octets
-statement ok
-INSERT INTO blobs VALUES('abc �'::VARBINARY)
-
-query T
-SELECT b::varchar FROM blobs ORDER BY b
-----
-abc �
-blablabla
-
# BINARY null and empty values
+statement error
query T
SELECT ''::VARBINARY
-----
-(empty)
query T
SELECT NULL::VARBINARY
----
NULL
-statement ok
-CREATE TABLE blob_empty (b binary);
-
-statement ok
-INSERT INTO blob_empty VALUES('')
-
-statement ok
-INSERT INTO blob_empty VALUES(''::VARBINARY)
-
-statement ok
-INSERT INTO blob_empty VALUES(NULL), (NULL::VARBINARY)
-
-query T rowsort
-SELECT * FROM blob_empty
-----
-NULL
-NULL
-(empty)
-(empty)
diff --git a/modules/runner/src/integrationTest/sql/types/blob/test_blob_cast.test b/modules/runner/src/integrationTest/sql/types/blob/test_blob_cast.test
index 17fc15761d..fb74587428 100644
--- a/modules/runner/src/integrationTest/sql/types/blob/test_blob_cast.test
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_cast.test
@@ -5,17 +5,15 @@
statement ok
PRAGMA enable_verification
-# BLOB to VARCHAR -> CastFromBlob, it always results in a hex representation
+# BLOB to VARCHAR -> CastFromBlob, which is forbidden.
+statement error
query T
SELECT 'a'::binary::VARCHAR
-----
-a
# VARCHAR to BLOB -> CastToBlob
+statement error
query T
SELECT 'a'::VARCHAR::binary
-----
-61
# Hex string with BLOB
query T
@@ -24,28 +22,24 @@ SELECT x'2000FF'::varbinary
2000ff
# CastFromBlob with hex string
+statement error
query T
SELECT x'612061'::VARBINARY::VARCHAR
-----
-a a
# CastFromBlob and after CastToBlob with hex string
+statement error
query T
SELECT x'612061'::VARBINARY::VARCHAR::VARBINARY
-----
-612061
# CastFromBlob -> CastToBlob -> CastFromBlob with hex string
+statement error
query T
SELECT x'612061'::VARBINARY::VARCHAR::VARBINARY::VARCHAR
-----
-a a
# CastToBlob -> CastFromBlob -> CastToBlob with hex string
+statement error
query T
SELECT x'612061'::VARCHAR::VARBINARY::VARCHAR::VARBINARY
-----
-612061
statement error
SELECT 1::VARBINARY
diff --git a/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test b/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test
index 80381607d6..7649e9e11d 100644
--- a/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_function.test
@@ -8,22 +8,11 @@ PRAGMA enable_verification
statement ok
CREATE TABLE blobs (b varbinary);
-statement ok
+statement error
INSERT INTO blobs VALUES ('a'::binary)
-# conventional concat
-query T
-SELECT (b || 'ZZ'::varbinary)::varchar FROM blobs
-----
-aZZ
-
-query T
-SELECT 'abc '::varbinary || 'klm *'::varbinary || x'EFBFBD'::varbinary || 'T'::varbinary
-----
-616263206b6c6d202aefbfbd54
-
statement ok
-INSERT INTO blobs VALUES ('abc '::varbinary || 'klm *'::varbinary || x'EFBFBD'::varbinary || 'T'::varbinary)
+INSERT INTO blobs VALUES (x'aa'::binary), (x'bbcc')
query I
SELECT COUNT(*) FROM blobs
@@ -35,7 +24,7 @@ query I
SELECT OCTET_LENGTH(b) FROM blobs ORDER BY 1
----
1
-13
+2
# HEX strings
statement ok
@@ -45,9 +34,9 @@ statement ok
INSERT INTO blobs VALUES (x'FF'::binary)
query T
-SELECT b || 'ZZ'::varbinary FROM blobs
+SELECT b || x'BB'::varbinary FROM blobs
----
-ff5a5a
+ffbb
query T
SELECT b || x'5A5A'::varbinary FROM blobs
@@ -65,19 +54,17 @@ DELETE FROM blobs
statement ok
INSERT INTO blobs VALUES (x'FF'::binary)
-statement ok
+statement error
INSERT INTO blobs VALUES ('FF'::varbinary)
statement ok
INSERT INTO blobs VALUES (x'55AAFF55AAFF55AAFF01'::varbinary)
-statement ok
+statement error
INSERT INTO blobs VALUES ('55AAFF55AAFF55AAFF01'::varbinary)
query I
SELECT OCTET_LENGTH(b) FROM blobs ORDER BY 1
----
1
-2
10
-20
diff --git a/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test b/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test
index 3572d8e54a..bd59b8bfa5 100644
--- a/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_operator.test
@@ -10,10 +10,10 @@ CREATE TABLE blobs (b varbinary, g INTEGER);
# strings: hello -> \x68656C6C6F, r -> \x72
statement ok
-INSERT INTO blobs VALUES ('hello', 0)
+INSERT INTO blobs VALUES (x'aabbccddee', 0)
statement ok
-INSERT INTO blobs VALUES (x'00' || 'whatisgoingon'::varbinary, 1)
+INSERT INTO blobs VALUES (x'00' || x'aabb'::varbinary, 1)
statement ok
INSERT INTO blobs VALUES (NULL, 0)
@@ -25,36 +25,23 @@ INSERT INTO blobs VALUES (x'FFFEFB', 1)
query IITT
SELECT COUNT(*), COUNT(b), MIN(b), MAX(b) FROM blobs
----
-4 3 00776861746973676f696e676f6e fffefb
+4 3 00aabb fffefb
# ORDER BY
query TI
SELECT * FROM blobs ORDER BY b
----
-00776861746973676f696e676f6e 1
-68656c6c6f 0
+00aabb 1
+aabbccddee 0
fffefb 1
NULL 0
-# GROUP BY
-statement ok
-INSERT INTO blobs VALUES ('hello', 3)
-
-statement ok
-INSERT INTO blobs VALUES (x'00' || 'whatisgoingon'::varbinary, 9)
-
-statement ok
-INSERT INTO blobs VALUES (NULL, 0)
-
-statement ok
-INSERT INTO blobs VALUES (x'FFFEFB', 19)
-
query II
SELECT b, SUM(g) FROM blobs GROUP BY b ORDER BY b
----
-00776861746973676f696e676f6e 10
-68656c6c6f 3
-fffefb 20
+00aabb 1
+aabbccddee 0
+fffefb 1
NULL 0
# JOIN
@@ -62,10 +49,10 @@ statement ok
CREATE TABLE blobs2 (b VARBINARY, g INTEGER);
statement ok
-INSERT INTO blobs2 VALUES ('hello', 0)
+INSERT INTO blobs2 VALUES (x'aabbccddee', 0)
statement ok
-INSERT INTO blobs2 VALUES (x'00' || 'whatisgoingon'::varbinary, 100)
+INSERT INTO blobs2 VALUES (x'00' || x'aabb'::varbinary, 100)
statement ok
INSERT INTO blobs2 VALUES (NULL, 0)
@@ -77,14 +64,14 @@ INSERT INTO blobs2 VALUES (x'FFFEFB', 200)
query TR
SELECT L.b, SUM(L.g) FROM blobs as L JOIN blobs2 AS R ON L.b=R.b GROUP BY L.b ORDER BY L.b
----
-00776861746973676f696e676f6e 10.000000
-68656c6c6f 3.000000
-fffefb 20.000000
+00aabb 1
+aabbccddee 0
+fffefb 1
# group by blobs2.b, implicit JOIN
query TR
SELECT R.b, SUM(R.g) FROM blobs as L, blobs2 AS R WHERE L.b=R.b GROUP BY R.b ORDER BY R.b
----
-00776861746973676f696e676f6e 200.000000
-68656c6c6f 0.000000
-fffefb 400.000000
+00aabb 100
+aabbccddee 0
+fffefb 200
diff --git a/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test b/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test
index a4866ab117..dc279feb34 100644
--- a/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test
+++ b/modules/runner/src/integrationTest/sql/types/blob/test_blob_string.test
@@ -10,7 +10,7 @@ CREATE TABLE blobs (b varbinary);
# insert BINARY from string
statement ok
-INSERT INTO blobs VALUES ('aaaaaaaaaa'::varbinary)
+INSERT INTO blobs VALUES (x'aaaaaaaaaaaaaaaaaaaa'::varbinary)
# sizes: 10, 100, 1000, 10000 -> double plus two due to hexadecimal representation
# The concat function casts BINARY to VARCHAR, resulting in a hex string
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/row/BaseTypeSpec.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/row/BaseTypeSpec.java
index 97b11880cc..ad000e06f8 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/row/BaseTypeSpec.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/exec/row/BaseTypeSpec.java
@@ -37,7 +37,7 @@ public class BaseTypeSpec extends TypeSpec {
/** Creates a basic type with the given nullability. */
public BaseTypeSpec(NativeType nativeType, boolean nullable) {
super(nullable);
- // TODO Uncomment his check after https://issues.apache.org/jira/browse/IGNITE-19096 is fixed
+ // TODO Uncomment his check after https://issues.apache.org/jira/browse/IGNITE-20163 is fixed
//this.nativeType = Objects.requireNonNull(nativeType, "native type has not been specified.");
this.nativeType = nativeType;
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteCustomAssigmentsRules.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteCustomAssigmentsRules.java
index 5ba42535c5..adaced4036 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteCustomAssigmentsRules.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/IgniteCustomAssigmentsRules.java
@@ -23,6 +23,19 @@ 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.YEAR_INTERVAL_TYPES;
import com.google.common.cache.CacheBuilder;
@@ -34,6 +47,7 @@ import com.google.common.collect.Sets;
import com.google.common.util.concurrent.UncheckedExecutionException;
import java.util.EnumSet;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
@@ -65,30 +79,14 @@ public class IgniteCustomAssigmentsRules implements SqlTypeMappingRule {
Set<SqlTypeName> rule = EnumSet.noneOf(SqlTypeName.class);
- // IntervalYearMonth is assignable from...
- for (SqlTypeName interval : YEAR_INTERVAL_TYPES) {
- rules.add(interval, YEAR_INTERVAL_TYPES);
- }
- for (SqlTypeName interval : DAY_INTERVAL_TYPES) {
- rules.add(interval, DAY_INTERVAL_TYPES);
- }
-
// MULTISET is assignable from...
rules.add(SqlTypeName.MULTISET, EnumSet.of(SqlTypeName.MULTISET));
rule.clear();
rule.addAll(EXACT_TYPES);
- rule.addAll(APPROX_TYPES);
+ rule.addAll(FRACTIONAL_TYPES);
rule.addAll(CHAR_TYPES);
- // 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);
- }
-
// FLOAT (up to 64 bit floating point) is assignable from...
// REAL (32 bit floating point) is assignable from...
// DOUBLE is assignable from...
@@ -97,30 +95,32 @@ public class IgniteCustomAssigmentsRules implements SqlTypeMappingRule {
rules.add(type, rule);
}
- // VARBINARY is assignable from...
- rule.clear();
- rule.addAll(BINARY_TYPES);
- rule.addAll(CHAR_TYPES);
- rules.add(SqlTypeName.VARBINARY, 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);
- // CHAR is assignable from...
+ // 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(CHAR_TYPES);
rule.addAll(BINARY_TYPES);
- rule.addAll(EXACT_TYPES);
- rule.addAll(APPROX_TYPES);
- rule.addAll(DAY_INTERVAL_TYPES);
- rule.addAll(YEAR_INTERVAL_TYPES);
- rule.add(SqlTypeName.BOOLEAN);
- rule.add(SqlTypeName.DATE);
- rule.add(SqlTypeName.TIME);
- rule.add(SqlTypeName.TIMESTAMP);
- rules.add(SqlTypeName.CHAR, rule);
+ 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(BINARY_TYPES);
rule.addAll(EXACT_TYPES);
rule.addAll(APPROX_TYPES);
rule.addAll(DAY_INTERVAL_TYPES);
@@ -129,17 +129,13 @@ public class IgniteCustomAssigmentsRules implements SqlTypeMappingRule {
rule.add(SqlTypeName.DATE);
rule.add(SqlTypeName.TIME);
rule.add(SqlTypeName.TIMESTAMP);
+
+ rules.add(SqlTypeName.CHAR, rule);
rules.add(SqlTypeName.VARCHAR, rule);
// BOOLEAN is assignable from...
rules.add(SqlTypeName.BOOLEAN, EnumSet.of(SqlTypeName.BOOLEAN, SqlTypeName.CHAR, SqlTypeName.VARCHAR));
- // BINARY is assignable from...
- rule.clear();
- rule.addAll(BINARY_TYPES);
- rule.addAll(CHAR_TYPES);
- rules.add(SqlTypeName.BINARY, rule);
-
// DATE is assignable from...
rule.clear();
rule.add(SqlTypeName.DATE);
@@ -180,15 +176,39 @@ public class IgniteCustomAssigmentsRules implements SqlTypeMappingRule {
rule.addAll(CHAR_TYPES);
rule.addAll(YEAR_INTERVAL_TYPES);
- for (SqlTypeName type : 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<SqlTypeName> 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<SqlTypeName> 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);
- for (SqlTypeName type : DAY_INTERVAL_TYPES) {
+ List<SqlTypeName> singleDayIntervals = List.of(INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE,
+ INTERVAL_SECOND);
+
+ for (SqlTypeName type : singleDayIntervals) {
rules.add(type, rule);
}
diff --git a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
index f80a7343b0..d09f7a5e89 100644
--- a/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
+++ b/modules/sql-engine/src/main/java/org/apache/ignite/internal/sql/engine/util/TypeUtils.java
@@ -635,8 +635,6 @@ public class TypeUtils {
NativeType nativeType = IgniteTypeFactory.relDataTypeToNative(type);
return RowSchemaTypes.nativeTypeWithNullability(nativeType, nullable);
} else if (SqlTypeName.ANY == type.getSqlTypeName()) {
- // TODO: After https://issues.apache.org/jira/browse/IGNITE-19096 is fixed
- // it should be possible to remove branch.
// TODO Some JSON functions that return ANY as well : https://issues.apache.org/jira/browse/IGNITE-20163
return new BaseTypeSpec(null, nullable);
} else if (SqlTypeUtil.isNull(type)) {
diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CastResolutionTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CastResolutionTest.java
index f9ad59e10c..9e16b484d4 100644
--- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CastResolutionTest.java
+++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/planner/CastResolutionTest.java
@@ -17,14 +17,28 @@
package org.apache.ignite.internal.sql.engine.planner;
+import static java.lang.String.format;
+import static org.apache.calcite.sql.type.SqlTypeName.ALL_TYPES;
import static org.apache.calcite.sql.type.SqlTypeName.BINARY_TYPES;
+import static org.apache.calcite.sql.type.SqlTypeName.BOOLEAN_TYPES;
import static org.apache.calcite.sql.type.SqlTypeName.CHAR_TYPES;
import static org.apache.calcite.sql.type.SqlTypeName.DATETIME_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_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.NUMERIC_TYPES;
+import static org.apache.calcite.sql.type.SqlTypeName.INTERVAL_YEAR_MONTH;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
@@ -33,59 +47,92 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import org.apache.calcite.sql.type.SqlTypeCoercionRule;
+import org.apache.calcite.sql.type.SqlTypeMappingRule;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.ignite.internal.sql.engine.type.UuidType;
+import org.apache.ignite.internal.sql.engine.util.IgniteCustomAssigmentsRules;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
/** Test CAST type to type possibilities. */
public class CastResolutionTest extends AbstractPlannerTest {
- private static final String castErrorMessage = "Cast function cannot convert value of type";
+ private static final String CAST_ERROR_MESSAGE = "Cast function cannot convert value of type";
- private static final Set<String> numericNames = NUMERIC_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
+ private static final Set<String> NUMERIC_NAMES = new HashSet<>();
- private static final Set<String> charNames = CHAR_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
+ private static final Set<String> FRACTIONAL_NAMES = FRACTIONAL_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
- private static final Set<String> binaryNames = BINARY_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
+ private static final Set<String> EXACT_NUMERIC = EXACT_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
- private static final Set<String> dtNames = DATETIME_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
+ private static final Set<String> CHAR_NAMES = CHAR_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
- private static final Set<String> charAndNumericNames = new HashSet<>();
+ private static final Set<String> BINARY_NAMES = BINARY_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
- private static final Set<String> charAndBinaryNames = new HashSet<>();
+ private static final Set<String> DT_NAMES = DATETIME_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
- private static final Set<String> charAndTs = new HashSet<>();
+ private static final Set<String> YM_INTERVAL = Set.of(INTERVAL_YEAR.getName(), INTERVAL_MONTH.getName());
- private static final Set<String> charAndDt = new HashSet<>();
+ private static final Set<String> DAY_INTERVAL = Set.of(INTERVAL_HOUR.getName(), INTERVAL_MINUTE.getName(), INTERVAL_SECOND.getName());
- private static final Set<String> charAndYInterval = new HashSet<>();
+ private static final Set<String> ALL_BESIDES_BINARY_NAMES =
+ BOOLEAN_TYPES.stream().map(SqlTypeName::getName).collect(Collectors.toSet());
- private static final Set<String> charAndDInterval = new HashSet<>();
+ private static final Set<String> CHAR_NUMERIC_AND_INTERVAL_NAMES = new HashSet<>();
- private static final String commonTemplate = "SELECT CAST('1'::%s AS %s)";
+ private static final Set<String> CHAR_AND_NUMERIC_NAMES = new HashSet<>();
- private static final String intervalTemplate = "SELECT CAST(INTERVAL 1 %s AS %s)";
+ private static final Set<String> CHAR_AND_TS = new HashSet<>();
- static {
- numericNames.add("NUMERIC");
+ private static final Set<String> CHAR_AND_DT = new HashSet<>();
- charAndNumericNames.addAll(numericNames);
- charAndNumericNames.addAll(charNames);
+ private static final Set<String> CHAR_EXACT_AND_YM_INTERVAL = new HashSet<>();
- charAndBinaryNames.addAll(charNames);
- charAndBinaryNames.addAll(binaryNames);
+ private static final Set<String> CHAR_EXACT_AND_DAY_INTERVAL = new HashSet<>();
- charAndTs.addAll(charNames);
- charAndTs.add(SqlTypeName.TIMESTAMP.getName());
+ private static final String COMMON_TEMPLATE = "SELECT CAST('1'::%s AS %s)";
- charAndDt.addAll(dtNames);
- charAndDt.addAll(charNames);
+ private static final String INTERVAL_TEMPLATE = "SELECT CAST(%s AS %s)";
- charAndYInterval.addAll(List.of(INTERVAL_YEAR.getName(), INTERVAL_MONTH.getName()));
- charAndYInterval.addAll(charNames);
+ private static final String BINARY_TEMPLATE = "SELECT CAST(X'01'::%s AS %s)";
- charAndDInterval.addAll(List.of(INTERVAL_HOUR.getName(), INTERVAL_MINUTE.getName()));
- charAndDInterval.addAll(charNames);
+ static {
+ NUMERIC_NAMES.add("NUMERIC");
+ NUMERIC_NAMES.addAll(EXACT_NUMERIC);
+ NUMERIC_NAMES.addAll(FRACTIONAL_NAMES);
+
+ ALL_BESIDES_BINARY_NAMES.addAll(NUMERIC_NAMES);
+ ALL_BESIDES_BINARY_NAMES.addAll(FRACTIONAL_NAMES);
+ ALL_BESIDES_BINARY_NAMES.addAll(CHAR_NAMES);
+ ALL_BESIDES_BINARY_NAMES.addAll(DT_NAMES);
+ ALL_BESIDES_BINARY_NAMES.addAll(YM_INTERVAL);
+ ALL_BESIDES_BINARY_NAMES.addAll(DAY_INTERVAL);
+ ALL_BESIDES_BINARY_NAMES.add("NUMERIC");
+ ALL_BESIDES_BINARY_NAMES.add(UuidType.NAME);
+
+ CHAR_NUMERIC_AND_INTERVAL_NAMES.addAll(NUMERIC_NAMES);
+ CHAR_NUMERIC_AND_INTERVAL_NAMES.addAll(CHAR_NAMES);
+ CHAR_NUMERIC_AND_INTERVAL_NAMES.addAll(YM_INTERVAL);
+ CHAR_NUMERIC_AND_INTERVAL_NAMES.addAll(DAY_INTERVAL);
+
+ CHAR_AND_NUMERIC_NAMES.addAll(NUMERIC_NAMES);
+ CHAR_AND_NUMERIC_NAMES.addAll(CHAR_NAMES);
+
+ CHAR_AND_TS.addAll(CHAR_NAMES);
+ CHAR_AND_TS.add(SqlTypeName.TIMESTAMP.getName());
+
+ CHAR_AND_DT.addAll(DT_NAMES);
+ CHAR_AND_DT.addAll(CHAR_NAMES);
+
+ CHAR_EXACT_AND_YM_INTERVAL.addAll(List.of(INTERVAL_YEAR.getName(), INTERVAL_MONTH.getName()));
+ CHAR_EXACT_AND_YM_INTERVAL.addAll(CHAR_NAMES);
+ CHAR_EXACT_AND_YM_INTERVAL.addAll(EXACT_NUMERIC);
+ CHAR_EXACT_AND_YM_INTERVAL.add("NUMERIC");
+
+ CHAR_EXACT_AND_DAY_INTERVAL.addAll(List.of(INTERVAL_HOUR.getName(), INTERVAL_MINUTE.getName()));
+ CHAR_EXACT_AND_DAY_INTERVAL.addAll(CHAR_NAMES);
+ CHAR_EXACT_AND_DAY_INTERVAL.addAll(EXACT_NUMERIC);
+ CHAR_EXACT_AND_DAY_INTERVAL.add("NUMERIC");
}
/** Test CAST possibility for different supported types. */
@@ -96,112 +143,210 @@ public class CastResolutionTest extends AbstractPlannerTest {
Set<String> allTypes = Arrays.stream(CastMatrix.values()).map(v -> v.from).collect(Collectors.toSet());
for (CastMatrix types : CastMatrix.values()) {
- String from = types.from;
+ String fromInitial = types.from;
Set<String> toTypes = types.toTypes;
- boolean allCastsPossible = false;
- boolean interval = isInterval(from);
- String template = interval ? intervalTemplate : commonTemplate;
- from = interval ? from.substring("interval_".length()) : from;
+ String template = template(fromInitial);
+ String from = makeUsableIntervalFromType(fromInitial);
for (String toType : toTypes) {
- toType = isInterval(toType) ? makeUsableIntervalType(toType) : toType;
-
- if (toType.equals("ALL")) {
- allCastsPossible = true;
-
- for (String type : allTypes) {
- type = isInterval(type) ? makeUsableIntervalType(type) : type;
-
- testItems.add(checkStatement().sql(String.format(template, from, type)).ok());
- }
-
- break;
- }
+ toType = makeUsableIntervalToType(toType);
// TODO: https://issues.apache.org/jira/browse/IGNITE-19274
- if (toType.contains("LOCAL_TIME")) {
+ if (toType.contains("LOCAL TIME")) {
continue;
}
- testItems.add(checkStatement().sql(String.format(template, from, toType)).ok(false));
+ testItems.add(checkStatement().sql(format(template, from, toType)).ok(false));
}
- if (!interval) {
- testItems.add(checkStatement().sql(String.format("SELECT '1'::%s", from)).ok(false));
+ testItems.add(checkStatement().sql(format(template, from, makeUsableIntervalToType(fromInitial))).ok());
+
+ testItems.add(checkStatement().sql(format(INTERVAL_TEMPLATE, "NULL", makeUsableIntervalToType(fromInitial))).ok());
+
+ String finalFrom = from;
+ Set<String> deprecatedCastTypes = allTypes.stream().filter(t -> !toTypes.contains(t) && !t.equals(finalFrom))
+ .collect(Collectors.toSet());
+
+ for (String toType : deprecatedCastTypes) {
+ boolean isInterval = toType.toLowerCase().contains("interval");
+
+ toType = isInterval ? makeUsableIntervalToType(toType) : toType;
+
+ testItems.add(checkStatement().sql(format(template, from, toType)).fails(CAST_ERROR_MESSAGE));
}
+ }
+
+ return testItems.stream();
+ }
+
+ /* Check that casts between intervals and exact numerics can involve only intervals with a single datetime field. */
+ @TestFactory
+ public Stream<DynamicTest> testMultiIntervals() {
+ List<DynamicTest> testItems = new ArrayList<>();
+
+ testItems.add(checkStatement().sql("SELECT CAST(1 AS interval year to month)").fails(CAST_ERROR_MESSAGE));
+ testItems.add(checkStatement().sql("SELECT CAST(1 AS interval day to hour)").fails(CAST_ERROR_MESSAGE));
+ testItems.add(checkStatement().sql("SELECT CAST(1 AS interval day to minute)").fails(CAST_ERROR_MESSAGE));
+ testItems.add(checkStatement().sql("SELECT CAST(1 AS interval day to second)").fails(CAST_ERROR_MESSAGE));
+ testItems.add(checkStatement().sql("SELECT CAST(1 AS interval hour to minute)").fails(CAST_ERROR_MESSAGE));
+ testItems.add(checkStatement().sql("SELECT CAST(1 AS interval hour to second)").fails(CAST_ERROR_MESSAGE));
+
+ testItems.add(checkStatement().sql("SELECT CAST('1' AS interval hour to second)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1' AS interval day to hour)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1' AS interval day to minute)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1' AS interval day to second)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1' AS interval hour to minute)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1' AS interval hour to second)").ok());
+
+ testItems.add(checkStatement().sql("SELECT CAST('1'::VARCHAR AS interval hour to second)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1'::VARCHAR AS interval day to hour)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1'::VARCHAR AS interval day to minute)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1'::VARCHAR AS interval day to second)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1'::VARCHAR AS interval hour to minute)").ok());
+ testItems.add(checkStatement().sql("SELECT CAST('1'::VARCHAR AS interval hour to second)").ok());
+
+ return testItems.stream();
+ }
+
+ @TestFactory
+ public Stream<DynamicTest> allowedCastsFromNull() {
+ List<DynamicTest> testItems = new ArrayList<>();
+
+ SqlTypeMappingRule rules = SqlTypeCoercionRule.instance(IgniteCustomAssigmentsRules.instance().getTypeMapping());
- // all types are allowed.
- if (allCastsPossible) {
+ for (SqlTypeName type : ALL_TYPES) {
+ if (type == SqlTypeName.NULL) {
continue;
}
- if (!interval) {
- testItems.add(checkStatement().sql(String.format(template, from, from)).ok());
+ testItems.add(DynamicTest.dynamicTest(format("ALLOW: from: %s to: %s", SqlTypeName.NULL.getName(), type),
+ () -> assertTrue(rules.canApplyFrom(type, SqlTypeName.NULL))));
+ }
+
+ return testItems.stream();
+ }
+
+ @TestFactory
+ public Stream<DynamicTest> testRulesContainsIntervalWithExactTypesRules() {
+ List<DynamicTest> testItems = new ArrayList<>();
+
+ List<SqlTypeName> singleIntervals = List.of(INTERVAL_YEAR, INTERVAL_MONTH, INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE,
+ INTERVAL_SECOND);
+
+ List<SqlTypeName> nonSingleIntervals = List.of(INTERVAL_YEAR_MONTH, INTERVAL_DAY_MINUTE, INTERVAL_DAY_SECOND,
+ INTERVAL_HOUR_MINUTE, INTERVAL_HOUR_SECOND, INTERVAL_MINUTE_SECOND);
+
+ List<SqlTypeName> singleYearIntervals = List.of(INTERVAL_YEAR, INTERVAL_MONTH);
+
+ List<SqlTypeName> singleDayIntervals = List.of(INTERVAL_DAY, INTERVAL_HOUR, INTERVAL_MINUTE,
+ INTERVAL_SECOND);
+
+ SqlTypeMappingRule rules = SqlTypeCoercionRule.instance(IgniteCustomAssigmentsRules.instance().getTypeMapping());
+
+ for (SqlTypeName toType : singleIntervals) {
+ for (SqlTypeName fromType : EXACT_TYPES) {
+ testItems.add(DynamicTest.dynamicTest(format("ALLOW: from: %s to: %s", fromType, toType),
+ () -> assertTrue(rules.canApplyFrom(toType, fromType))));
}
+ }
- String finalFrom = from;
- Set<String> deprecatedCastTypes = allTypes.stream().filter(t -> !toTypes.contains(t) && !t.equals(finalFrom))
- .collect(Collectors.toSet());
+ for (SqlTypeName toType : nonSingleIntervals) {
+ for (SqlTypeName fromType : EXACT_TYPES) {
+ testItems.add(DynamicTest.dynamicTest(format("ALLOW: from: %s to: %s", fromType, toType),
+ () -> assertFalse(rules.canApplyFrom(toType, fromType))));
+ }
+ }
- for (String toType : deprecatedCastTypes) {
- toType = isInterval(toType) ? makeUsableIntervalType(toType) : toType;
+ for (SqlTypeName toType : singleYearIntervals) {
+ for (SqlTypeName fromType : singleYearIntervals) {
+ testItems.add(DynamicTest.dynamicTest(format("ALLOW: from: %s to: %s", fromType, toType),
+ () -> assertTrue(rules.canApplyFrom(toType, fromType))));
+ }
+ }
- testItems.add(checkStatement().sql(String.format(template, from, toType)).fails(castErrorMessage));
+ for (SqlTypeName toType : singleDayIntervals) {
+ for (SqlTypeName fromType : singleDayIntervals) {
+ testItems.add(DynamicTest.dynamicTest(format("ALLOW: from: %s to: %s", fromType, toType),
+ () -> assertTrue(rules.canApplyFrom(toType, fromType))));
+ }
+ }
+
+ for (SqlTypeName toType : singleYearIntervals) {
+ for (SqlTypeName fromType : singleDayIntervals) {
+ testItems.add(DynamicTest.dynamicTest(format("FORBID: from: %s to: %s", fromType, toType),
+ () -> assertFalse(rules.canApplyFrom(toType, fromType))));
+ }
+ }
+
+ for (SqlTypeName toType : singleDayIntervals) {
+ for (SqlTypeName fromType : singleYearIntervals) {
+ testItems.add(DynamicTest.dynamicTest(format("FORBID: from: %s to: %s", fromType, toType),
+ () -> assertFalse(rules.canApplyFrom(toType, fromType))));
}
}
return testItems.stream();
}
- private static boolean isInterval(String typeName) {
- return typeName.toLowerCase().contains("interval");
+ private static String template(String typeName) {
+ if (typeName.toLowerCase().contains("interval")) {
+ return INTERVAL_TEMPLATE;
+ } else if (typeName.toLowerCase().contains("binary")) {
+ return BINARY_TEMPLATE;
+ } else {
+ return COMMON_TEMPLATE;
+ }
}
- private static String makeUsableIntervalType(String typeName) {
+ private static String makeUsableIntervalToType(String typeName) {
return typeName.replace("_", " ");
}
+ private static String makeUsableIntervalFromType(String typeName) {
+ return typeName.replace("_", " 1 ");
+ }
+
private enum CastMatrix {
- BOOLEAN(SqlTypeName.BOOLEAN.getName(), charNames),
+ BOOLEAN(SqlTypeName.BOOLEAN.getName(), CHAR_NAMES),
- INT8(SqlTypeName.TINYINT.getName(), charAndNumericNames),
+ INT8(SqlTypeName.TINYINT.getName(), CHAR_NUMERIC_AND_INTERVAL_NAMES),
- INT16(SqlTypeName.SMALLINT.getName(), charAndNumericNames),
+ INT16(SqlTypeName.SMALLINT.getName(), CHAR_NUMERIC_AND_INTERVAL_NAMES),
- INT32(SqlTypeName.INTEGER.getName(), charAndNumericNames),
+ INT32(SqlTypeName.INTEGER.getName(), CHAR_NUMERIC_AND_INTERVAL_NAMES),
- INT64(SqlTypeName.BIGINT.getName(), charAndNumericNames),
+ INT64(SqlTypeName.BIGINT.getName(), CHAR_NUMERIC_AND_INTERVAL_NAMES),
- DECIMAL(SqlTypeName.DECIMAL.getName(), charAndNumericNames),
+ DECIMAL(SqlTypeName.DECIMAL.getName(), CHAR_NUMERIC_AND_INTERVAL_NAMES),
- REAL(SqlTypeName.REAL.getName(), charAndNumericNames),
+ REAL(SqlTypeName.REAL.getName(), CHAR_AND_NUMERIC_NAMES),
- DOUBLE(SqlTypeName.DOUBLE.getName(), charAndNumericNames),
+ DOUBLE(SqlTypeName.DOUBLE.getName(), CHAR_AND_NUMERIC_NAMES),
- FLOAT(SqlTypeName.FLOAT.getName(), charAndNumericNames),
+ FLOAT(SqlTypeName.FLOAT.getName(), CHAR_AND_NUMERIC_NAMES),
- NUMERIC("NUMERIC", charAndNumericNames),
+ NUMERIC("NUMERIC", CHAR_NUMERIC_AND_INTERVAL_NAMES),
- UUID(UuidType.NAME, new HashSet<>(charNames)),
+ UUID(UuidType.NAME, new HashSet<>(CHAR_NAMES)),
- VARCHAR(SqlTypeName.VARCHAR.getName(), Set.of("ALL")),
+ VARCHAR(SqlTypeName.VARCHAR.getName(), ALL_BESIDES_BINARY_NAMES),
- CHAR(SqlTypeName.CHAR.getName(), Set.of("ALL")),
+ CHAR(SqlTypeName.CHAR.getName(), ALL_BESIDES_BINARY_NAMES),
- VARBINARY(SqlTypeName.VARBINARY.getName(), charAndBinaryNames),
+ VARBINARY(SqlTypeName.VARBINARY.getName(), BINARY_NAMES),
- BINARY(SqlTypeName.BINARY.getName(), charAndBinaryNames),
+ BINARY(SqlTypeName.BINARY.getName(), BINARY_NAMES),
- DATE(SqlTypeName.DATE.getName(), charAndTs),
+ DATE(SqlTypeName.DATE.getName(), CHAR_AND_TS),
- TIME(SqlTypeName.TIME.getName(), charAndTs),
+ TIME(SqlTypeName.TIME.getName(), CHAR_AND_TS),
- TIMESTAMP(SqlTypeName.TIMESTAMP.getName(), charAndDt),
+ TIMESTAMP(SqlTypeName.TIMESTAMP.getName(), CHAR_AND_DT),
- INTERVAL_YEAR(SqlTypeName.INTERVAL_YEAR.getName(), charAndYInterval),
+ INTERVAL_YEAR(SqlTypeName.INTERVAL_YEAR.getName(), CHAR_EXACT_AND_YM_INTERVAL),
- INTERVAL_HOUR(SqlTypeName.INTERVAL_HOUR.getName(), charAndDInterval);
+ INTERVAL_HOUR(SqlTypeName.INTERVAL_HOUR.getName(), CHAR_EXACT_AND_DAY_INTERVAL);
// TODO: https://issues.apache.org/jira/browse/IGNITE-19274
//TIMESTAMP_TS(SqlTypeName.TIMESTAMP_WITH_LOCAL_TIME_ZONE.getName(), charAndDt);