twalthr commented on code in PR #28758:
URL: https://github.com/apache/flink/pull/28758#discussion_r3622420298


##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,62 @@ close to the semantics of JSON. Compared to `ROW` and 
`STRUCTURED` type, `VARIAN
 flexibility to support highly nested and evolving schema.
 
 `VARIANT` allows for deeply nested data structures, such as arrays within 
arrays, maps within maps,
-or combinations of both.This capability makes `VARIANT` ideal for scenarios 
where data complexity
+or combinations of both. This capability makes `VARIANT` ideal for scenarios 
where data complexity
 and nesting are significant.
 
 `VARIANT` allows schema evolution, enabling the storage of data with changing 
or unknown schemas
 without requiring upfront schema definition. For example, if a new field is 
added to the data, it
 can be directly incorporated into the `VARIANT` data without modifying the 
table schema. This is
 particularly useful in dynamic environments where schemas may evolve over time.
 
+A `VARIANT` stores a single value of one of the following kinds: `NULL`, 
`BOOLEAN`, `TINYINT`,
+`SMALLINT`, `INT`, `BIGINT`, `FLOAT`, `DOUBLE`, `DECIMAL` (up to precision 
38), `STRING`, `DATE`,
+`TIMESTAMP`, `TIMESTAMP_LTZ`, `BYTES`, or a nested array or object. 
`TIMESTAMP` and `TIMESTAMP_LTZ`
+are stored with microsecond precision and `DATE` as a day count. There is no 
`TIME` kind.
+
+The `PARSE_JSON` function produces only the kinds that JSON syntax can express:
+
+| JSON input                                          | Stored `VARIANT` kind  
                         |
+|-----------------------------------------------------|-------------------------------------------------|
+| `null`                                              | `NULL`                 
                         |
+| `true` / `false`                                    | `BOOLEAN`              
                         |
+| Integer within the 64-bit signed range              | smallest of 
`TINYINT`/`SMALLINT`/`INT`/`BIGINT` |
+| Integer beyond 64-bit, up to 38 significant digits  | `DECIMAL`              
                         |
+| Decimal in plain notation, up to precision/scale 38 | `DECIMAL`              
                         |
+| Number in scientific notation, e.g. `1.5e3`         | `DOUBLE`               
                         |
+| Number exceeding 38 digits of precision or scale    | `DOUBLE`               
                         |
+| String                                              | `STRING`               
                         |
+| Array                                               | array (elements 
encoded by the same rules)      |
+| Object                                              | object (values encoded 
by the same rules)       |
+
+Because JSON has no literal for float, date, timestamp, or binary values, 
`PARSE_JSON` never
+produces `FLOAT`, `DATE`, `TIMESTAMP`, `TIMESTAMP_LTZ`, or `BYTES`. A 
`VARIANT` can still hold
+those kinds when built by other producers, such as the programmatic 
`VariantBuilder` API or format
+conversions like Avro.
+
+The JSON specification has no `NaN` or infinity literals, so 
`PARSE_JSON('NaN')`,
+`PARSE_JSON('Infinity')`, and `PARSE_JSON('-Infinity')` fail, and 
`TRY_PARSE_JSON` returns `NULL`.
+A `VARIANT` has no dedicated kind for these values. To keep one, store it as a 
JSON string and cast
+it back out, for example `CAST(CAST(PARSE_JSON('"Infinity"') AS STRING) AS 
FLOAT)`.
+
 A primitive-valued `VARIANT` can be converted to a scalar type with `CAST` or 
`TRY_CAST`. Numeric
-targets are lenient: a variant holding any numeric value casts to any numeric 
type, so a JSON integer
-such as `PARSE_JSON('42')` casts to `INT` or `BIGINT`. Other targets require 
the stored value to be of
-the matching kind. When the value cannot be converted, `CAST` fails the job 
and `TRY_CAST` returns
-`NULL`. Use the `JSON_STRING` function to obtain the JSON string 
representation of a `VARIANT`.
+targets accept any numeric value, so `PARSE_JSON('42')` casts to `INT`, 
`BIGINT`, or `DOUBLE`. A
+value outside an integer or `DECIMAL` target's range fails `CAST` and returns 
`NULL` for
+`TRY_CAST`. Other targets require the stored value to be of the matching kind, 
and a mismatch is
+handled the same way. Casting a `VARIANT` to `CHAR`/`VARCHAR` extracts the 
scalar value, so a
+stored string is returned unquoted (for example `foo`), while objects and 
arrays return their JSON

Review Comment:
   did you double check this behavior with other vendors? sounds incorrect to 
me.
   ```
   while objects and arrays return their JSON
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,62 @@ close to the semantics of JSON. Compared to `ROW` and 
`STRUCTURED` type, `VARIAN
 flexibility to support highly nested and evolving schema.
 
 `VARIANT` allows for deeply nested data structures, such as arrays within 
arrays, maps within maps,
-or combinations of both.This capability makes `VARIANT` ideal for scenarios 
where data complexity
+or combinations of both. This capability makes `VARIANT` ideal for scenarios 
where data complexity
 and nesting are significant.
 
 `VARIANT` allows schema evolution, enabling the storage of data with changing 
or unknown schemas
 without requiring upfront schema definition. For example, if a new field is 
added to the data, it
 can be directly incorporated into the `VARIANT` data without modifying the 
table schema. This is
 particularly useful in dynamic environments where schemas may evolve over time.
 
+A `VARIANT` stores a single value of one of the following kinds: `NULL`, 
`BOOLEAN`, `TINYINT`,
+`SMALLINT`, `INT`, `BIGINT`, `FLOAT`, `DOUBLE`, `DECIMAL` (up to precision 
38), `STRING`, `DATE`,
+`TIMESTAMP`, `TIMESTAMP_LTZ`, `BYTES`, or a nested array or object. 
`TIMESTAMP` and `TIMESTAMP_LTZ`
+are stored with microsecond precision and `DATE` as a day count. There is no 
`TIME` kind.
+
+The `PARSE_JSON` function produces only the kinds that JSON syntax can express:
+
+| JSON input                                          | Stored `VARIANT` kind  
                         |
+|-----------------------------------------------------|-------------------------------------------------|
+| `null`                                              | `NULL`                 
                         |
+| `true` / `false`                                    | `BOOLEAN`              
                         |
+| Integer within the 64-bit signed range              | smallest of 
`TINYINT`/`SMALLINT`/`INT`/`BIGINT` |
+| Integer beyond 64-bit, up to 38 significant digits  | `DECIMAL`              
                         |
+| Decimal in plain notation, up to precision/scale 38 | `DECIMAL`              
                         |
+| Number in scientific notation, e.g. `1.5e3`         | `DOUBLE`               
                         |
+| Number exceeding 38 digits of precision or scale    | `DOUBLE`               
                         |
+| String                                              | `STRING`               
                         |
+| Array                                               | array (elements 
encoded by the same rules)      |
+| Object                                              | object (values encoded 
by the same rules)       |
+
+Because JSON has no literal for float, date, timestamp, or binary values, 
`PARSE_JSON` never
+produces `FLOAT`, `DATE`, `TIMESTAMP`, `TIMESTAMP_LTZ`, or `BYTES`. A 
`VARIANT` can still hold
+those kinds when built by other producers, such as the programmatic 
`VariantBuilder` API or format
+conversions like Avro.
+
+The JSON specification has no `NaN` or infinity literals, so 
`PARSE_JSON('NaN')`,
+`PARSE_JSON('Infinity')`, and `PARSE_JSON('-Infinity')` fail, and 
`TRY_PARSE_JSON` returns `NULL`.
+A `VARIANT` has no dedicated kind for these values. To keep one, store it as a 
JSON string and cast
+it back out, for example `CAST(CAST(PARSE_JSON('"Infinity"') AS STRING) AS 
FLOAT)`.
+
 A primitive-valued `VARIANT` can be converted to a scalar type with `CAST` or 
`TRY_CAST`. Numeric
-targets are lenient: a variant holding any numeric value casts to any numeric 
type, so a JSON integer
-such as `PARSE_JSON('42')` casts to `INT` or `BIGINT`. Other targets require 
the stored value to be of
-the matching kind. When the value cannot be converted, `CAST` fails the job 
and `TRY_CAST` returns
-`NULL`. Use the `JSON_STRING` function to obtain the JSON string 
representation of a `VARIANT`.
+targets accept any numeric value, so `PARSE_JSON('42')` casts to `INT`, 
`BIGINT`, or `DOUBLE`. A
+value outside an integer or `DECIMAL` target's range fails `CAST` and returns 
`NULL` for
+`TRY_CAST`. Other targets require the stored value to be of the matching kind, 
and a mismatch is
+handled the same way. Casting a `VARIANT` to `CHAR`/`VARCHAR` extracts the 
scalar value, so a
+stored string is returned unquoted (for example `foo`), while objects and 
arrays return their JSON
+representation. Use `JSON_STRING` for the JSON representation, where a string 
stays quoted (for
+example `"foo"`). Casting to `TIME` is not supported.
+
+{{< hint info >}}
+Unlike a regular numeric cast, casting a numeric `VARIANT` never wraps on 
overflow. To narrow a 

Review Comment:
   ```suggestion
   Unlike a regular numeric cast, casting a numeric `VARIANT` never overflows. 
