Skip to content

fix: preserve TopK dynamic filters across recursive CTE iterations - #26670

Merged
jayzhan211 merged 8 commits into
apache:mainfrom
Kristofferjoha:fix/topk-recursive-26642
Oct 25, 2026
Merged

jayzhan211 merged 8 commits into
apache:mainfrom
Kristofferjoha:fix/topk-recursive-26642

Conversation

@Kristofferjoha

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Closes #26642.

Rationale for this change

TopK dynamic filters inside recursive CTEs stop updating the scan because SortExec::reset_state() creates a new filter on each iteration, while the scan still references the original one. This prevents pruning during recursive queries.

What changes are included in this PR?

  • Reset the existing shared dynamic filter instead of replacing it.
  • Rebuild the TopK filter state using the existing DynamicFilterPhysicalExpr.
  • Add a recursive TopK regression test in cte.slt.

Depends on #26056 for DynamicFilterPhysicalExpr::reset(). It hasn't been merged yet, but it has been approved, so I based this change on its implementation.

What is the testing strategy for this PR?

The new cte.slt case checks that the recursive query returns 1–4 and that the Parquet scan outputs 30 rows instead of 300, pruning 27 row groups across three iterations.

Are there any user-facing changes?

No.

@Kristofferjoha Kristofferjoha changed the title Fix/topk recursive 26642 fix: preserve TopK dynamic filters across recursive CTE iterations Oct 23, 2026
.clone();
// Our dynamic filter and execution metrics are the state we need to reset.
new_sort.filter = Some(new_sort.create_filter());
let expr = new_sort.filter.as_ref().map(|filter| filter.read().expr());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: after the reset this is the same as the existing rebuild_filter_for_current_partitioning() helper, so it could reuse it:

-        let expr = new_sort.filter.as_ref().map(|filter| filter.read().expr());
-        if let Some(expr) = expr {
-            expr.reset()?;
-            new_sort.filter = Some(new_sort.create_filter_with_expr(expr));
+        if let Some(filter) = &new_sort.filter {
+            filter.read().expr().reset()?;
         }
-
+        new_sort.rebuild_filter_for_current_partitioning();
         new_sort.metrics_set = ExecutionPlanMetricsSet::new();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, missed the existing helper. reset_state() uses it now.

@github-actions github-actions Bot added physical-expr Changes to the physical-expr crates sqllogictest SQL Logic Tests (.slt) physical-plan Changes to the physical-plan crate labels Oct 24, 2026
// Our dynamic filter and execution metrics are the state we need to reset.
new_sort.filter = Some(new_sort.create_filter());
if let Some(filter) = &new_sort.filter {
filter.read().expr().reset()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: #26669 makes DynamicFilterPhysicalExpr::reset() infallible, so this ? won't compile once both PRs are in. The two PRs touch different files, so git won't flag a conflict. Could you drop the ? in whichever of the two merges second (or rebase this one on #26669)?

-            filter.read().expr().reset()?;
+            filter.read().expr().reset();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let me address this as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry didnt see it until now, Thanks

@github-actions github-actions Bot removed the physical-expr Changes to the physical-expr crates label Oct 25, 2026
@jayzhan211
jayzhan211 added this pull request to the merge queue Oct 25, 2026
@jayzhan211

Copy link
Copy Markdown
Contributor

Thanks @Kristofferjoha

Merged via the queue into apache:main with commit 6aef019 Oct 25, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

physical-plan Changes to the physical-plan crate sqllogictest SQL Logic Tests (.slt) v56.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TopK dynamic filters don't prune inside recursive CTEs

3 participants