fix(proto): honor ExecutionPlan downcast_delegate during serialization#23154
Merged
gabotechs merged 1 commit intoJun 24, 2026
Merged
Conversation
Physical plan proto serialization was still binding the plan as dyn Any after the as_any removal. That bypassed ExecutionPlan::downcast_ref, so wrapper plans that delegate their public downcast identity fell through to the extension codec instead of serializing as the wrapped built-in plan. Bind the serializer view as dyn ExecutionPlan so the existing downcast chain uses the delegating helper, and add a regression test with a wrapper around EmptyExec.
46286ae to
401d8fc
Compare
gabotechs
approved these changes
Jun 24, 2026
gabotechs
left a comment
Contributor
There was a problem hiding this comment.
👍 nice catch @geoffreyclaude!
Contributor
Author
All the credit goes to @fred1268 , I just fed his findings to Codex 🫠 |
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?
N/A. This is a follow-up to #22559 and #21263, but there is no dedicated issue for this specific bug.
Rationale for this change
#21263 removed
ExecutionPlan::as_any()now thatExecutionPlanhasAnyas a supertrait. Most call sites were migrated from code like this:to the new helper:
That distinction matters after #22559.
ExecutionPlan::downcast_refis now the public downcast operation for execution plans: it followsExecutionPlan::downcast_delegate()before falling back to rawAny. This lets wrapper plans keep their own concrete type private while still presenting the wrapped plan's normal public identity.For example, tracing or instrumentation wrappers can implement
downcast_delegate()so callers that ask "is this aFilterExec/EmptyExec/ProjectionExec?" get the same answer they would have received without the wrapper. The wrapper remains visible only to code that intentionally performs a raw concrete-typeAnycheck.This problem came up while trying to instrument distributed plans through
datafusion-distributed. In that setting, physical plans may be wrapped for tracing/instrumentation and then serialized for distributed execution. The wrapper is meant to be transparent for normal plan inspection, but serialization still needs to recognize the delegated built-in plan underneath it.The physical plan proto serializer still had one leftover mechanical migration from the old
as_any()world:That line bypasses
ExecutionPlan::downcast_refentirely. As a result, a delegating wrapper around a built-in execution plan is not serialized as the built-in plan. Instead, the serializer sees only the wrapper's concrete type, fails all built-inExecchecks, and falls through to the extension codec. With the default extension codec this produces an unsupported-plan error, even though the wrapped plan itself is serializable.This is particularly relevant for transparent wrappers such as
InstrumentedExecindatafusion-contrib/datafusion-tracing, which intentionally delegate public downcasts to the wrapped plan.What changes are included in this PR?
&dyn ExecutionPlaninstead of&dyn Any.plan.downcast_ref::<...>()now dispatches through theExecutionPlanhelper and therefore honorsdowncast_delegate().EmptyExec.as_ref() as &dyn Anypatterns. The remaining hits are either non-ExecutionPlantraits,PhysicalExprchecks, UDF tests, or the intentional rawAnyfallback inside theExecutionPlanhelper itself.Are these changes tested?
Yes.
cargo fmt --all cargo fmt --all --check cargo test -p datafusion-proto --test proto_integration serialize_uses_downcast_delegate cargo clippy --all-targets --all-features -- -D warningsAre there any user-facing changes?
Yes, as a bug fix. Physical plan proto serialization now honors the documented
ExecutionPlan::downcast_delegate()behavior. Transparent wrapper plans can serialize as their delegated built-in execution plan when appropriate instead of requiring an extension codec for the wrapper itself.