raminqaf commented on code in PR #29311:
URL: https://github.com/apache/flink/pull/29311#discussion_r4153457365
##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java:
##########
@@ -432,6 +435,272 @@ private static List<TestSetSpec> variantPrimitiveCasts() {
TINYINT()));
}
+ private static List<TestSetSpec> primitiveToVariantCasts() {
+ final VariantBuilder builder = Variant.newBuilder();
+ final LocalDateTime nanos =
LocalDateTime.parse("2026-09-25T10:15:30.123456789");
+ final Instant instant = Instant.parse("2026-09-25T10:15:30.123Z");
+ return List.of(
+ // A value keeps the kind of its SQL type, so an integer keeps
its width, and a
+ // string is wrapped rather than parsed.
+ CastTestSpecBuilder.testCastTo(VARIANT())
+ .fromCase(BOOLEAN(), true, builder.of(true))
+ .fromCase(INT(), 42, builder.of(42))
+ .fromCase(BIGINT(), 1L, builder.of(1L))
+ .fromCase(BIGINT(), 10000000000L,
builder.of(10000000000L))
+ .fromCase(DOUBLE(), 1.5d, builder.of(1.5d))
+ .fromCase(
+ DECIMAL(4, 2),
+ new BigDecimal("12.50"),
+ builder.of(new BigDecimal("12.50")))
+ .fromCase(STRING(), "{\"a\":1}",
builder.of("{\"a\":1}"))
+ .fromCase(
+ DATE(),
+ LocalDate.parse("2026-09-25"),
+ builder.of(LocalDate.parse("2026-09-25")))
+ .fromCase(TIMESTAMP(9), nanos, builder.of(nanos))
+ .fromCase(TIMESTAMP_LTZ(3), instant,
builder.of(instant))
+ .fromCase(UUID(), DEFAULT_UUID,
builder.of(DEFAULT_UUID))
+ .fromCase(INT(), null, null)
+ // a type without a VARIANT kind is rejected at
validation
+ .failValidation(INTERVAL(MONTH()), Period.ofMonths(2))
+ .build(),
+ TestSetSpec.forExpression("Cast a primitive to VARIANT and
back")
+ .onFieldsWithData(
+ 42L,
+ new BigDecimal("12.50"),
+ "hello",
+ LocalTime.of(10, 15, 30),
+ nanos,
+ instant,
+ DEFAULT_UUID,
+ new byte[] {1, 2, 3})
+ .andDataTypes(
+ BIGINT(),
+ DECIMAL(4, 2),
+ STRING(),
+ TIME(),
+ TIMESTAMP(9),
+ TIMESTAMP_LTZ(3),
+ UUID(),
+ BYTES())
+ .testResult(
+ $("f0").cast(VARIANT()).cast(BIGINT()),
+ "CAST(CAST(f0 AS VARIANT) AS BIGINT)",
+ 42L,
+ BIGINT())
+ .testResult(
+ $("f1").cast(VARIANT()).cast(DECIMAL(4, 2)),
+ "CAST(CAST(f1 AS VARIANT) AS DECIMAL(4, 2))",
+ new BigDecimal("12.50"),
+ DECIMAL(4, 2))
+ .testResult(
+ $("f2").cast(VARIANT()).cast(STRING()),
+ "CAST(CAST(f2 AS VARIANT) AS STRING)",
+ "hello",
+ STRING())
+ .testResult(
+ $("f3").cast(VARIANT()).cast(TIME()),
+ "CAST(CAST(f3 AS VARIANT) AS TIME)",
+ LocalTime.of(10, 15, 30),
+ TIME())
+ .testResult(
+ $("f4").cast(VARIANT()).cast(TIMESTAMP(9)),
+ "CAST(CAST(f4 AS VARIANT) AS TIMESTAMP(9))",
+ nanos,
+ TIMESTAMP(9))
+ .testResult(
+ $("f5").cast(VARIANT()).cast(TIMESTAMP_LTZ(3)),
+ "CAST(CAST(f5 AS VARIANT) AS
TIMESTAMP_LTZ(3))",
+ instant,
+ TIMESTAMP_LTZ(3))
+ .testResult(
+ $("f6").cast(VARIANT()).cast(UUID()),
+ "CAST(CAST(f6 AS VARIANT) AS UUID)",
+ DEFAULT_UUID,
+ UUID())
+ .testResult(
+ $("f7").cast(VARIANT()).cast(BYTES()),
+ "CAST(CAST(f7 AS VARIANT) AS BYTES)",
+ new byte[] {1, 2, 3},
+ BYTES()),
+ TestSetSpec.forExpression("Cast TIME to VARIANT and back")
+ .onFieldsWithData(
+ LocalTime.of(12, 34, 56, 123_000_000),
+ LocalTime.MIDNIGHT,
+ LocalTime.of(23, 59, 59, 999_000_000))
+ .andDataTypes(TIME(3), TIME(0), TIME(3))
+ .testResult(
+ $("f0").cast(VARIANT()).cast(TIME(3)),
+ "CAST(CAST(f0 AS VARIANT) AS TIME(3))",
+ LocalTime.of(12, 34, 56, 123_000_000),
+ TIME(3))
+ .testResult(
+ $("f1").cast(VARIANT()).cast(TIME(0)),
+ "CAST(CAST(f1 AS VARIANT) AS TIME(0))",
+ LocalTime.MIDNIGHT,
+ TIME(0))
+ .testResult(
+ $("f2").cast(VARIANT()).cast(TIME(3)),
+ "CAST(CAST(f2 AS VARIANT) AS TIME(3))",
+ LocalTime.of(23, 59, 59, 999_000_000),
+ TIME(3)),
+ // a VARIANT holds at most 16 MiB, so a longer string fails
CAST and TRY_CAST
+ // returns NULL
+ TestSetSpec.forExpression("Cast a string over the size limit
to VARIANT")
+ .onFieldsWithData("x")
+ .andDataTypes(STRING())
+ .testSqlRuntimeError(
+ "CAST(REPEAT(f0, 17000000) AS VARIANT)",
+ TableRuntimeException.class,
+ "A VARIANT is limited to 16 MiB")
+ .testTableApiRuntimeError(
+ $("f0").repeat(17000000).cast(VARIANT()),
+ TableRuntimeException.class,
+ "A VARIANT is limited to 16 MiB")
+ .testResult(
+ $("f0").repeat(17000000).tryCast(VARIANT()),
+ "TRY_CAST(REPEAT(f0, 17000000) AS VARIANT)",
+ null,
+ VARIANT()),
+ // nanoseconds only cover 1677-09-21 to 2262-04-11, while
microseconds hold any year
+ TestSetSpec.forExpression("Cast a late TIMESTAMP to VARIANT")
+ .onFieldsWithData(
+ LocalDateTime.parse("3000-01-01T00:00"),
+ LocalDateTime.parse("3000-01-01T00:00"))
+ .andDataTypes(TIMESTAMP(9), TIMESTAMP(6))
+ .testSqlRuntimeError(
+ "CAST(f0 AS VARIANT)",
+ TableRuntimeException.class,
+ "1677-09-21 to 2262-04-11")
+ .testTableApiRuntimeError(
+ $("f0").cast(VARIANT()),
+ TableRuntimeException.class,
+ "1677-09-21 to 2262-04-11")
+ .testResult(
+ $("f0").tryCast(VARIANT()),
+ "TRY_CAST(f0 AS VARIANT)",
+ null,
+ VARIANT())
+ .testResult(
+ $("f1").cast(VARIANT()).cast(TIMESTAMP(6)),
+ "CAST(CAST(f1 AS VARIANT) AS TIMESTAMP(6))",
+ LocalDateTime.parse("3000-01-01T00:00"),
+ TIMESTAMP(6)),
+ TestSetSpec.forExpression("Cast a literal to VARIANT")
+ .onFieldsWithData(0)
+ .testResult(
+ lit(42).cast(VARIANT()),
+ "CAST(42 AS VARIANT)",
+ builder.of(42),
+ VARIANT().notNull())
+ .testResult(
+ lit("hello").cast(VARIANT()),
+ "CAST('hello' AS VARIANT)",
+ builder.of("hello"),
+ VARIANT().notNull())
+ // a NULL literal casts to a SQL NULL, not to a
variant null
+ .testSqlResult("CAST(NULL AS VARIANT)", null,
VARIANT())
+ .testSqlResult("TRY_CAST(NULL AS VARIANT)", null,
VARIANT()),
+ // a VARIANT is not limited to JSON, so NaN and infinity are
kept and round-trip
+ TestSetSpec.forExpression("Cast a NaN literal to VARIANT and
back")
+ .onFieldsWithData(0)
+ .testResult(
+
lit("NaN").cast(DOUBLE()).cast(VARIANT()).cast(DOUBLE()),
+ "CAST(CAST(CAST('NaN' AS DOUBLE) AS VARIANT)
AS DOUBLE)",
+ Double.NaN,
+ DOUBLE())
+ .testResult(
+
lit("NaN").cast(DOUBLE()).cast(VARIANT()).cast(STRING()),
+ "CAST(CAST(CAST('NaN' AS DOUBLE) AS VARIANT)
AS STRING)",
+ "NaN",
+ STRING()),
+ TestSetSpec.forExpression("Cast a non-finite number to VARIANT
and back")
+ // the non-finite values are produced at runtime from
strings
+ .onFieldsWithData("NaN", "Infinity", "-Infinity")
+ .andDataTypes(STRING(), STRING(), STRING())
+ .testResult(
+
$("f0").cast(DOUBLE()).cast(VARIANT()).cast(DOUBLE()),
+ "CAST(CAST(CAST(f0 AS DOUBLE) AS VARIANT) AS
DOUBLE)",
+ Double.NaN,
+ DOUBLE())
+ .testResult(
+
$("f1").cast(FLOAT()).cast(VARIANT()).cast(FLOAT()),
+ "CAST(CAST(CAST(f1 AS FLOAT) AS VARIANT) AS
FLOAT)",
+ Float.POSITIVE_INFINITY,
+ FLOAT())
+ .testResult(
+
$("f2").cast(DOUBLE()).cast(VARIANT()).cast(FLOAT()),
+ "CAST(CAST(CAST(f2 AS DOUBLE) AS VARIANT) AS
FLOAT)",
+ Float.NEGATIVE_INFINITY,
+ FLOAT())
+ // an integer cannot hold NaN, so that cast still fails
+ .testSqlRuntimeError(
+ "CAST(CAST(CAST(f0 AS DOUBLE) AS VARIANT) AS
INT)",
+ TableRuntimeException.class,
+ "overflowed"),
+ TestSetSpec.forExpression("Cast narrow and fixed-width types
to VARIANT and back")
+ .onFieldsWithData((byte) 42, (short) 1000)
+ .andDataTypes(TINYINT(), SMALLINT())
+ .testResult(
+ $("f0").cast(VARIANT()).cast(TINYINT()),
+ "CAST(CAST(f0 AS VARIANT) AS TINYINT)",
+ (byte) 42,
+ TINYINT())
+ .testResult(
+ $("f1").cast(VARIANT()).cast(SMALLINT()),
+ "CAST(CAST(f1 AS VARIANT) AS SMALLINT)",
+ (short) 1000,
+ SMALLINT())
+ // a CHAR(n) is stored with its padding, so the spaces
survive
+ .testResult(
+
lit("ab").cast(CHAR(4)).cast(VARIANT()).cast(STRING()),
+ "CAST(CAST(CAST('ab' AS CHAR(4)) AS VARIANT)
AS STRING)",
+ "ab ",
+ STRING())
+ .testResult(
+
lit("ab").cast(CHAR(4)).cast(VARIANT()).cast(CHAR(4)),
+ "CAST(CAST(CAST('ab' AS CHAR(4)) AS VARIANT)
AS CHAR(4))",
+ "ab ",
+ CHAR(4))
+ // a BINARY(n) is stored with its zero padding
+ .testResult(
+ lit(new byte[] {1, 2})
+ .cast(BINARY(4))
+ .cast(VARIANT())
+ .cast(BYTES()),
+ "CAST(CAST(CAST(X'0102' AS BINARY(4)) AS
VARIANT) AS BYTES)",
+ new byte[] {1, 2, 0, 0},
+ BYTES()),
+ // a constructed type casts element by element when each child
casts to VARIANT
+ TestSetSpec.forExpression("Cast a constructed type to one of
VARIANT and back")
Review Comment:
Done, renamed
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]