jayzhan211 commented on code in PR #26773:
URL: https://github.com/apache/datafusion/pull/26773#discussion_r4434439119
##########
datafusion/physical-optimizer/src/limit_pushdown_past_window.rs:
##########
@@ -141,23 +162,27 @@ fn apply_limit(
if !node.is::<SortExec>() && !node.is::<SortPreservingMergeExec>() {
return None;
}
- let latest = ctx.limit.take();
- let Some(fetch) = latest else {
- ctx.limit = None;
- ctx.lookahead = 0;
+ // Taking the state both reads the chain we were walking and forgets it.
+ let TraverseState {
+ limit, lookahead, ..
+ } = std::mem::take(ctx);
+ let Some(fetch) = limit else {
return Some(Transformed::no(Arc::clone(node)));
};
// A fetch that overflows can't drop any rows, so skip the pushdown rather
// than turning the sort into a TopK that holds every row and can't spill.
- let Some(fetch) = fetch.checked_add(ctx.lookahead) else {
- ctx.lookahead = 0;
+ let Some(fetch) = fetch.checked_add(lookahead) else {
return Some(Transformed::no(Arc::clone(node)));
};
let fetch = match node.fetch() {
None => fetch,
Some(existing) => existing.min(fetch),
};
- Some(Transformed::complete(node.with_fetch(Some(fetch)).unwrap()))
+ // Carry on past this sort rather than stopping. `ctx` is back to its
default
+ // above, which is exactly what it held at the start of the traversal, so
+ // walking the subtree below is equivalent to running the rule on it afresh
+ // -- and that subtree may hold another independent limit over another
window.
+ Some(Transformed::yes(node.with_fetch(Some(fetch)).unwrap()))
Review Comment:
@cool9850311 thanks, the stacked-lookahead fix holds up: I ran on/off
comparisons with `lead`, three stacked windows, `PARTITION BY`, and `OFFSET`,
and they all match. One case of #26659 is still left. An SPM's fetch also
limits everything below it, but once the walk is past a window that fetch never
starts a new chain: `ctx` resets after the SPM gets its fetch, and with no
limit in hand the SPM's own fetch is ignored. So the sort under a second,
partitioned window stays a full sort (main does the same). I should have caught
this when I suggested the SPM exception in an earlier round, sorry for the
extra round.
```sql
SELECT depname, empno, s1, sum(s1) OVER (ORDER BY depname, empno ROWS
BETWEEN CURRENT ROW AND 1 FOLLOWING) AS s2
FROM (SELECT depname, empno, sum(salary) OVER (PARTITION BY depname ORDER BY
empno ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING) AS s1 FROM employees)
ORDER BY depname, empno LIMIT 5;
-- SortPreservingMergeExec fetch=6, but the SortExec under the inner window
stays a full sort
```
With the change below it becomes `TopK(fetch=7)`, and the results don't
change. I ran the window/limit slt files, the unit tests and clippy with it
(`window.slt:6417` still covers the case where the SPM itself gets the fetch).
Could you add the query above, plus the same query with `LIMIT 5` moved into
the subquery (no limit above the outer window → `TopK(fetch=6)`), as slt cases?
````suggestion
// Taking the state both reads the chain we were walking and forgets it.
let TraverseState {
limit, lookahead, ..
} = std::mem::take(ctx);
// A fetch that overflows can't drop any rows, so skip the pushdown
rather
// than turning the sort into a TopK that holds every row and can't
spill.
let fetch = limit
.and_then(|limit| limit.checked_add(lookahead))
.map(|fetch| node.fetch().map_or(fetch, |existing|
existing.min(fetch)));
// A `SortPreservingMergeExec` outputs its input's first rows, so its
fetch
// also bounds everything below it: start the next chain from it.
if node.is::<SortPreservingMergeExec>() {
ctx.reset_limit(fetch.or_else(|| node.fetch()));
}
let Some(fetch) = fetch else {
return Some(Transformed::no(Arc::clone(node)));
};
// Carry on past this sort rather than stopping: the subtree below may
hold
// another independent limit over another window.
Some(Transformed::yes(node.with_fetch(Some(fetch)).unwrap()))
````
--
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]