Commit bd904b3
authored
feat: eliminate GlobalLimitExec when input statistics prove limit is already satisfied (#22150)
## Which issue does this PR close?
- Closes #.
## Rationale for this change
`GlobalLimitExec` (and `LocalLimitExec`) are sometimes redundant: if the
input can be proven via exact statistics to produce no more rows than
the fetch value, the limit node does nothing and should be removed
entirely.
Previously, the `LimitPushdown` rule had no mechanism to eliminate such
trivially-satisfied limits. A query like `SELECT * FROM (VALUES ...)
LIMIT 10` — where the input is a single-row `PlaceholderRowExec` — still
carried an unnecessary `GlobalLimitExec` in the physical plan.
Similarly, a `LIMIT N` over an `EmptyExec` or any zero-row plan was
retained.
## What changes are included in this PR?
- Adds `limit_satisfied_by_input()` in `limit_pushdown.rs`: checks
whether a plan's child provably produces at most `fetch` rows (requires
`skip == 0` and a single output partition).
- Adds `limit_eliminable_exact_num_rows()`: iteratively unwraps
`ProjectionExec` wrappers and recognises `EmptyExec` (0 rows),
`PlaceholderRowExec` (1 row), and any plan reporting
`Precision::Exact(0)` rows as eliminable producers.
- When a limit is statically satisfied, marks `global_state.satisfied =
true` and returns early — **without** resetting `fetch`/`skip` — so
nested limit nodes still receive the correct outer constraints to merge
against.
- Updates the `merges_local_limit_with_local_limit` snapshot: the result
is now bare `EmptyExec` (limit eliminated).
- Updates `union.slt`: `ProjectionExec` over `PlaceholderRowExec` (1
row) with `fetch=3` no longer carries a redundant `GlobalLimitExec`.
- Adds `explain_tree.slt` test: `SELECT count(*) … LIMIT 10` over a
two-row VALUES clause is correctly reduced to `ProjectionExec →
PlaceholderRowExec` with no limit node.
- Updates copy.slt: `fetch=10` is now correctly pushed all the way down
to `DataSourceExec`.
## Are these changes tested?
Yes.
- `cargo fmt --all`
- `cargo clippy --all-targets --all-features -- -D warnings`
- `cargo test -p datafusion-core --test physical_optimizer limit`
- `cargo test --features backtrace,parquet_encryption --profile ci
--package datafusion-sqllogictest --test sqllogictests -- copy.slt
union.slt explain_tree.slt`
## Are there any user-facing changes?
No API changes. Physical plans for queries with `LIMIT` over statically
small inputs (`EmptyExec`, `PlaceholderRowExec`, or zero-row tables)
will now have the redundant `GlobalLimitExec`/`LocalLimitExec` nodes
eliminated, resulting in simpler and slightly more efficient plans.1 parent 9d92944 commit bd904b3
5 files changed
Lines changed: 97 additions & 12 deletions
File tree
- datafusion
- core/tests/physical_optimizer
- physical-optimizer/src
- sqllogictest/test_files
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
467 | 467 | | |
468 | 468 | | |
469 | 469 | | |
470 | | - | |
471 | | - | |
472 | | - | |
473 | | - | |
| 470 | + | |
474 | 471 | | |
475 | 472 | | |
476 | 473 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| 70 | + | |
70 | 71 | | |
71 | 72 | | |
72 | 73 | | |
| 74 | + | |
73 | 75 | | |
| 76 | + | |
| 77 | + | |
74 | 78 | | |
75 | 79 | | |
76 | 80 | | |
| |||
158 | 162 | | |
159 | 163 | | |
160 | 164 | | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
161 | 184 | | |
162 | 185 | | |
163 | 186 | | |
| |||
284 | 307 | | |
285 | 308 | | |
286 | 309 | | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
287 | 363 | | |
288 | 364 | | |
289 | 365 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1803 | 1803 | | |
1804 | 1804 | | |
1805 | 1805 | | |
| 1806 | + | |
| 1807 | + | |
| 1808 | + | |
| 1809 | + | |
| 1810 | + | |
| 1811 | + | |
| 1812 | + | |
| 1813 | + | |
| 1814 | + | |
| 1815 | + | |
| 1816 | + | |
| 1817 | + | |
| 1818 | + | |
1806 | 1819 | | |
1807 | 1820 | | |
1808 | 1821 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
393 | 393 | | |
394 | 394 | | |
395 | 395 | | |
396 | | - | |
| 396 | + | |
397 | 397 | | |
398 | 398 | | |
399 | 399 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
530 | 530 | | |
531 | 531 | | |
532 | 532 | | |
533 | | - | |
534 | | - | |
535 | | - | |
536 | | - | |
537 | | - | |
538 | | - | |
539 | | - | |
| 533 | + | |
| 534 | + | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
540 | 539 | | |
541 | 540 | | |
542 | 541 | | |
| |||
0 commit comments