Ramin Gharib created FLINK-41406:
------------------------------------
Summary: VARIANT mixed with another type in UNION ALL, CASE or
VALUES is cast implicitly
Key: FLINK-41406
URL: https://issues.apache.org/jira/browse/FLINK-41406
Project: Flink
Issue Type: Bug
Components: Table SQL / Planner
Affects Versions: 2.4.0
Reporter: Ramin Gharib
Assignee: Ramin Gharib
A {{VARIANT}} is never cast implicitly, so it should have no common type with
any other type. On master, a {{VARIANT}} that meets another type in {{UNION
ALL}}, {{CASE}} or {{VALUES}} gets one anyway. The result type depends on the
order of the inputs, and the other inputs are cast to it without an explicit
{{CAST}}.
h3. Reproducer
{{t}} stands for {{(VALUES (PARSE_JSON('1')), (PARSE_JSON('"x"'))) AS t(v)}}.
{code:sql}
SELECT v FROM t UNION ALL SELECT 'abc';
SELECT 'abc' UNION ALL SELECT v FROM t;
SELECT CASE WHEN v IS NOT NULL THEN v ELSE 1 END FROM t;
SELECT x FROM (VALUES (PARSE_JSON('1')), ('abc')) AS u(x);
SELECT ARRAY[v, 'abc'] FROM t;
{code}
||Statement||Actual||Expected||
|{{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 ... 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}}|
|{{CASE ... THEN 1 ELSE v END}}|type {{VARIANT}}, rows {{1}}, {{"x"}}|{{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'\]}}|{{AssertionError: Conversion to relational algebra
failed to preserve datatypes}}|{{Parameters must be of the same type}}|
|{{MAP\['k', v, 'l', 2\]}}|same {{AssertionError}}|{{Parameters must be of the
same type}}|
The expected messages are the ones Flink already gives for other incompatible
types, e.g. with {{ARRAY\[TRUE\]}} in place of the {{VARIANT}}.
The same happens inside constructed types:
{{FlinkTypeFactory.leastRestrictive}} of {{ARRAY<VARIANT>}} and {{ARRAY<INT>}}
returns {{ARRAY<INT>}}, and likewise for {{MAP}} values and {{ROW}} fields.
h3. Cause
Since the Calcite upgrade in FLINK-39817, the copied
{{SqlTypeUtil.canCastFrom}} returns true whenever either side is a {{VARIANT}}.
{{SqlTypeFactoryImpl.leastRestrictiveByCast}} then keeps the last input type,
and {{FlinkTypeFactory.leastRestrictive}} returns it. For {{ARRAY}},
{{MULTISET}}, {{MAP}} and {{ROW}} the cast check recurses into the elements, so
a {{VARIANT}} element is castable there too. release-2.4 has the same code. On
2.3.0 the shortcut is missing, so the same statements most likely fail with an
internal {{AssertionError}} instead (from reading the code, not run).
h3. Proposal
{{FlinkTypeFactory.leastRestrictive}} returns {{null}} when a {{VARIANT}} meets
any type other than {{VARIANT}} or an untyped {{NULL}}. Calcite then reports
its usual type mismatch errors. The check also looks inside {{ARRAY}},
{{MULTISET}}, {{MAP}} and {{ROW}}, because Calcite falls back to the cast check
for the whole type when the elements have no common type. {{VARIANT}} with
{{VARIANT}} and with {{NULL}} keep working. A query that mixes types casts
explicitly, e.g. {{CAST(s AS VARIANT)}} or {{CAST(v AS STRING)}}.
2.4.0 is not released yet, so a backport keeps the implicit cast out of every
release.
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)