Repository navigation
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #26270 +/- ##
==========================================
+ Coverage 82.87% 82.88% +0.01%
==========================================
Files 1153 1153
Lines 457101 457702 +601
Branches 457101 457702 +601
==========================================
+ Hits 378818 379381 +563
- Misses 55268 55286 +18
- Partials 23015 23035 +20 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
adriangb
force-pushed
the
mcte-per-run-output
branch
2 times, most recently
from
October 16, 2026 03:46
91d3f3d to
71f28c3
Compare
`MaterializedCteExec::reset_state` cleared the shared buffer in place and returned `self`. This did not follow the `ExecutionPlan::reset_state` contract, and two overlapping runs of one plan shared one buffer. The buffered output is now in a new `RunState` of the `TaskContext`, keyed by the CTE id. Each run of a plan has its own `TaskContext`, so each run has its own output. The plan nodes keep no state of a run, so the default `reset_state` is correct for them. `RecursiveQueryExec` gives each iteration a child `RunState` in which the CTEs that the recursive term declares have no output. A CTE that is declared outside the recursive term keeps its output for the whole query. Closes apache#26229. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adriangb
force-pushed
the
mcte-per-run-output
branch
from
October 16, 2026 16:57
71f28c3 to
07a4fbb
Compare
adriangb
marked this pull request as ready for review
October 16, 2026 16:57
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Which issue does this PR close?
MaterializedCteExec::reset_stateclears its shared buffer in place #26229.Rationale for this change
MaterializedCteExec::reset_statecleared its shared buffer in place and returnedself.ExecutionPlan::reset_statemust return a node with new state and leave the old node unchanged. Because the buffered output was in the plan, two runs of one physical plan also shared it.Example with one physical plan for
WITH c AS MATERIALIZED (SELECT random() AS x) SELECT * FROM c:TaskContextreset_plan_states(plan)while run 1 is openNo SQL query is known to give a wrong result on #25580.
What changes are included in this PR?
TaskContexthas a newRunState. It holds state that operators keep for one run of a plan.RunState, keyed by the CTE id.MaterializedCteBufferholds plan-time data only.MaterializedCteExecuses the defaultreset_state.RecursiveQueryExecgives each iteration a childRunState, in which the CTEs that the recursive term declares are computed again. A CTE that is declared outside the recursive term keeps its output for the whole query.Alternative that was not used: rebind the scans to a new buffer in
reset_plan_states. That function cannot reach a scan in a scalar subquery, and it does not separate two runs when nothing calls reset.Notes for reviewers:
TaskContext. If each partition is executed with a differentTaskContext, the body is computed one time for each context.TaskContextof the run and the replay streams are dropped. Release the buffer of a materialized CTE when the last scan finishes #26233 releases it earlier.Arc<TaskContext>, theTaskContextowns theRunState, and theRunStateowns the future. While the body is not complete this is a reference cycle. A run that is abandoned before the body completes can keep its memory. This is not confirmed by a test and is not corrected yet.ScalarSubqueryExec::reset_statehas the same in-place reset and is not changed here.Comparison with PostgreSQL and DuckDB. This is what PostgreSQL does: the CTE output is a tuplestore in the executor state of the first
CteScan, not in the plan, so each execution has its own (nodeCtescan.c).Are these changes tested?
Yes. Unit tests: two overlapping runs of one plan have separate outputs,
reset_plan_statesleaves the old plan usable, a renewed context computes only the given CTEs again, and a test forRunState. The recursive cases incte_materialized.sltpass with no change. There is no slt for nested recursion.Are there any user-facing changes?
New public API:
datafusion_execution::RunState,TaskContext::run_state,TaskContext::with_run_state, andTaskContext: Clone. No existing API changes.