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


##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,64 @@ 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`). A variant that holds 
an object, array, or
+binary value is not castable to a string; use `JSON_STRING` for its JSON 
representation instead
+(there a string stays quoted, for example `"foo"`). A bounded 
`CHAR(n)`/`VARCHAR(n)` target is length-checked strictly, with no
+padding or truncation: `VARCHAR(n)` accepts up to `n` characters and `CHAR(n)` 
requires exactly `n`,
+otherwise `CAST` fails and `TRY_CAST` returns `NULL`.

Review Comment:
   people should know how they work already
   ```suggestion
   padding or truncation.
   ```



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,64 @@ 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`). A variant that holds 
an object, array, or
+binary value is not castable to a string; use `JSON_STRING` for its JSON 
representation instead
+(there a string stays quoted, for example `"foo"`). A bounded 
`CHAR(n)`/`VARCHAR(n)` target is length-checked strictly, with no
+padding or truncation: `VARCHAR(n)` accepts up to `n` characters and `CHAR(n)` 
requires exactly `n`,
+otherwise `CAST` fails and `TRY_CAST` returns `NULL`.
+
+{{< hint info >}}
+Unlike a regular numeric cast, casting a numeric `VARIANT` never overflows. To 
narrow a 
+`VARIANT`, 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 (after 
overflow)
+```
+{{< /hint >}}

Review Comment:
   I would vote for dropping this paragraph entirely. Or make it less 
prominent. A `hint info` is definitely too much. A sentence like `Numeric 
overflows are prohibited.` should be enough after: `targets accept any wider 
numeric value, so `PARSE_JSON('42')` casts to `INT`, `BIGINT`, or `DOUBLE`. 
Numeric overflows are prohibited.`



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,64 @@ 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

Review Comment:
   ```suggestion
   targets accept any wider numeric value, so `PARSE_JSON('42')` casts to 
`INT`, `BIGINT`, or `DOUBLE`. A
   ```