To narrow a 
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,62 @@ close to the semantics of JSON. Compared to `ROW` and 
`STRUCTURED` type, `VARIAN
 flexibility to support highly nested and evolving schema.
 
 `VARIANT` allows for deeply nested data structures, such as arrays within 
arrays, maps within maps,
-or combinations of both.This capability makes `VARIANT` ideal for scenarios 
where data complexity
+or combinations of both. This capability makes `VARIANT` ideal for scenarios 
where data complexity
 and nesting are significant.
 
 `VARIANT` allows schema evolution, enabling the storage of data with changing 
or unknown schemas
 without requiring upfront schema definition. For example, if a new field is 
added to the data, it
 can be directly incorporated into the `VARIANT` data without modifying the 
table schema. This is
 particularly useful in dynamic environments where schemas may evolve over time.
 
+A `VARIANT` stores a single value of one of the following kinds: `NULL`, 
`BOOLEAN`, `TINYINT`,
+`SMALLINT`, `INT`, `BIGINT`, `FLOAT`, `DOUBLE`, `DECIMAL` (up to precision 
38), `STRING`, `DATE`,
+`TIMESTAMP`, `TIMESTAMP_LTZ`, `BYTES`, or a nested array or object. 
`TIMESTAMP` and `TIMESTAMP_LTZ`
+are stored with microsecond precision and `DATE` as a day count. There is no 
`TIME` kind.
+
+The `PARSE_JSON` function produces only the kinds that JSON syntax can express:
+
+| JSON input                                          | Stored `VARIANT` kind  
                         |
+|-----------------------------------------------------|-------------------------------------------------|
+| `null`                                              | `NULL`                 
                         |
+| `true` / `false`                                    | `BOOLEAN`              
                         |
+| Integer within the 64-bit signed range              | smallest of 
`TINYINT`/`SMALLINT`/`INT`/`BIGINT` |
+| Integer beyond 64-bit, up to 38 significant digits  | `DECIMAL`              
                         |
+| Decimal in plain notation, up to precision/scale 38 | `DECIMAL`              
                         |
+| Number in scientific notation, e.g. `1.5e3`         | `DOUBLE`               
                         |
+| Number exceeding 38 digits of precision or scale    | `DOUBLE`               
                         |
+| String                                              | `STRING`               
                         |
+| Array                                               | array (elements 
encoded by the same rules)      |
+| Object                                              | object (values encoded 
by the same rules)       |
+
+Because JSON has no literal for float, date, timestamp, or binary values, 
`PARSE_JSON` never
+produces `FLOAT`, `DATE`, `TIMESTAMP`, `TIMESTAMP_LTZ`, or `BYTES`. A 
`VARIANT` can still hold
+those kinds when built by other producers, such as the programmatic 
`VariantBuilder` API or format
+conversions like Avro.
+
+The JSON specification has no `NaN` or infinity literals, so 
`PARSE_JSON('NaN')`,
+`PARSE_JSON('Infinity')`, and `PARSE_JSON('-Infinity')` fail, and 
`TRY_PARSE_JSON` returns `NULL`.
+A `VARIANT` has no dedicated kind for these values. To keep one, store it as a 
JSON string and cast
+it back out, for example `CAST(CAST(PARSE_JSON('"Infinity"') AS STRING) AS 
FLOAT)`.
+
 A primitive-valued `VARIANT` can be converted to a scalar type with `CAST` or 
`TRY_CAST`. Numeric
-targets are lenient: a variant holding any numeric value casts to any numeric 
type, so a JSON integer
-such as `PARSE_JSON('42')` casts to `INT` or `BIGINT`. Other targets require 
the stored value to be of
-the matching kind. When the value cannot be converted, `CAST` fails the job 
and `TRY_CAST` returns
-`NULL`. Use the `JSON_STRING` function to obtain the JSON string 
representation of a `VARIANT`.
+targets accept any numeric value, so `PARSE_JSON('42')` casts to `INT`, 
`BIGINT`, or `DOUBLE`. A
+value outside an integer or `DECIMAL` target's range fails `CAST` and returns 
`NULL` for
+`TRY_CAST`. Other targets require the stored value to be of the matching kind, 
and a mismatch is
+handled the same way. Casting a `VARIANT` to `CHAR`/`VARCHAR` extracts the 
scalar value, so a
+stored string is returned unquoted (for example `foo`), while objects and 
arrays return their JSON
+representation. Use `JSON_STRING` for the JSON representation, where a string 
stays quoted (for
+example `"foo"`). Casting to `TIME` is not supported.

Review Comment:
   incomplete list anyway.
   ```suggestion
   example `"foo"`).
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,62 @@ close to the semantics of JSON. Compared to `ROW` and 
`STRUCTURED` type, `VARIAN
 flexibility to support highly nested and evolving schema.
 
 `VARIANT` allows for deeply nested data structures, such as arrays within 
arrays, maps within maps,
-or combinations of both.This capability makes `VARIANT` ideal for scenarios 
where data complexity
+or combinations of both. This capability makes `VARIANT` ideal for scenarios 
where data complexity
 and nesting are significant.
 
 `VARIANT` allows schema evolution, enabling the storage of data with changing 
or unknown schemas
 without requiring upfront schema definition. For example, if a new field is 
added to the data, it
 can be directly incorporated into the `VARIANT` data without modifying the 
table schema. This is
 particularly useful in dynamic environments where schemas may evolve over time.
 
+A `VARIANT` stores a single value of one of the following kinds: `NULL`, 
`BOOLEAN`, `TINYINT`,
+`SMALLINT`, `INT`, `BIGINT`, `FLOAT`, `DOUBLE`, `DECIMAL` (up to precision 
38), `STRING`, `DATE`,
+`TIMESTAMP`, `TIMESTAMP_LTZ`, `BYTES`, or a nested array or object. 
`TIMESTAMP` and `TIMESTAMP_LTZ`
+are stored with microsecond precision and `DATE` as a day count. There is no 
`TIME` kind.
+
+The `PARSE_JSON` function produces only the kinds that JSON syntax can express:
+
+| JSON input                                          | Stored `VARIANT` kind  
                         |
+|-----------------------------------------------------|-------------------------------------------------|
+| `null`                                              | `NULL`                 
                         |
+| `true` / `false`                                    | `BOOLEAN`              
                         |
+| Integer within the 64-bit signed range              | smallest of 
`TINYINT`/`SMALLINT`/`INT`/`BIGINT` |
+| Integer beyond 64-bit, up to 38 significant digits  | `DECIMAL`              
                         |
+| Decimal in plain notation, up to precision/scale 38 | `DECIMAL`              
                         |
+| Number in scientific notation, e.g. `1.5e3`         | `DOUBLE`               
                         |
+| Number exceeding 38 digits of precision or scale    | `DOUBLE`               
                         |
+| String                                              | `STRING`               
                         |
+| Array                                               | array (elements 
encoded by the same rules)      |
+| Object                                              | object (values encoded 
by the same rules)       |
+
+Because JSON has no literal for float, date, timestamp, or binary values, 
`PARSE_JSON` never
+produces `FLOAT`, `DATE`, `TIMESTAMP`, `TIMESTAMP_LTZ`, or `BYTES`. A 
`VARIANT` can still hold
+those kinds when built by other producers, such as the programmatic 
`VariantBuilder` API or format
+conversions like Avro.
+
+The JSON specification has no `NaN` or infinity literals, so 
`PARSE_JSON('NaN')`,
+`PARSE_JSON('Infinity')`, and `PARSE_JSON('-Infinity')` fail, and 
`TRY_PARSE_JSON` returns `NULL`.
+A `VARIANT` has no dedicated kind for these values. To keep one, store it as a 
JSON string and cast
+it back out, for example `CAST(CAST(PARSE_JSON('"Infinity"') AS STRING) AS 
FLOAT)`.
+
 A primitive-valued `VARIANT` can be converted to a scalar type with `CAST` or 
`TRY_CAST`. Numeric
-targets are lenient: a variant holding any numeric value casts to any numeric 
type, so a JSON integer
-such as `PARSE_JSON('42')` casts to `INT` or `BIGINT`. Other targets require 
the stored value to be of
-the matching kind. When the value cannot be converted, `CAST` fails the job 
and `TRY_CAST` returns
-`NULL`. Use the `JSON_STRING` function to obtain the JSON string 
representation of a `VARIANT`.
+targets accept any numeric value, so `PARSE_JSON('42')` casts to `INT`, 
`BIGINT`, or `DOUBLE`. A
+value outside an integer or `DECIMAL` target's range fails `CAST` and returns 
`NULL` for
+`TRY_CAST`. Other targets require the stored value to be of the matching kind, 
and a mismatch is
+handled the same way. Casting a `VARIANT` to `CHAR`/`VARCHAR` extracts the 
scalar value, so a
+stored string is returned unquoted (for example `foo`), while objects and 
arrays return their JSON
+representation. Use `JSON_STRING` for the JSON representation, where a string 
stays quoted (for
+example `"foo"`). Casting to `TIME` is not supported.
+
+{{< hint info >}}
+Unlike a regular numeric cast, casting a numeric `VARIANT` never wraps on 
overflow. To narrow a 
+`VARIANT` with wrap-around semantics, first cast it to a type that fits the 
stored value, then apply a 

Review Comment:
   ```suggestion
   `VARIANT`, first cast it to a type that fits the stored value, then apply a 
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,62 @@ close to the semantics of JSON. Compared to `ROW` and 
`STRUCTURED` type, `VARIAN
 flexibility to support highly nested and evolving schema.
 
 `VARIANT` allows for deeply nested data structures, such as arrays within 
arrays, maps within maps,
-or combinations of both.This capability makes `VARIANT` ideal for scenarios 
where data complexity
+or combinations of both. This capability makes `VARIANT` ideal for scenarios 
where data complexity
 and nesting are significant.
 
 `VARIANT` allows schema evolution, enabling the storage of data with changing 
or unknown schemas
 without requiring upfront schema definition. For example, if a new field is 
added to the data, it
 can be directly incorporated into the `VARIANT` data without modifying the 
table schema. This is
 particularly useful in dynamic environments where schemas may evolve over time.
 
+A `VARIANT` stores a single value of one of the following kinds: `NULL`, 
`BOOLEAN`, `TINYINT`,
+`SMALLINT`, `INT`, `BIGINT`, `FLOAT`, `DOUBLE`, `DECIMAL` (up to precision 
38), `STRING`, `DATE`,
+`TIMESTAMP`, `TIMESTAMP_LTZ`, `BYTES`, or a nested array or object. 
`TIMESTAMP` and `TIMESTAMP_LTZ`
+are stored with microsecond precision and `DATE` as a day count. There is no 
`TIME` kind.
+
+The `PARSE_JSON` function produces only the kinds that JSON syntax can express:
+
+| JSON input                                          | Stored `VARIANT` kind  
                         |
+|-----------------------------------------------------|-------------------------------------------------|
+| `null`                                              | `NULL`                 
                         |
+| `true` / `false`                                    | `BOOLEAN`              
                         |
+| Integer within the 64-bit signed range              | smallest of 
`TINYINT`/`SMALLINT`/`INT`/`BIGINT` |
+| Integer beyond 64-bit, up to 38 significant digits  | `DECIMAL`              
                         |
+| Decimal in plain notation, up to precision/scale 38 | `DECIMAL`              
                         |
+| Number in scientific notation, e.g. `1.5e3`         | `DOUBLE`               
                         |
+| Number exceeding 38 digits of precision or scale    | `DOUBLE`               
                         |
+| String                                              | `STRING`               
                         |
+| Array                                               | array (elements 
encoded by the same rules)      |
+| Object                                              | object (values encoded 
by the same rules)       |
+
+Because JSON has no literal for float, date, timestamp, or binary values, 
`PARSE_JSON` never
+produces `FLOAT`, `DATE`, `TIMESTAMP`, `TIMESTAMP_LTZ`, or `BYTES`. A 
`VARIANT` can still hold
+those kinds when built by other producers, such as the programmatic 
`VariantBuilder` API or format
+conversions like Avro.
+
+The JSON specification has no `NaN` or infinity literals, so 
`PARSE_JSON('NaN')`,
+`PARSE_JSON('Infinity')`, and `PARSE_JSON('-Infinity')` fail, and 
`TRY_PARSE_JSON` returns `NULL`.
+A `VARIANT` has no dedicated kind for these values. To keep one, store it as a 
JSON string and cast
+it back out, for example `CAST(CAST(PARSE_JSON('"Infinity"') AS STRING) AS 
FLOAT)`.
+
 A primitive-valued `VARIANT` can be converted to a scalar type with `CAST` or 
`TRY_CAST`. Numeric
-targets are lenient: a variant holding any numeric value casts to any numeric 
type, so a JSON integer
-such as `PARSE_JSON('42')` casts to `INT` or `BIGINT`. Other targets require 
the stored value to be of
-the matching kind. When the value cannot be converted, `CAST` fails the job 
and `TRY_CAST` returns
-`NULL`. Use the `JSON_STRING` function to obtain the JSON string 
representation of a `VARIANT`.
+targets accept any numeric value, so `PARSE_JSON('42')` casts to `INT`, 
`BIGINT`, or `DOUBLE`. A
+value outside an integer or `DECIMAL` target's range fails `CAST` and returns 
`NULL` for
+`TRY_CAST`. Other targets require the stored value to be of the matching kind, 
and a mismatch is
+handled the same way. Casting a `VARIANT` to `CHAR`/`VARCHAR` extracts the 
scalar value, so a
+stored string is returned unquoted (for example `foo`), while objects and 
arrays return their JSON
+representation. Use `JSON_STRING` for the JSON representation, where a string 
stays quoted (for
+example `"foo"`). Casting to `TIME` is not supported.
+
+{{< hint info >}}
+Unlike a regular numeric cast, casting a numeric `VARIANT` never wraps on 
overflow. To narrow a 
+`VARIANT` with wrap-around semantics, first cast it to a type that fits the 
stored value, then apply a 
+regular narrowing cast:
+
+```sql
+CAST(CAST(PARSE_JSON('1000') AS INT) AS TINYINT) -- returns -24 (wrap-around)

