raminqaf opened a new pull request, #29311:
URL: https://github.com/apache/flink/pull/29311

   > **Depends on #29310 (FLINK-40828).** This PR is stacked on it. Only the 
last two commits belong here: `[FLINK-40825][table] Keep a stored NaN or 
infinity when casting VARIANT to FLOAT or DOUBLE` and `[FLINK-40825][table] 
Support casting primitive types to VARIANT`. I will rebase once #29310 is 
merged.
   
   ## What is the purpose of the change
   
   FLIP-521 lists the types that cast to and from VARIANT. The direction 
VARIANT to SQL type exists, but `SqlCastFunction` rejected every cast to 
VARIANT. This PR adds `CAST` and `TRY_CAST` from primitive types to VARIANT. 
Constructed types follow in FLINK-40826.
   
   A type casts to VARIANT only if a VARIANT kind holds its value without loss. 
Any other type is rejected at validation.
   
   | Expression                                 | Result                        
           |
   
|--------------------------------------------|------------------------------------------|
   | `CAST(CAST(1 AS BIGINT) AS VARIANT)`       | `1`, stored as BIGINT         
           |
   | `CAST(42 AS VARIANT)`                      | `42`, stored as INT           
           |
   | `CAST('{"a": 1}' AS VARIANT)`              | the string `'{"a": 1}'`, not 
an object   |
   | `CAST(CAST(NULL AS INT) AS VARIANT)`       | SQL `NULL`, not a variant 
null           |
   | `CAST(CAST('NaN' AS DOUBLE) AS VARIANT)`   | `NaN`, stored as DOUBLE       
           |
   | `CAST(INTERVAL '2' DAY AS VARIANT)`        | fails at validation           
           |
   | `CAST(ARRAY[1, 2] AS VARIANT)`             | fails at validation, 
FLINK-40826         |
   
   ## Brief change log
   
     - Casting a VARIANT to FLOAT or DOUBLE keeps a stored NaN or infinity. The 
FLOAT cast rejects only a finite value that does not fit, such as `1e40`. 
Before, a VARIANT holding NaN, for example from the Avro converter, could not 
be read back. This is the first commit.
     - `SqlCastFunction#canCastFrom` routes a VARIANT target to 
`LogicalTypeCasts` instead of rejecting it, the same way it handles UUID.
     - `LogicalTypeCasts` declares the supported sources explicitly: BOOLEAN, 
the numeric types, CHAR, VARCHAR, BINARY, VARBINARY, DATE, TIME, TIMESTAMP, 
TIMESTAMP_LTZ, and UUID. The cast is explicit only.
     - New `PrimitiveToVariantCastRule`, backed by `VariantCastUtils#fromXxx` 
helpers. The rule never fails.
     - Docs: a "cast to VARIANT" section in `data-types.md`, the VARIANT column 
of the cast matrix, and TIME and UUID in the list of VARIANT kinds.
   
   Notes for reviewers:
     - An integer keeps the width of its SQL type. `PARSE_JSON('1')` picks the 
smallest kind because JSON text carries no width, but a cast keeps the declared 
type. Both are valid under the Parquet Variant spec, which treats all integer 
widths as one equivalence class.
     - A string is wrapped as a STRING kind and never parsed. `PARSE_JSON` 
stays the way to parse JSON text.
     - NaN and infinity are stored. The binary format holds IEEE numbers, and 
`PARSE_JSON` rejects them only because JSON has no literal for them. 
`JSON_STRING` still fails on such a value. Printing and `CAST(v AS STRING)` 
show `NaN`, which relies on #29310.
     - Timestamps choose microsecond or nanosecond precision by value, as 
`VariantBuilder` does. Following the declared precision instead is an open 
question on FLINK-40825.
     - The explicit-only choice is deliberate. Making the cast implicit later 
is additive, but taking implicit back would break queries.
     - `SqlCastFunction` is a copy of the Calcite class. The change replaces 
its existing TODO to support casts to VARIANT.
     - A `LogicalTypeCastsTest` row asserted that UUID does not cast to 
VARIANT. It now asserts that it does.
   
   ## Verifying this change
   
   This change added tests and can be verified as follows:
   
     - `LogicalTypeCastsTest` covers every supported source, and rejects 
INTERVAL, TIMESTAMP WITH TIME ZONE, MULTISET, ARRAY, MAP, and ROW.
     - `CastRuleProviderTest` checks rule resolution, that the cast never 
fails, and that VARIANT to VARIANT stays the identity.
     - `CastRulesTest` checks the stored kind for every source, including the 
kept integer width, `TIMESTAMP(3)`, `TIMESTAMP(6)`, and `TIMESTAMP(9)`, NaN and 
infinity, and SQL NULL. It also covers NaN and infinity read back from VARIANT 
to FLOAT and DOUBLE.
     - `CastFunctionITCase` round-trips each type through VARIANT in SQL and 
the Table API, for literals and for values computed at runtime, including NaN 
and infinity. It also checks the validation errors.
   
   ## Does this pull request potentially affect one of the following parts:
   
     - Dependencies (does it add or upgrade a dependency): no
     - The public API, i.e., is any changed class annotated with 
`@Public(Evolving)`: no
     - The serializers: no
     - The runtime per-record code paths (performance sensitive): yes. The new 
cast runs per record and builds one VARIANT per value.
     - Anything that affects deployment or recovery: JobManager (and its 
components), Checkpointing, Kubernetes/Yarn, ZooKeeper: no
     - The S3 file system connector: no
   
   ## Documentation
   
     - Does this pull request introduce a new feature? yes
     - If yes, how is the feature documented? docs, in `data-types.md` (English 
and Chinese)
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes (please specify the tool below)
   
   Generated-by: Opus 5.5
   


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