This is an automated email from the ASF dual-hosted git repository.
snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git
The following commit(s) were added to refs/heads/master by this push:
new 5a1e6202832 [FLINK-40493][table] Support casting from `VARIANT` to
`UUID`
5a1e6202832 is described below
commit 5a1e6202832a4573d2605ebf31542f17c7890b81
Author: Moritz Manner <[email protected]>
AuthorDate: Tue Sep 15 08:56:44 2026 +0200
[FLINK-40493][table] Support casting from `VARIANT` to `UUID`
---
docs/content.zh/docs/sql/reference/data-types.md | 1 +
docs/content/docs/sql/reference/data-types.md | 1 +
.../types/logical/utils/LogicalTypeCasts.java | 6 ++-
.../flink/table/types/logical/utils/UuidUtils.java | 53 ++++++++++++++++++++++
.../flink/table/types/LogicalTypeCastsTest.java | 7 +--
.../casting/VariantToPrimitiveCastRule.java | 5 ++
.../table/planner/plan/utils/RexLiteralUtil.java | 8 +---
.../functions/casting/CastRuleProviderTest.java | 2 +
.../planner/functions/casting/CastRulesTest.java | 11 +++++
.../table/runtime/functions/UuidCastUtils.java | 7 +--
.../table/runtime/functions/VariantCastUtils.java | 15 ++++++
.../table/data/conversion/UuidUuidConverter.java | 12 ++---
12 files changed, 104 insertions(+), 24 deletions(-)
diff --git a/docs/content.zh/docs/sql/reference/data-types.md
b/docs/content.zh/docs/sql/reference/data-types.md
index 26fc40150b2..8ff0137dd48 100644
--- a/docs/content.zh/docs/sql/reference/data-types.md
+++ b/docs/content.zh/docs/sql/reference/data-types.md
@@ -1558,6 +1558,7 @@ to make it fit. Otherwise `CAST` fails and `TRY_CAST`
returns `NULL`.
| `TIMESTAMP` | `TIMESTAMP(p)` |
| `TIMESTAMP_LTZ` | `TIMESTAMP_LTZ(p)` |
| `BYTES` | `BINARY(n)`, `VARBINARY(n)`, and a character string |
+| `UUID` | `UUID` |
| any scalar | `STRING`, `CHAR(n)`, `VARCHAR(n)` |
| `NULL` | SQL `NULL` for any nullable target |
diff --git a/docs/content/docs/sql/reference/data-types.md
b/docs/content/docs/sql/reference/data-types.md
index 1a687f6b972..768c2e1ab7d 100644
--- a/docs/content/docs/sql/reference/data-types.md
+++ b/docs/content/docs/sql/reference/data-types.md
@@ -1566,6 +1566,7 @@ to make it fit. Otherwise `CAST` fails and `TRY_CAST`
returns `NULL`.
| `TIMESTAMP` | `TIMESTAMP(p)` |
| `TIMESTAMP_LTZ` | `TIMESTAMP_LTZ(p)` |
| `BYTES` | `BINARY(n)`, `VARBINARY(n)`, and a character string |
+| `UUID` | `UUID` |
| any scalar | `STRING`, `CHAR(n)`, `VARCHAR(n)` |
| `NULL` | SQL `NULL` for any nullable target |
diff --git
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java
index e213c477164..d351a36746d 100644
---
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java
+++
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/LogicalTypeCasts.java
@@ -410,7 +410,11 @@ public final class LogicalTypeCasts {
// A UUID can be parsed from a character string and reinterpreted from
its 16-byte encoding.
// The reverse direction (UUID to CHAR/VARCHAR and to
BINARY(16)/BYTES) is declared on the
// respective target types.
- castTo(UUID).implicitFrom(UUID).explicitFromFamily(CHARACTER_STRING,
BINARY_STRING).build();
+ castTo(UUID)
+ .implicitFrom(UUID)
+ .explicitFromFamily(CHARACTER_STRING, BINARY_STRING)
+ .explicitFrom(VARIANT)
+ .build();
}
/**
diff --git
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/UuidUtils.java
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/UuidUtils.java
new file mode 100644
index 00000000000..653b708760e
--- /dev/null
+++
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/types/logical/utils/UuidUtils.java
@@ -0,0 +1,53 @@
+/*
+ * 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.flink.table.types.logical.utils;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.table.types.logical.UuidType;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.UUID;
+
+/**
+ * Helpers to convert between a {@link UUID} and the canonical 16-byte
big-endian encoding used to
+ * store a {@link UuidType} value: the most significant 64 bits followed by
the least significant 64
+ * bits.
+ */
+@Internal
+public final class UuidUtils {
+
+ private UuidUtils() {}
+
+ /** Encodes a {@link UUID} into its canonical 16-byte big-endian
representation. */
+ public static byte[] toBytes(UUID uuid) {
+ final ByteBuffer buffer = ByteBuffer.allocate(UuidType.BYTE_LENGTH);
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ buffer.putLong(uuid.getMostSignificantBits());
+ buffer.putLong(uuid.getLeastSignificantBits());
+ return buffer.array();
+ }
+
+ /** Decodes the canonical 16-byte big-endian representation into a {@link
UUID}. */
+ public static UUID fromBytes(byte[] bytes) {
+ final ByteBuffer buffer = ByteBuffer.wrap(bytes);
+ buffer.order(ByteOrder.BIG_ENDIAN);
+ return new UUID(buffer.getLong(), buffer.getLong());
+ }
+}
diff --git
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java
index f696227034e..1e96b65a130 100644
---
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java
+++
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java
@@ -290,6 +290,7 @@ class LogicalTypeCastsTest {
Arguments.of(new VariantType(), new CharType(), false, true),
Arguments.of(new VariantType(), VarCharType.STRING_TYPE,
false, true),
Arguments.of(new VariantType(), new TimeType(), false, true),
+ Arguments.of(new VariantType(), new UuidType(), false, true),
// variant identity cast is implicit
Arguments.of(new VariantType(), new VariantType(), true, true),
// A variant imposes a schema on a constructed target,
explicit only, recursing on
@@ -386,11 +387,11 @@ class LogicalTypeCastsTest {
Arguments.of(new BinaryType(10), new UuidType(), false, false),
// UUID identity cast is implicit
Arguments.of(new UuidType(), new UuidType(), true, true),
- // numeric and VARIANT are not castable to or from UUID
+ // numeric is not castable to or from UUID
Arguments.of(new UuidType(), new IntType(), false, false),
Arguments.of(new IntType(), new UuidType(), false, false),
- Arguments.of(new UuidType(), new VariantType(), false, false),
- Arguments.of(new VariantType(), new UuidType(), false, false));
+ // a UUID does not cast to VARIANT
+ Arguments.of(new UuidType(), new VariantType(), false, false));
}
@ParameterizedTest(name = "{index}: [From: {0}, To: {1}, Implicit: {2},
Explicit: {3}]")
diff --git
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToPrimitiveCastRule.java
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToPrimitiveCastRule.java
index 4a16d7fce81..b0bf6703e64 100644
---
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToPrimitiveCastRule.java
+++
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToPrimitiveCastRule.java
@@ -75,6 +75,7 @@ class VariantToPrimitiveCastRule extends
AbstractNullAwareCodeGeneratorCastRule<
case TIME_WITHOUT_TIME_ZONE:
case TIMESTAMP_WITHOUT_TIME_ZONE:
case TIMESTAMP_WITH_LOCAL_TIME_ZONE:
+ case UUID:
return true;
default:
return false;
@@ -201,6 +202,10 @@ class VariantToPrimitiveCastRule extends
AbstractNullAwareCodeGeneratorCastRule<
inputTerm,
LogicalTypeChecks.getPrecision(targetLogicalType)));
break;
+ case UUID:
+ writer.assignStmt(
+ returnVariable, staticCall(VariantCastUtils.class,
"toUuid", inputTerm));
+ break;
default:
throw new IllegalArgumentException(
"Unsupported target type for casting from VARIANT: " +
targetLogicalType);
diff --git
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/RexLiteralUtil.java
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/RexLiteralUtil.java
index 2e3561f3d5f..ecabff65235 100644
---
a/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/RexLiteralUtil.java
+++
b/flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/plan/utils/RexLiteralUtil.java
@@ -28,6 +28,7 @@ import
org.apache.flink.table.planner.codegen.CodeGenException;
import org.apache.flink.table.types.logical.DistinctType;
import org.apache.flink.table.types.logical.LogicalType;
import org.apache.flink.table.types.logical.utils.LogicalTypeChecks;
+import org.apache.flink.table.types.logical.utils.UuidUtils;
import org.apache.calcite.avatica.util.ByteString;
import org.apache.calcite.rel.type.RelDataType;
@@ -39,7 +40,6 @@ import org.apache.calcite.util.TimeString;
import org.apache.calcite.util.TimestampString;
import java.math.BigDecimal;
-import java.nio.ByteBuffer;
import java.time.ZoneOffset;
import static
org.apache.flink.table.planner.utils.TimestampStringUtils.toLocalDateTime;
@@ -105,11 +105,7 @@ public class RexLiteralUtil {
break;
case UUID:
if (value instanceof java.util.UUID) {
- final java.util.UUID uuidValue = (java.util.UUID) value;
- return ByteBuffer.allocate(16)
- .putLong(uuidValue.getMostSignificantBits())
- .putLong(uuidValue.getLeastSignificantBits())
- .array();
+ return UuidUtils.toBytes((java.util.UUID) value);
}
break;
case DECIMAL:
diff --git
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRuleProviderTest.java
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRuleProviderTest.java
index 0bebbd2c815..df910dfe1c4 100644
---
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRuleProviderTest.java
+++
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRuleProviderTest.java
@@ -47,6 +47,7 @@ import static org.apache.flink.table.api.DataTypes.TIME;
import static org.apache.flink.table.api.DataTypes.TIMESTAMP;
import static org.apache.flink.table.api.DataTypes.TIMESTAMP_LTZ;
import static org.apache.flink.table.api.DataTypes.TINYINT;
+import static org.apache.flink.table.api.DataTypes.UUID;
import static org.apache.flink.table.api.DataTypes.VARIANT;
import static org.apache.flink.table.types.logical.VarCharType.STRING_TYPE;
import static org.assertj.core.api.Assertions.assertThat;
@@ -135,6 +136,7 @@ class CastRuleProviderTest {
assertThat(CastRuleProvider.exists(VARIANT,
TIMESTAMP_LTZ().getLogicalType())).isTrue();
assertThat(CastRuleProvider.exists(VARIANT,
TIME().getLogicalType())).isTrue();
assertThat(CastRuleProvider.exists(VARIANT,
BYTES().getLogicalType())).isTrue();
+ assertThat(CastRuleProvider.exists(VARIANT,
UUID().getLogicalType())).isTrue();
assertThat(CastRuleProvider.canFail(VARIANT, INT)).isTrue();
// INTERVAL has no VARIANT counterpart, so it is not a castable target
diff --git
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java
index 2aa04aeab2d..09c0ef5c10d 100644
---
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java
+++
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/casting/CastRulesTest.java
@@ -168,6 +168,7 @@ class CastRulesTest {
private static final Bitmap DEFAULT_BITMAP = Bitmap.fromArray(new int[]
{0, 1, 2});
private static final String UUID_STRING =
"550e8400-e29b-41d4-a716-446655440000";
+ private static final UUID UUID_VALUE = UUID.fromString(UUID_STRING);
private static final byte[] UUID_BYTES = uuidBytes(UUID_STRING);
private static byte[] uuidBytes(String uuid) {
@@ -1864,6 +1865,12 @@ class CastRulesTest {
Variant.newBuilder()
.of(Instant.parse("2021-09-24T12:34:56.123456789Z")),
fromString("2021-09-24 14:34:56.123456789"))
+ // a UUID renders in its canonical lower-case
8-4-4-4-12 form, the same as
+ // a regular UUID to string cast
+ .fromCase(
+ VARIANT(),
+ Variant.newBuilder().of(UUID_VALUE),
+ fromString(UUID_STRING))
// a binary value is read as UTF-8, like a regular
BINARY to string cast
.fromCase(
VARIANT(),
@@ -2034,6 +2041,10 @@ class CastRulesTest {
// them, which is what makes this the way to inspect
such a value
.fromCase(VARIANT(), VARIANT_BUILDER.of(INVALID_UTF8),
INVALID_UTF8)
.fail(VARIANT(), VARIANT_BUILDER.of("foo"),
TableRuntimeException.class),
+ CastTestSpecBuilder.testCastTo(UUID())
+ .fromCase(VARIANT(), null, null)
+ .fromCase(VARIANT(),
Variant.newBuilder().of(UUID_VALUE), UUID_BYTES)
+ .fail(VARIANT(), VARIANT_BUILDER.of("foo"),
TableRuntimeException.class),
CastTestSpecBuilder.testCastTo(DATE())
.fromCase(
VARIANT(),
diff --git
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/UuidCastUtils.java
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/UuidCastUtils.java
index 79a4921caca..3cd5ce75dee 100644
---
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/UuidCastUtils.java
+++
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/UuidCastUtils.java
@@ -21,9 +21,7 @@ package org.apache.flink.table.runtime.functions;
import org.apache.flink.annotation.Internal;
import org.apache.flink.table.api.TableRuntimeException;
import org.apache.flink.table.types.logical.UuidType;
-
-import java.nio.ByteBuffer;
-import java.util.UUID;
+import org.apache.flink.table.types.logical.utils.UuidUtils;
/**
* Runtime helpers for casting to and from a {@code UUID} value.
@@ -42,8 +40,7 @@ public final class UuidCastUtils {
* the 16-byte big-endian encoding, so this never fails.
*/
public static String toStringValue(byte[] uuidBytes) {
- final ByteBuffer buffer = ByteBuffer.wrap(uuidBytes);
- return new UUID(buffer.getLong(), buffer.getLong()).toString();
+ return UuidUtils.fromBytes(uuidBytes).toString();
}
/**
diff --git
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java
index 561da100cee..0405575aaed 100644
---
a/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java
+++
b/flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java
@@ -25,6 +25,7 @@ import org.apache.flink.table.data.TimestampData;
import org.apache.flink.table.data.binary.BinaryStringData;
import org.apache.flink.table.data.binary.BinaryStringDataUtil;
import org.apache.flink.table.data.binary.StringUtf8Utils;
+import org.apache.flink.table.types.logical.utils.UuidUtils;
import org.apache.flink.table.utils.DateTimeUtils;
import org.apache.flink.types.variant.Variant;
@@ -271,6 +272,17 @@ public final class VariantCastUtils {
throw unsupportedKind(variant, String.format("TIME(%d)", precision));
}
+ /**
+ * Reads a UUID variant as the target {@code UUID}, encoded as the
canonical 16-byte big-endian
+ * representation.
+ */
+ public static byte[] toUuid(Variant variant) {
+ if (variant.getType() == Variant.Type.UUID) {
+ return UuidUtils.toBytes(variant.getUuid());
+ }
+ throw unsupportedKind(variant, "UUID");
+ }
+
/**
* Reads a binary variant as the target binary type. {@code BINARY} is
fixed width, so a shorter
* value is padded with zero bytes, and either target truncates a value
longer than {@code
@@ -433,6 +445,9 @@ public final class VariantCastUtils {
sessionZone,
TIMESTAMP_NANOS_PRECISION);
break;
+ case UUID:
+ value = variant.getUuid().toString();
+ break;
case NULL:
// Only reachable for a NOT NULL target. A nullable target
maps a null-valued
// variant to SQL NULL before this method is called.
diff --git
a/flink-table/flink-table-type-utils/src/main/java/org/apache/flink/table/data/conversion/UuidUuidConverter.java
b/flink-table/flink-table-type-utils/src/main/java/org/apache/flink/table/data/conversion/UuidUuidConverter.java
index a3f97f9da6c..efb04d0228a 100644
---
a/flink-table/flink-table-type-utils/src/main/java/org/apache/flink/table/data/conversion/UuidUuidConverter.java
+++
b/flink-table/flink-table-type-utils/src/main/java/org/apache/flink/table/data/conversion/UuidUuidConverter.java
@@ -20,8 +20,8 @@ package org.apache.flink.table.data.conversion;
import org.apache.flink.annotation.Internal;
import org.apache.flink.table.types.logical.UuidType;
+import org.apache.flink.table.types.logical.utils.UuidUtils;
-import java.nio.ByteBuffer;
import java.util.UUID;
/**
@@ -37,17 +37,11 @@ public class UuidUuidConverter implements
DataStructureConverter<byte[], UUID> {
@Override
public byte[] toInternal(UUID external) {
- return ByteBuffer.allocate(16)
- .putLong(external.getMostSignificantBits())
- .putLong(external.getLeastSignificantBits())
- .array();
+ return UuidUtils.toBytes(external);
}
@Override
public UUID toExternal(byte[] internal) {
- final ByteBuffer buffer = ByteBuffer.wrap(internal);
- final long mostSignificantBits = buffer.getLong();
- final long leastSignificantBits = buffer.getLong();
- return new UUID(mostSignificantBits, leastSignificantBits);
+ return UuidUtils.fromBytes(internal);
}
}