Alessandro Solimando created CALCITE-7920:
---------------------------------------------

             Summary: Enhance RelFieldTrimmer to trim Uncollect nodes
                 Key: CALCITE-7920
                 URL: https://issues.apache.org/jira/browse/CALCITE-7920
             Project: Calcite
          Issue Type: Improvement
          Components: core
    Affects Versions: 1.42.0
            Reporter: Alessandro Solimando


h3. Preliminaries

_Uncollect_ expands input collections into output rows. When expanding multiple 
collections, elements are paired by position and shorter collections are padded 
with NULL. The longest collection determines the number of output rows.

For example, this query selects only a:
{code:sql}
SELECT a
FROM UNNEST(ARRAY[10, 20], ARRAY[1, 2, 3]) AS t(a, b);
{code}
It returns three rows: 10, 20 and NULL. Although b is not selected, its 
collection produces a third row, where a is NULL.

Removing that collection changes the query to:
{code:sql}
SELECT a
FROM UNNEST(ARRAY[10, 20]) AS t(a);
{code}
This returns only two rows: 10 and 20. Field trimming may remove column b from 
the output, but must retain ARRAY[1, 2, 3] as an input to UNNEST, because it 
causes the third output row.
h3. Missing support

_RelFieldTrimmer_ has no specialized handling for {_}Uncollect{_}. Its generic 
implementation treats every input field as used and returns an identity output 
mapping 
([here|https://github.com/apache/calcite/blob/148bb0975f6be47057137283a98c1914bab87037/core/src/main/java/org/apache/calcite/sql2rel/RelFieldTrimmer.java#L414-L436]).

Add explicit handling so that field trimming can distinguish required output 
fields from collection inputs that must remain to preserve row counts.
h3. Verification

The examples were executed locally on PostgreSQL 18.6 and Trino 484. Both 
returned 10, 20 and NULL for the two-collection query, and 10 and 20 for the 
one-collection query.

Equivalent queries were executed on DuckDB 2.0.0 using its SELECT-list syntax:
{code:sql}
SELECT a
FROM (
  SELECT unnest([10, 20]) AS a,
         unnest([1, 2, 3]) AS b
) AS t;

SELECT a
FROM (
  SELECT unnest([10, 20]) AS a
) AS t;
{code}
DuckDB returned the same values. Additional count queries confirmed these 
results on all three engines:
||Expansion||COUNT( * )||COUNT(a)||
|Two collections|3|2|
|One collection|2|2|

These executions confirm that removing an unused collection input can change 
the query result.
h3. Proposed fix

Add {_}Uncollect{_}-specific handling to {_}RelFieldTrimmer{_}. Return the 
required output fields with the appropriate mapping, introducing a projection 
where necessary, and propagate the required input fields to the child.

Preserve collection inputs whose cardinality affects the result, even when 
their output fields are unused.

Add coverage in _RelFieldTrimmerTest_ for partially used outputs, multiple 
collections, ordinality, and preservation of row counts for NULL and empty 
collections. This issue stays within the current field-trimming framework. A 
future extension could use information from ancestor operators to remove 
collection inputs when doing so provably preserves the final result.



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

Reply via email to