Ramin Gharib created FLINK-41438:
------------------------------------

             Summary: ARRAY, MAP and ROW element access returns -1 instead of 
NULL when the element type is NOT NULL
                 Key: FLINK-41438
                 URL: https://issues.apache.org/jira/browse/FLINK-41438
             Project: Flink
          Issue Type: Bug
          Components: Table SQL / Planner
    Affects Versions: 2.3.0
            Reporter: Ramin Gharib
            Assignee: Ramin Gharib


Reading an element that doesn't exist returns a made-up value instead of NULL 
when the element type is NOT NULL. This covers an ARRAY index past the end, a 
missing MAP key, ELEMENT on an empty array, and any of these on a NULL 
container. Reproduced on master, identical in batch and streaming mode:

{code:sql}
SELECT ARRAY[10,20,30][4], ARRAY[10,20,30][4] IS NULL;
SELECT x FROM (VALUES (2), (4)) AS t(x) WHERE ARRAY[10,20,30][x] IS NULL;
SELECT MAP['a', 1]['b'];
SELECT ELEMENT(ARRAY_REMOVE(ARRAY[1], 1));

CREATE TABLE src (r ROW<f INT NOT NULL>) WITH (
  'connector' = 'datagen', 'number-of-rows' = '1', 'fields.r.null-rate' = 
'1.0');
SELECT r['f'], r.f FROM src;
{code}

||Expression||Expected||Actual||
|{{ARRAY\[10,20,30\]\[4\]}}|NULL|-1|
|{{ARRAY\[10,20,30\]\[4\] IS NULL}}|TRUE|FALSE|
|{{WHERE ARRAY\[10,20,30\]\[x\] IS NULL}} over x = 2, 4|row 4|no rows|
|{{MAP\['a', 1\]\['b'\]}}|NULL|-1|
|{{ELEMENT(ARRAY_REMOVE(ARRAY\[1\], 1))}}|NULL|-1|
|{{r\['f'\]}} on a NULL {{ROW<f INT NOT NULL>}}|NULL|-1|
|{{r.f}} on the same row|NULL|NULL|

h3. Cause

The planner types all of these results as nullable, so 
{{TYPEOF(ARRAY\[10,20,30\]\[4\])}} is {{INT}}. {{generateArrayElementAt}}, 
{{generateArrayElement}}, {{generateMapGet}} and {{generateDot}} in 
{{ScalarOperatorGens}} set the null term correctly but return the element or 
field type, here {{INT NOT NULL}}. Consumers that trust the type drop the null 
term: {{CodeGenUtils.rowSetField}} skips {{setNullAt}} for a NOT NULL field of 
a {{BoxedWrapperRowData}} or {{GenericRowData}}, which covers the Calc output 
and constant folding, and {{generateIsNull}} folds to {{false}}. The value 
written is the type's placeholder from {{primitiveDefaultValue}}, {{-1}} for 
INT and an empty string for STRING.

FLINK-34656 reports the NULL-array case of the same cause.

{{ArrayTypeTest#testReturnNullWhenArrayIndexOutOfBounds}} asserts NULL for 
{{f2\[4\]}} on an {{ARRAY<INT NOT NULL>}} and passes, because 
{{ExpressionTestBase}} casts every expression to VARCHAR and writes a 
{{BinaryRowData}}, and both read the null term.

PostgreSQL, Calcite, Spark with ANSI off, DuckDB, Hive, CockroachDB and 
StarRocks all return NULL. The SQL:1999 standard raises an error.

h3. Proposal

Pass the call's result type into the four generators, as 
{{generateArrayCardinality}} already does, so the generated expression carries 
the type the planner inferred. Queries that return the placeholder today return 
NULL afterwards.




--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to