adriangb opened a new pull request, #26274:
URL: https://github.com/apache/datafusion/pull/26274

   ## Which issue does this PR close?
   
   - Closes apache/datafusion#26240.
   
   Stacked on apache/datafusion#25580 and on #26269 (apache/datafusion#26238). 
Only the last commit is new.
   
   ## Rationale for this change
   
   The body of a materialized CTE kept all its columns, also when no reference 
read them.
   
   ```sql
   CREATE TABLE wide(a INT, b INT, c INT, d INT, e INT) AS VALUES (1, 10, 100, 
1000, 10000);
   
   EXPLAIN WITH w AS MATERIALIZED (SELECT * FROM wide)
   SELECT a FROM w
   UNION ALL
   SELECT b FROM w;
   ```
   
   Before, all five columns were read and buffered:
   
   ```text
   MaterializedCte: name=w
     SubqueryAlias: w
       TableScan: wide projection=[a, b, c, d, e]
     Union
       Projection: w.a
         MaterializedCteScan: name=w
       Projection: w.b AS a
         MaterializedCteScan: name=w
   ```
   
   After, only `a` and `b` are read and buffered:
   
   ```text
   MaterializedCte: name=w
     SubqueryAlias: w
       TableScan: wide projection=[a, b]
     Union
       MaterializedCteScan: name=w projection=[a]
       Projection: w.b AS a
         MaterializedCteScan: name=w projection=[b]
   ```
   
   ## What changes are included in this PR?
   
   - `MaterializedCteScan` has a new field `projection: Option<Vec<usize>>`: 
the indexes of the columns of the CTE body that the scan outputs.
   - `OptimizeProjections` narrows each `MaterializedCteScan` to the columns 
that its parent needs, as it does for a `TableScan`.
   - At a `MaterializedCte`, `OptimizeProjections` optimizes the continuation 
first. It then computes the union of the columns of all the scans of the CTE, 
projects the body to this union, and gives each scan the new positions of its 
columns. Scans in subquery expressions and in the bodies of other CTEs are 
counted.
   - `MaterializedCteScanExec` applies the projection to the shared buffered 
batches.
   - The physical planner checks that each scan agrees with the physical schema 
of its body when it binds the scan, and returns an internal error if it does 
not.
   
   The projection in the scan makes the intermediate state (a narrow scan and a 
wide body) a valid plan, so no separate rule is necessary.
   
   Notes:
   
   - A reference that reads no column (`SELECT count(*) FROM c`) gets 
`projection=[]`. If no reference reads a column, the body keeps its first 
column.
   - The projection is put above the body. A volatile column can be removed, 
but the number of rows does not change. Columns below a `DISTINCT` or a 
recursive query are not removed.
   - A scan in a correlated subquery that is not decorrelated is not narrowed. 
The CTE then keeps all its columns.
   - The pgjson display does not show the projection.
   
   ## Are these changes tested?
   
   Yes. `cte_materialized.slt`: `EXPLAIN` and result checks for the example 
above, a column that only a filter reads, `count(*)`, a CTE that another CTE 
reads, references in subqueries, a volatile body, a `DISTINCT` body, aliased 
columns, a self join, a recursive body and a CTE that nothing reads. Unit tests 
for the scan projection, nested CTEs, idempotence and the zero-column case.
   
   ## Are there any user-facing changes?
   
   `EXPLAIN` shows `projection=[...]` on `MaterializedCteScan` and 
`MaterializedCteScanExec` when the scan does not read all the columns. 
`MaterializedCteScan` has the new public field `projection`. The upgrade guide 
for 56.0.0 says this.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to