Review Comment:
   ```suggestion
   CAST(CAST(PARSE_JSON('1000') AS INT) AS TINYINT) -- returns -24 (after 
overflow)
   ```



##########
flink-table/flink-table-common/src/test/java/org/apache/flink/table/types/LogicalTypeCastsTest.java:
##########
@@ -284,11 +285,12 @@ private static Stream<Arguments> testData() {
                         new VarBinaryType(VarBinaryType.MAX_LENGTH),
                         false,
                         true),
+                Arguments.of(new VariantType(), new CharType(), false, true),
+                Arguments.of(new VariantType(), VarCharType.STRING_TYPE, 
false, true),
                 // variant identity cast is implicit
                 Arguments.of(new VariantType(), new VariantType(), true, true),
                 // TIME, character strings and constructed targets are not 
castable from variant

Review Comment:
   ```suggestion
                   // TIME and constructed targets are not castable from variant
   ```



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToPrimitiveCastRule.java:
##########
@@ -166,38 +164,35 @@ protected String generateCodeBlockInternal(
     }
 
     /**
-     * Converts a numeric variant to the numeric {@code target} via the 
matching {@link Number}
-     * accessor, mirroring regular numeric cast semantics. A non-numeric 
variant raises {@link
-     * ClassCastException}, failing {@code CAST} and yielding {@code null} for 
{@code TRY_CAST}.
+     * Converts a numeric variant to the numeric {@code target}. Integer and 
decimal targets are
+     * range-checked so that an out-of-range value fails {@code CAST} and 
yields {@code null} for
+     * {@code TRY_CAST}, following Spark's variant cast semantics; {@code 
FLOAT} and {@code DOUBLE}

Review Comment:
   not sure if we follow 100%, better remove
   ```suggestion
        * {@code TRY_CAST}; {@code FLOAT} and {@code DOUBLE}
   ```



##########
flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java:
##########
@@ -0,0 +1,114 @@
+/*
+ * 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.runtime.functions;
+
+import org.apache.flink.annotation.Internal;
+import org.apache.flink.table.data.DecimalData;
+import org.apache.flink.types.variant.Variant;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * Runtime helpers for casting a {@code VARIANT} value to a SQL type.
+ *
+ * <p>Numeric targets are range-checked and raise {@link ArithmeticException} 
on overflow instead of
+ * wrapping. Casting to a string extracts the scalar value (a string stays 
unquoted), while objects
+ * and arrays use their JSON representation.
+ */
+@Internal
+public final class VariantCastUtils {
+
+    private VariantCastUtils() {}
+
+    public static byte toByteExact(Number value) {
+        return (byte) toIntegralExact(value, Byte.MIN_VALUE, Byte.MAX_VALUE, 
"TINYINT");
+    }
+
+    public static short toShortExact(Number value) {
+        return (short) toIntegralExact(value, Short.MIN_VALUE, 
Short.MAX_VALUE, "SMALLINT");
+    }
+
+    public static int toIntExact(Number value) {
+        return (int) toIntegralExact(value, Integer.MIN_VALUE, 
Integer.MAX_VALUE, "INTEGER");
+    }
+
+    public static long toLongExact(Number value) {
+        return toIntegralExact(value, Long.MIN_VALUE, Long.MAX_VALUE, 
"BIGINT");
+    }
+
+    public static DecimalData toDecimalExact(Number value, int precision, int 
scale) {
+        final DecimalData decimal =
+                DecimalData.fromBigDecimal(toBigDecimal(value), precision, 
scale);
+        if (decimal == null) {
+            throw new ArithmeticException(

Review Comment:
   is it guaranteed that this becomes a `TableRuntimeException` eventually? or 
how do we treat other cast cases currently?



##########
flink-table/flink-table-planner/src/main/java/org/apache/flink/table/planner/functions/casting/VariantToStringCastRule.java:
##########
@@ -45,6 +51,6 @@ public String generateStringExpression(
             String inputTerm,
             LogicalType inputLogicalType,
             LogicalType targetLogicalType) {
-        return methodCall(inputTerm, "toString");
+        return staticCall(VariantCastUtils.class, "toStringValue", inputTerm);

Review Comment:
   according to LogicalTypeCasts, we support VARCHAR and CHAR both with various 
length. this cast rule should consider the target length as well. I guess it 
should throw if length exceeds?



##########
flink-core/src/main/java/org/apache/flink/types/variant/Variant.java:
##########
@@ -127,18 +127,19 @@ public interface Variant {
 
     /**
      * Get the scalar value of variant as LocalDateTime, if the variant type 
is {@link
-     * Type#TIMESTAMP}.
+     * Type#TIMESTAMP}. The returned value has microsecond precision.
      *
      * @throws VariantTypeException If this variant is not a scalar value or 
is not {@link
      *     Type#TIMESTAMP}.
      */
     LocalDateTime getDateTime() throws VariantTypeException;
 
     /**
-     * Get the scalar value of variant as Instant, if the variant type is 
{@link Type#TIMESTAMP}.
+     * Get the scalar value of variant as Instant, if the variant type is 
{@link

Review Comment:
   can you apply to all JavaDocs in this class?
   ```suggestion
        * Get the scalar value of variant as {@link Instant}, if the variant 
type is {@link
   ```



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java:
##########
@@ -151,35 +151,47 @@ private static List<TestSetSpec> variantCasts() {
                                 "CAST(PARSE_JSON('42') AS INT)",
                                 42,
                                 INT().notNull())
-                        // Integer overflow wraps around (Java narrowing), 
like a regular numeric
-                        // cast.
+                        // Spark-style overflow: an integer value outside the 
target range fails
+                        // CAST and returns NULL for TRY_CAST, instead of 
wrapping around.
+                        .testTableApiRuntimeError(
+                                call("PARSE_JSON", "40000").cast(SMALLINT()), 
"overflowed")
+                        .testSqlRuntimeError("CAST(PARSE_JSON('40000') AS 
SMALLINT)", "overflowed")
                         .testResult(
-                                call("PARSE_JSON", "40000").cast(SMALLINT()),
-                                "CAST(PARSE_JSON('40000') AS SMALLINT)",
-                                (short) -25536,
-                                SMALLINT().notNull())
-                        .testResult(
-                                call("PARSE_JSON", "128").cast(TINYINT()),
-                                "CAST(PARSE_JSON('128') AS TINYINT)",
-                                (byte) -128,
-                                TINYINT().notNull())
+                                call("PARSE_JSON", 
"40000").tryCast(SMALLINT()),
+                                "TRY_CAST(PARSE_JSON('40000') AS SMALLINT)",
+                                null,
+                                SMALLINT())
+                        .testTableApiRuntimeError(
+                                call("PARSE_JSON", "128").cast(TINYINT()), 
"overflowed")

Review Comment:
   to conclude on this: whereas for regular casts people opt-in on the 
overflow, for variant casts they should first extract the correct value before 
opt-in to overflow.



##########
flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/functions/CastFunctionITCase.java:
##########
@@ -151,35 +151,47 @@ private static List<TestSetSpec> variantCasts() {
                                 "CAST(PARSE_JSON('42') AS INT)",
                                 42,
                                 INT().notNull())
-                        // Integer overflow wraps around (Java narrowing), 
like a regular numeric
-                        // cast.
+                        // Spark-style overflow: an integer value outside the 
target range fails

Review Comment:
   
   ```suggestion
                           // An integer value outside the target range fails
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,62 @@ close to the semantics of JSON. Compared to `ROW` and 
`STRUCTURED` type, `VARIAN
 flexibility to support highly nested and evolving schema.
 
 `VARIANT` allows for deeply nested data structures, such as arrays within 
arrays, maps within maps,
-or combinations of both.This capability makes `VARIANT` ideal for scenarios 
where data complexity
+or combinations of both. This capability makes `VARIANT` ideal for scenarios 
where data complexity
 and nesting are significant.
 
 `VARIANT` allows schema evolution, enabling the storage of data with changing 
or unknown schemas
 without requiring upfront schema definition. For example, if a new field is 
added to the data, it
 can be directly incorporated into the `VARIANT` data without modifying the 
table schema. This is
 particularly useful in dynamic environments where schemas may evolve over time.
 
+A `VARIANT` stores a single value of one of the following kinds: `NULL`, 
`BOOLEAN`, `TINYINT`,
+`SMALLINT`, `INT`, `BIGINT`, `FLOAT`, `DOUBLE`, `DECIMAL` (up to precision 
38), `STRING`, `DATE`,
+`TIMESTAMP`, `TIMESTAMP_LTZ`, `BYTES`, or a nested array or object. 
`TIMESTAMP` and `TIMESTAMP_LTZ`
+are stored with microsecond precision and `DATE` as a day count. There is no 
`TIME` kind.
+
+The `PARSE_JSON` function produces only the kinds that JSON syntax can express:
+
+| JSON input                                          | Stored `VARIANT` kind  
                         |
+|-----------------------------------------------------|-------------------------------------------------|
+| `null`                                              | `NULL`                 
                         |
+| `true` / `false`                                    | `BOOLEAN`              
                         |
+| Integer within the 64-bit signed range              | smallest of 
`TINYINT`/`SMALLINT`/`INT`/`BIGINT` |
+| Integer beyond 64-bit, up to 38 significant digits  | `DECIMAL`              
                         |
+| Decimal in plain notation, up to precision/scale 38 | `DECIMAL`              
                         |
+| Number in scientific notation, e.g. `1.5e3`         | `DOUBLE`               
                         |
+| Number exceeding 38 digits of precision or scale    | `DOUBLE`               
                         |
+| String                                              | `STRING`               
                         |
+| Array                                               | array (elements 
encoded by the same rules)      |
+| Object                                              | object (values encoded 
by the same rules)       |
+
+Because JSON has no literal for float, date, timestamp, or binary values, 
`PARSE_JSON` never
+produces `FLOAT`, `DATE`, `TIMESTAMP`, `TIMESTAMP_LTZ`, or `BYTES`. A 
`VARIANT` can still hold
+those kinds when built by other producers, such as the programmatic 
`VariantBuilder` API or format
+conversions like Avro.
+
+The JSON specification has no `NaN` or infinity literals, so 
`PARSE_JSON('NaN')`,
+`PARSE_JSON('Infinity')`, and `PARSE_JSON('-Infinity')` fail, and 
`TRY_PARSE_JSON` returns `NULL`.
+A `VARIANT` has no dedicated kind for these values. To keep one, store it as a 
JSON string and cast
+it back out, for example `CAST(CAST(PARSE_JSON('"Infinity"') AS STRING) AS 
FLOAT)`.
+
 A primitive-valued `VARIANT` can be converted to a scalar type with `CAST` or 
`TRY_CAST`. Numeric
-targets are lenient: a variant holding any numeric value casts to any numeric 
type, so a JSON integer
-such as `PARSE_JSON('42')` casts to `INT` or `BIGINT`. Other targets require 
the stored value to be of
-the matching kind. When the value cannot be converted, `CAST` fails the job 
and `TRY_CAST` returns
-`NULL`. Use the `JSON_STRING` function to obtain the JSON string 
representation of a `VARIANT`.
+targets accept any numeric value, so `PARSE_JSON('42')` casts to `INT`, 
`BIGINT`, or `DOUBLE`. A
+value outside an integer or `DECIMAL` target's range fails `CAST` and returns 
`NULL` for
+`TRY_CAST`. Other targets require the stored value to be of the matching kind, 
and a mismatch is
+handled the same way. Casting a `VARIANT` to `CHAR`/`VARCHAR` extracts the 
scalar value, so a
+stored string is returned unquoted (for example `foo`), while objects and 
arrays return their JSON

Review Comment:
   this is what JSON_STRING should be good for.



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