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

   ## What is the purpose of the change
   
   A VARIANT is never cast implicitly, but when it met another type in `UNION 
ALL`, `CASE`, `VALUES` or an `ARRAY` or `MAP` constructor, the planner picked a 
common type anyway. The last input type won, and the other inputs were cast to 
it without an explicit `CAST`. With `t(v)` holding `PARSE_JSON('1')` and 
`PARSE_JSON('"x"')`:
   
   ```sql
   SELECT v FROM t UNION ALL SELECT 'abc';
   ```
   
   | Statement                              | Before                            
                                             | After                            
                             |
   
|----------------------------------------|--------------------------------------------------------------------------------|---------------------------------------------------------------|
   | `v UNION ALL 'abc'`                    | type `CHAR(3)`, rows `'1  '`, `'x 
 '`, `'abc'`                                 | `Type mismatch in column 1 of 
UNION ALL`                      |
   | `'abc' UNION ALL v`                    | type `VARIANT`, rows `"abc"`, 
`1`, `"x"`                                       | `Type mismatch in column 1 
of UNION ALL`                      |
   | `CASE WHEN .. THEN v ELSE 1 END`       | type `INT`, the job fails on 
`"x"`: `Cannot cast a VARIANT STRING value to INTEGER.` | `Illegal mixing of 
types in CASE or COALESCE statement`       |
   | `VALUES (PARSE_JSON('1')), ('abc')`    | type `CHAR(3)`, rows `'1  '`, 
`'abc'`                                          | `Values passed to VALUES 
operator must have compatible types` |
   | `ARRAY[v, 'abc']`, `MAP['k', v, 'l', 2]` | `AssertionError: Conversion to 
relational algebra failed to preserve datatypes` | `Parameters must be of the 
same type`                         |
   | `ARRAY[v] UNION ALL ARRAY[1]`          | common type `ARRAY<INT>`          
                                             | `Type mismatch in column 1 of 
UNION ALL`                      |
   
   The new messages are the ones Flink already gives for other incompatible 
types, e.g. `ARRAY[TRUE]` in place of the VARIANT. VARIANT with VARIANT, 
including different nullability, and VARIANT with an untyped `NULL` still 
resolve, so `COALESCE(v, PARSE_JSON('0'))` and `ARRAY[v, PARSE_JSON('2')]` work 
as before.
   
   The cause is the Calcite upgrade in FLINK-39817: `SqlTypeUtil.canCastFrom` 
returns true whenever either side is a VARIANT, and 
`SqlTypeFactoryImpl.leastRestrictiveByCast` then keeps the last type. 2.3.0 
doesn't have that shortcut, and release-2.4 does. I plan to backport this to 
release-2.4 so 2.4.0 never ships the implicit cast.
   
   ## Brief change log
   
     - `FlinkTypeFactory.leastRestrictive` finds no common type when a VARIANT 
meets any type other than VARIANT or an untyped `NULL`. The check also looks 
inside ARRAY, MULTISET, MAP and ROW. When their elements have no common type, 
Calcite still resolves the whole constructed type by cast, so a top-level check 
alone would leave `ARRAY<VARIANT>` with `ARRAY<INT>` resolving to `ARRAY<INT>`. 
The check lives in Flink's type factory because the copied Calcite classes stay 
as they are.
   
   ## Verifying this change
   
   This change added tests and can be verified as follows:
   
     - `FlinkTypeFactoryTest#testLeastRestrictiveOfVariantAndOtherType` covers 
VARIANT with STRING in both orders and VARIANT nested in ARRAY, MAP and ROW. 
Without the fix it fails with `expected: null but was: INTEGER ARRAY` and 
similar. With only a top-level check, the ARRAY, MAP and ROW cases still fail.
     - `FlinkTypeFactoryTest#testLeastRestrictive` gets three cases that must 
keep resolving: VARIANT with VARIANT of different nullability, VARIANT with 
`NULL`, and the same nested in ARRAY.
     - `VariantSemanticTest` gets `VARIANT_MIXED_WITH_OTHER_TYPE`, which pins 
the user-facing messages for `UNION ALL`, `CASE`, `VALUES` and the `ARRAY` 
constructor. Without the fix it fails with `Expecting code to raise a 
throwable.`
     - The other planner tests that use VARIANT pass, 3963 tests across 18 
classes, among them `CastFunctionITCase`, `JsonFunctionsITCase`, 
`VariantFunctionsITCase`, `ComparableTypeValidatorTest` and `CalcRestoreTest`.
   
   ## 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): no, the check 
runs during planning
     - 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? no
     - If yes, how is the feature documented? not applicable. The data types 
docs already say a cast to VARIANT is never applied implicitly.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes (please specify the tool below)
   
   Generated-by: Claude Code 2.1.319 (Claude 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