adriangb opened a new pull request, #24167:
URL: https://github.com/apache/datafusion/pull/24167

   ## Which issue does this PR close?
   
   <!--
   We generally require a GitHub issue to be filed for all bug fixes and 
enhancements and this helps us generate change logs for our releases. You can 
link an issue to this PR using the GitHub syntax. For example `Closes #123` 
indicates that this PR will close issue #123.
   -->
   
   - Follow-up cleanup for EPIC #23494; does not close an issue on its own.
   
   ## Rationale for this change
   
   EPIC #23494 moved every built-in `ExecutionPlan` off the central 
`downcast_ref`
   chain in `datafusion-proto` onto per-plan hooks: a `try_to_proto` override on
   the `ExecutionPlan` trait and an inherent `FooExec::try_from_proto`, both 
living
   in the plan's own module.
   
   Those hooks currently read plan state through **getters** (`self.expr()`,
   `self.fetch()`, ...). That means adding a field to a plan struct is 
invisible to
   serialization: nothing breaks, nothing warns, the field is just silently
   dropped on round-trip. This is not hypothetical — it has already cost us a 
real
   bug: `HashJoinExec.fetch` is not serialized and is lost on round-trip (being
   fixed separately).
   
   Starting each hook with an *exhaustive* destructure turns that class of 
mistake
   into a compile error:
   
   * **Encode side** — destructure `self` naming every field, no `..`. Add a 
field
     to `SortExec` and `SortExec::try_to_proto` stops compiling until you either
     serialize it or bind it to `_` with a comment saying why it does not need 
to
     be (derived at construction, runtime state, recomputed on decode).
   * **Decode side** — destructure the prost node struct the same way. The
     generated structs are plain, all-`pub` and not `#[non_exhaustive]`, so 
adding
     a field to the `.proto` becomes a compile error in every decoder rather 
than a
     silent omission.
   
   ## What changes are included in this PR?
   
   A mechanical, behavior-preserving refactor of the serde hooks for the core
   (non-join, non-aggregate, non-window) plans in 
`datafusion/physical-plan/src/`:
   
   `SortExec`, `SortPreservingMergeExec`, `GlobalLimitExec`, `LocalLimitExec`,
   `FilterExec`, `ProjectionExec`, `RepartitionExec`, `UnionExec`,
   `InterleaveExec`, `CoalesceBatchesExec`, `CoalescePartitionsExec`,
   `CooperativeExec`, `BufferExec`, `EmptyExec`, `PlaceholderRowExec`,
   `ExplainExec`, `ScalarSubqueryExec`.
   
   **The wire format is unchanged — byte for byte — and no behavior changes.** 
The
   same values are written to and read from the same proto fields; only the way
   they are reached in Rust changes.
   
   Split into four commits, one per group of plans, each of which builds green.
   
   ### Fields the refactor documented as not serialized
   
   Writing the destructures surfaced and pinned down, in comments at the point 
of
   use, which fields do not survive a round-trip today. All of these are
   pre-existing; **none of them are changed here**, because fixing any of them
   means changing the wire format, which is a separate decision:
   
   * `SortPreservingMergeExec::enable_round_robin_repartition` — no field on
     `SortPreservingMergeExecNode`; decoding always restores the `true` default
     from `SortPreservingMergeExec::new`.
   * `GlobalLimitExec::required_ordering` and 
`LocalLimitExec::required_ordering` —
     no field on the limit nodes; they are set by the `enforce_sorting` 
optimizer
     rule, so a decoded plan starts with `None`.
   
   Everything else that binds to `_` is genuinely not plan shape: runtime 
metrics
   (`metrics` / `metrics_set`), execution-time state (`RepartitionExec::state`,
   `ScalarSubqueryExec::subquery_future` and `results`), values derived at
   construction from other serialized fields (`SortExec::common_sort_prefix`), 
the
   positional `ScalarSubqueryLink::index`, and the `cache` / `properties` plan
   properties that are recomputed on decode.
   
   Two small notes for reviewers:
   
   * `RepartitionExec` keeps its output partitioning *inside* `cache`
     (`partitioning()` is `&self.cache.partitioning`), so `cache` is bound 
rather
     than `_`-ed there and is read for the serialized `partitioning` field.
   * `EmptyExec` / `PlaceholderRowExec` now read the encoded partition count 
from
     the `partitions` field instead of going through
     `properties().output_partitioning().partition_count()`. The two are kept in
     sync by `with_partitions`, the only writer besides `new`, so the encoded 
value
     is identical.
   
   ## Are these changes tested?
   
   Covered by the existing round-trip tests, which are the real proof that the 
wire
   format did not move. Locally on this branch:
   
   * `cargo test -p datafusion-proto --test proto_integration` — 214 passed, 0 
failed
   * `cargo test -p datafusion-physical-plan` — 1640 passed, 0 failed
   * `cargo clippy -p datafusion-physical-plan --all-targets --all-features -- 
-D warnings` — clean
   * `cargo fmt --all`
   
   No new tests are added: the change adds no new behavior to test, and the
   compile-error property it introduces is enforced by rustc rather than by a 
test.
   
   ## Are there any user-facing changes?
   
   No. No public API changes, no wire format changes, no behavior changes.
   


-- 
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]

Reply via email to