##########
flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java:
##########
@@ -0,0 +1,137 @@
+/*
+ * 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.api.TableRuntimeException;
+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 
TableRuntimeException} on overflow instead
+ * of wrapping. Casting to a character string extracts the scalar value (a 
string stays unquoted); a
+ * variant holding an object, array, or binary value is not castable and 
raises {@link
+ * TableRuntimeException} (use {@code JSON_STRING} for its 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 TableRuntimeException(
+                    String.format(
+                            "Casting the VARIANT value %s to DECIMAL(%d, %d) 
overflowed.",
+                            value, precision, scale));
+        }
+        return decimal;
+    }
+
+    /**
+     * Casts a {@code VARIANT} to its SQL string value for a {@code 
CHAR(targetLength)} or {@code
+     * VARCHAR(targetLength)} target. Scalars use their raw value (a string 
stays unquoted, unlike
+     * {@code JSON_STRING}); a variant holding an object, array, or binary 
value is not castable and
+     * raises {@link TableRuntimeException}.
+     *
+     * <p>The target length is enforced strictly, with no padding or 
truncation: a {@code VARCHAR}
+     * value must not be longer than {@code targetLength}, and a {@code CHAR} 
value must match it
+     * exactly. A value that does not fit raises {@link TableRuntimeException}.

Review Comment:
   nit: Too much comment. Code is shorter than this JavaDoc.



##########
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}; {@code FLOAT} and {@code DOUBLE} keep lenient IEEE 
conversion (overflow
+     * becomes infinity). A non-numeric variant raises {@link 
ClassCastException}, which fails
+     * {@code CAST} and yields {@code null} for {@code TRY_CAST}.
      */
     private static String numericExpression(String inputTerm, LogicalType 
target) {
         final String number = cast(className(Number.class), 
methodCall(inputTerm, "get"));
-        if (!target.is(LogicalTypeRoot.DECIMAL)) {
-            return methodCall(number, numberAccessor(target));
-        }
-        final DecimalType decimalType = (DecimalType) target;
-        return staticCall(
-                DecimalData.class,
-                "fromBigDecimal",
-                constructorCall(BigDecimal.class, methodCall(number, 
"toString")),
-                decimalType.getPrecision(),
-                decimalType.getScale());
-    }
-
-    private static String numberAccessor(LogicalType target) {
         switch (target.getTypeRoot()) {
             case TINYINT:
-                return "byteValue";
+                return staticCall(VariantCastUtils.class, "toByteExact", 
number);
             case SMALLINT:
-                return "shortValue";
+                return staticCall(VariantCastUtils.class, "toShortExact", 
number);
             case INTEGER:
-                return "intValue";
+                return staticCall(VariantCastUtils.class, "toIntExact", 
number);
             case BIGINT:
-                return "longValue";
+                return staticCall(VariantCastUtils.class, "toLongExact", 
number);
             case FLOAT:
-                return "floatValue";
+                return methodCall(number, "floatValue");
             case DOUBLE:
-                return "doubleValue";
+                return methodCall(number, "doubleValue");

Review Comment:
   Are there any data loss checks in this method? Asking AI leads to exactness 
loss.



##########
flink-table/flink-table-runtime/src/main/java/org/apache/flink/table/runtime/functions/VariantCastUtils.java:
##########
@@ -0,0 +1,137 @@
+/*
+ * 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.api.TableRuntimeException;
+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 
TableRuntimeException} on overflow instead
+ * of wrapping. Casting to a character string extracts the scalar value (a 
string stays unquoted); a
+ * variant holding an object, array, or binary value is not castable and 
raises {@link
+ * TableRuntimeException} (use {@code JSON_STRING} for its 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);

Review Comment:
   Where is max precision of 38 checked?



##########
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}; {@code FLOAT} and {@code DOUBLE} keep lenient IEEE 
conversion (overflow
+     * becomes infinity). A non-numeric variant raises {@link 
ClassCastException}, which fails
+     * {@code CAST} and yields {@code null} for {@code TRY_CAST}.
      */
     private static String numericExpression(String inputTerm, LogicalType 
target) {
         final String number = cast(className(Number.class), 
methodCall(inputTerm, "get"));
-        if (!target.is(LogicalTypeRoot.DECIMAL)) {
-            return methodCall(number, numberAccessor(target));
-        }
-        final DecimalType decimalType = (DecimalType) target;
-        return staticCall(
-                DecimalData.class,
-                "fromBigDecimal",
-                constructorCall(BigDecimal.class, methodCall(number, 
"toString")),
-                decimalType.getPrecision(),
-                decimalType.getScale());
-    }
-
-    private static String numberAccessor(LogicalType target) {
         switch (target.getTypeRoot()) {
             case TINYINT:
-                return "byteValue";
+                return staticCall(VariantCastUtils.class, "toByteExact", 
number);
             case SMALLINT:
-                return "shortValue";
+                return staticCall(VariantCastUtils.class, "toShortExact", 
number);
             case INTEGER:
-                return "intValue";
+                return staticCall(VariantCastUtils.class, "toIntExact", 
number);
             case BIGINT:
-                return "longValue";
+                return staticCall(VariantCastUtils.class, "toLongExact", 
number);
             case FLOAT:
-                return "floatValue";
+                return methodCall(number, "floatValue");

Review Comment:
   Are there any data loss checks in this method? Asking AI leads to exactness 
loss.
   
   > can I store a full long in a float?
   > 
   > The short answer is yes, in terms of size (range), but no, in terms of 
exactness (precision).
   > 
   > If you attempt to store a large long inside a float, the application will 
not crash, but you will likely lose data through rounding errors.



##########
docs/content.zh/docs/sql/reference/data-types.md:
##########
@@ -1507,19 +1507,64 @@ 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

Review Comment:
   Let's not go into the details of CHAR/VARCHAR at this line
   ```suggestion
   handled the same way. Casting a `VARIANT` to `STRING` extracts the scalar 
value, so a
   ```



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