AHeise commented on code in PR #29311:
URL: https://github.com/apache/flink/pull/29311#discussion_r4152829355


##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/PrimitiveToVariantCastRule.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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.planner.functions.casting;
+
+import org.apache.flink.table.runtime.functions.VariantCastUtils;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.LogicalTypeRoot;
+import org.apache.flink.table.types.logical.utils.LogicalTypeChecks;
+import org.apache.flink.types.variant.Variant;
+
+import static 
org.apache.flink.table.planner.functions.casting.CastRuleUtils.staticCall;
+
+/**
+ * Primitive type to {@link LogicalTypeRoot#VARIANT} cast rule.
+ *
+ * <p>The value keeps the kind of its SQL type, so a {@code BIGINT} is stored 
as a {@code BIGINT}
+ * even when it would fit a smaller one. Unlike {@code PARSE_JSON}, a {@code 
NaN} or infinite {@code
+ * FLOAT} or {@code DOUBLE} is accepted, since a {@code VARIANT} is not 
limited to what JSON can
+ * express. A timestamp keeps its declared precision, so a {@code 
TIMESTAMP(p)} with {@code p} above
+ * 6 is stored with nanoseconds. That kind only covers 1677-09-21 to 
2262-04-11, so such a cast
+ * fails for a value outside that range. Every other cast never fails.
+ */
+class PrimitiveToVariantCastRule extends 
AbstractExpressionCodeGeneratorCastRule<Object, Variant> {
+
+    static final PrimitiveToVariantCastRule INSTANCE = new 
PrimitiveToVariantCastRule();
+
+    /** The highest precision a VARIANT stores with microseconds, above it 
nanoseconds are used. */
+    private static final int MAX_MICROS_PRECISION = 6;

Review Comment:
   Thanks.



##########
flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java:
##########
@@ -540,6 +551,111 @@ private static Number numeric(Variant variant, String 
targetType) {
         }
     }
 
+    public static Variant fromBoolean(boolean value) {
+        return BUILDER.of(value);
+    }
+
+    /**
+     * Stores an integer in the integer kind of its SQL type, so a {@code 
BIGINT} stays a {@code
+     * BIGINT} whatever its value. The generated code passes the primitive of 
the source type, which
+     * selects the overload.
+     */
+    public static Variant fromIntegral(byte value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromIntegral(short value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromIntegral(int value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromIntegral(long value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromFloat(float value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromDouble(double value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromDecimal(DecimalData value) {
+        return BUILDER.of(value.toBigDecimal());
+    }
+
+    public static Variant fromString(StringData value) {

Review Comment:
   Good point about invalid UTF-8. Follow-up is fine.



##########
flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java:
##########
@@ -540,6 +551,111 @@ private static Number numeric(Variant variant, String 
targetType) {
         }
     }
 
+    public static Variant fromBoolean(boolean value) {
+        return BUILDER.of(value);
+    }
+
+    /**
+     * Stores an integer in the integer kind of its SQL type, so a {@code 
BIGINT} stays a {@code
+     * BIGINT} whatever its value. The generated code passes the primitive of 
the source type, which
+     * selects the overload.
+     */
+    public static Variant fromIntegral(byte value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromIntegral(short value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromIntegral(int value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromIntegral(long value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromFloat(float value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromDouble(double value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromDecimal(DecimalData value) {
+        return BUILDER.of(value.toBigDecimal());
+    }
+
+    public static Variant fromString(StringData value) {
+        return BUILDER.of(value.toString());
+    }
+
+    public static Variant fromBytes(byte[] value) {
+        return BUILDER.of(value);
+    }
+
+    public static Variant fromDate(int epochDay) {
+        return BUILDER.of(LocalDate.ofEpochDay(epochDay));
+    }
+
+    public static Variant fromTime(int millisOfDay) {
+        return BUILDER.of(LocalTime.ofNanoOfDay(millisOfDay * 1_000_000L));
+    }
+
+    /**
+     * Stores a timestamp in the kind its declared precision needs: 
microseconds up to {@code
+     * TIMESTAMP(6)} and nanoseconds above, whatever the digits of the value. 
A nanosecond kind only
+     * covers 1677-09-21 to 2262-04-11, so a {@code TIMESTAMP(7)} to {@code 
TIMESTAMP(9)} value
+     * outside that range fails.
+     */
+    public static Variant fromTimestamp(TimestampData value, int precision) {
+        final BinaryVariantInternalBuilder builder = new 
BinaryVariantInternalBuilder(false);
+        if (precision <= TIMESTAMP_PRECISION) {
+            builder.appendTimestamp(timestampMicros(value));
+        } else {
+            builder.appendTimestampNanos(timestampNanos(value, "TIMESTAMP(" + 
precision + ")"));
+        }
+        return builder.build();
+    }
+
+    /** Like {@link #fromTimestamp(TimestampData, int)}, for {@code 
TIMESTAMP_LTZ}. */
+    public static Variant fromTimestampLtz(TimestampData value, int precision) 
{
+        final BinaryVariantInternalBuilder builder = new 
BinaryVariantInternalBuilder(false);
+        if (precision <= TIMESTAMP_PRECISION) {
+            builder.appendTimestampLtz(timestampMicros(value));
+        } else {
+            builder.appendTimestampLtzNanos(
+                    timestampNanos(value, "TIMESTAMP_LTZ(" + precision + ")"));
+        }
+        return builder.build();
+    }
+
+    /** Microseconds since the epoch, which cover every year a {@link 
TimestampData} can hold. */
+    private static long timestampMicros(TimestampData value) {

Review Comment:
   Thanks.



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java:
##########
@@ -432,6 +435,201 @@ 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")

Review Comment:
   Thanks, good to have the padding pinned.



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/PrimitiveToVariantCastRule.java:
##########
@@ -0,0 +1,149 @@
+/*
+ * 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.planner.functions.casting;
+
+import org.apache.flink.table.runtime.functions.VariantCastUtils;
+import org.apache.flink.table.types.logical.LogicalType;
+import org.apache.flink.table.types.logical.LogicalTypeRoot;
+import org.apache.flink.table.types.logical.utils.LogicalTypeChecks;
+import org.apache.flink.types.variant.Variant;
+
+import static 
org.apache.flink.table.planner.functions.casting.CastRuleUtils.staticCall;
+
+/**

Review Comment:
   Thanks.



-- 
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]

Reply via email to