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

   ## Which issue does this PR close?
   
   - Closes #27131.
   
   ## Rationale for this change
   
   A `MoveTowardsLeafNodes` expression loses leaf pushdown as soon as it shares 
a
   projection with a `KeepInPlace` expression and a `RepartitionExec` is 
inserted.
   The two symptom classes are:
   
   ```sql
   -- errors: "file_row_index() is source dependent and cannot be evaluated 
directly"
   SELECT file_row_index(), id + 1 FROM t;
   
   -- no error, but reads the whole struct instead of pruning to one field
   SELECT s['value'], id + 1 FROM t;
   ```
   
   `ProjectionExec::benefits_from_input_partitioning` is true as soon as *any*
   expression in the projection is `KeepInPlace` (the default placement). That 
one
   flag does two opposing things:
   
   - `EnsureRequirements` inserts a `RepartitionExec` beneath the projection, 
and
   - `RepartitionExec::try_swapping_with_projection` then refuses to let that 
same
     projection descend past it.
   
   So the leaf expression can never reach the source. The leaf call does not 
have to
   be wrapped in anything — an unrelated computed column in the same projection 
is
   enough, because the flag is a property of the whole projection, not of one
   expression. `get_field` is the headline case: nested column pruning is its
   documented purpose and it degrades silently.
   
   The trigger needs a `RepartitionExec`, so the table has to exceed 
`batch_size`
   (`EnsureRequirements` uses `batch_size` as a row-count threshold). That is 
why
   this has not shown up in the existing small-fixture tests.
   
   ## What changes are included in this PR?
   
   `ProjectionPushdown` now splits such a projection instead of giving up:
   
   - the leaf expressions move into a lower projection that the source absorbs;
   - the remaining computation stays above the repartition, where it is still
     parallelised.
   
   Both guards keep their intent rather than one being traded for the other: the
   scan only gains the cheap leaf expression, and the expensive computation is 
still
   run in parallel.
   
   Deliberate limits:
   
   - The split is committed only when `child.try_swapping_with_projection` 
actually
     returns `Some` for the lower projection, i.e. when the source really does
     absorb it. Otherwise the plan is left untouched.
   - Only `Partitioning::RoundRobinBatch` is handled. Hash and range 
partitioning
     reference columns by index, so changing the schema below them would require
     remapping their expressions; that is left out of this PR.
   - Only the columns the outer expressions still reference outside the 
extracted
     leaves are passed through, so `s['a'] + 1` does not end up reading the 
whole
     struct alongside the extracted field.
   
   ## What is the testing strategy for this PR?
   
   - `datafusion/sqllogictest/test_files/file_row_index.slt` — value-based
     regression tests for the error class: bare marker plus a computed sibling,
     `+ 1`, `arrow_typeof`, two different markers mixed, a duplicated marker, 
and
     non-regression for `Filter` / `Sort` / `Aggregate`.
   - `datafusion/sqllogictest/test_files/projection_pushdown.slt` — plan 
assertions
     for the `get_field` class. Value comparison can never catch that one: 
without
     pruning the results are still correct, only more data is read, so the test 
has
     to assert the pruned `projection=[...]` in the plan.
   - `datafusion/physical-optimizer` unit tests: 50 passed.
   
   Two traps are called out in comments in both test files, because either one 
makes
   the tests pass against unfixed code:
   
   1. the fixture must exceed `batch_size`, or no `RepartitionExec` is inserted 
and
      there is nothing to reproduce;
   2. a bare `LIMIT n` is pushed into the scan and hides the bug again.
   
   Verified by reverting the source file with `git checkout main -- <file>`: 
the new
   tests then fail with 6 errors and 16 plan-diff lines.
   
   ## Are there any user-facing changes?
   
   No API changes. Queries that previously failed with "is source dependent and
   cannot be evaluated directly" now succeed, and `get_field` regains nested 
column
   pruning when it shares a projection with other expressions.
   
   One plan-shape note for #25456 (inlining projection columns), which touches
   adjacent code: its test pins `file_row_index()` used twice as *not* inlined. 
When
   the split here fires, the two identical leaf calls are de-duplicated into a
   single lower-projection column. Values are unaffected; only the plan shape
   differs. Happy to rebase in whichever order is convenient.
   
   🤖 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