Skip to content

Commit f6dceb6

Browse files
adriangbclaude
andcommitted
refactor(proto): destructure repartition, union and coalesce plans in serde hooks
Exhaustive destructures for `RepartitionExec`, `UnionExec`, `InterleaveExec`, `CoalesceBatchesExec` and `CoalescePartitionsExec` on both sides. Note `RepartitionExec` keeps its output partitioning inside `cache`, so `cache` is bound (not `_`) and read for the serialized `partitioning` field. Wire format unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent b03c68a commit f6dceb6

4 files changed

Lines changed: 107 additions & 32 deletions

File tree

‎datafusion/physical-plan/src/coalesce_batches.rs‎

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -297,14 +297,26 @@ impl ExecutionPlan for CoalesceBatchesExec {
297297
ctx: &crate::proto::ExecutionPlanEncodeCtx<'_>,
298298
) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
299299
use datafusion_proto_models::protobuf;
300-
let input = ctx.encode_child(self.input())?;
300+
// Destructure exhaustively (no `..`) so that adding a field to
301+
// `CoalesceBatchesExec` is a compile error here until it is either
302+
// serialized or explicitly documented as not needing to be.
303+
let Self {
304+
input,
305+
target_batch_size,
306+
fetch,
307+
// Runtime metrics, not part of the plan shape.
308+
metrics: _,
309+
// Derived plan properties, recomputed on decode.
310+
cache: _,
311+
} = self;
312+
let input = ctx.encode_child(input)?;
301313
Ok(Some(protobuf::PhysicalPlanNode {
302314
physical_plan_type: Some(
303315
protobuf::physical_plan_node::PhysicalPlanType::CoalesceBatches(
304316
Box::new(protobuf::CoalesceBatchesExecNode {
305317
input: Some(Box::new(input)),
306-
target_batch_size: self.target_batch_size() as u32,
307-
fetch: self.fetch().map(|n| n as u32),
318+
target_batch_size: *target_batch_size as u32,
319+
fetch: fetch.map(|n| n as u32),
308320
}),
309321
),
310322
),
@@ -335,14 +347,19 @@ impl CoalesceBatchesExec {
335347
protobuf::physical_plan_node::PhysicalPlanType::CoalesceBatches,
336348
"CoalesceBatchesExec",
337349
);
338-
let input = ctx.decode_required_child(
339-
coalesce_batches.input.as_deref(),
340-
"CoalesceBatchesExec",
341-
"input",
342-
)?;
350+
// Destructure exhaustively so that a new field on
351+
// `CoalesceBatchesExecNode` is a compile error here rather than a
352+
// silently dropped field.
353+
let protobuf::CoalesceBatchesExecNode {
354+
input,
355+
target_batch_size,
356+
fetch,
357+
} = &**coalesce_batches;
358+
let input =
359+
ctx.decode_required_child(input.as_deref(), "CoalesceBatchesExec", "input")?;
343360
Ok(Arc::new(
344-
CoalesceBatchesExec::new(input, coalesce_batches.target_batch_size as usize)
345-
.with_fetch(coalesce_batches.fetch.map(|f| f as usize)),
361+
CoalesceBatchesExec::new(input, *target_batch_size as usize)
362+
.with_fetch(fetch.map(|f| f as usize)),
346363
))
347364
}
348365
}

‎datafusion/physical-plan/src/coalesce_partitions.rs‎

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -353,13 +353,24 @@ impl ExecutionPlan for CoalescePartitionsExec {
353353
ctx: &crate::proto::ExecutionPlanEncodeCtx<'_>,
354354
) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
355355
use datafusion_proto_models::protobuf;
356-
let input = ctx.encode_child(self.input())?;
356+
// Destructure exhaustively (no `..`) so that adding a field to
357+
// `CoalescePartitionsExec` is a compile error here until it is either
358+
// serialized or explicitly documented as not needing to be.
359+
let Self {
360+
input,
361+
// Runtime metrics, not part of the plan shape.
362+
metrics: _,
363+
// Derived plan properties, recomputed on decode.
364+
cache: _,
365+
fetch,
366+
} = self;
367+
let input = ctx.encode_child(input)?;
357368
Ok(Some(protobuf::PhysicalPlanNode {
358369
physical_plan_type: Some(
359370
protobuf::physical_plan_node::PhysicalPlanType::Merge(Box::new(
360371
protobuf::CoalescePartitionsExecNode {
361372
input: Some(Box::new(input)),
362-
fetch: self.fetch().map(|f| f as u32),
373+
fetch: fetch.map(|f| f as u32),
363374
},
364375
)),
365376
),
@@ -386,14 +397,17 @@ impl CoalescePartitionsExec {
386397
protobuf::physical_plan_node::PhysicalPlanType::Merge,
387398
"CoalescePartitionsExec",
388399
);
400+
// Destructure exhaustively so that a new field on
401+
// `CoalescePartitionsExecNode` is a compile error here rather than a
402+
// silently dropped field.
403+
let protobuf::CoalescePartitionsExecNode { input, fetch } = &**merge;
389404
let input = ctx.decode_required_child(
390-
merge.input.as_deref(),
405+
input.as_deref(),
391406
"CoalescePartitionsExec",
392407
"input",
393408
)?;
394409
Ok(Arc::new(
395-
CoalescePartitionsExec::new(input)
396-
.with_fetch(merge.fetch.map(|f| f as usize)),
410+
CoalescePartitionsExec::new(input).with_fetch(fetch.map(|f| f as usize)),
397411
))
398412
}
399413
}

‎datafusion/physical-plan/src/repartition/mod.rs‎

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,17 +1711,33 @@ impl ExecutionPlan for RepartitionExec {
17111711
) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
17121712
use datafusion_proto_models::protobuf;
17131713

1714-
let input = ctx.encode_child(self.input())?;
1714+
// Destructure exhaustively (no `..`) so that adding a field to
1715+
// `RepartitionExec` is a compile error here until it is either
1716+
// serialized or explicitly documented as not needing to be.
1717+
let Self {
1718+
input,
1719+
// Execution-time channel state, created on `execute()`.
1720+
state: _,
1721+
// Runtime metrics, not part of the plan shape.
1722+
metrics: _,
1723+
preserve_order,
1724+
// Derived plan properties. The output partitioning lives here (it is
1725+
// the plan's own `partitioning`) and *is* serialized below; the rest
1726+
// is recomputed on decode.
1727+
cache,
1728+
} = self;
17151729

1716-
let partitioning = self.partitioning().try_to_proto(&ctx.expr_ctx())?;
1730+
let input = ctx.encode_child(input)?;
1731+
1732+
let partitioning = cache.partitioning.try_to_proto(&ctx.expr_ctx())?;
17171733

17181734
Ok(Some(protobuf::PhysicalPlanNode {
17191735
physical_plan_type: Some(
17201736
protobuf::physical_plan_node::PhysicalPlanType::Repartition(Box::new(
17211737
protobuf::RepartitionExecNode {
17221738
input: Some(Box::new(input)),
17231739
partitioning: Some(partitioning),
1724-
preserve_order: self.preserve_order(),
1740+
preserve_order: *preserve_order,
17251741
},
17261742
)),
17271743
),
@@ -1743,15 +1759,19 @@ impl RepartitionExec {
17431759
protobuf::physical_plan_node::PhysicalPlanType::Repartition,
17441760
"RepartitionExec",
17451761
);
1746-
let input = ctx.decode_required_child(
1747-
repart.input.as_deref(),
1748-
"RepartitionExec",
1749-
"input",
1750-
)?;
1762+
// Destructure exhaustively so that a new field on
1763+
// `RepartitionExecNode` is a compile error here rather than a silently
1764+
// dropped field.
1765+
let protobuf::RepartitionExecNode {
1766+
input,
1767+
partitioning,
1768+
preserve_order,
1769+
} = &**repart;
1770+
let input =
1771+
ctx.decode_required_child(input.as_deref(), "RepartitionExec", "input")?;
17511772
let input_schema = input.schema();
17521773

1753-
let partitioning = repart
1754-
.partitioning
1774+
let partitioning = partitioning
17551775
.as_ref()
17561776
.map(|partitioning| {
17571777
Partitioning::try_from_proto(
@@ -1768,7 +1788,7 @@ impl RepartitionExec {
17681788
})?;
17691789

17701790
let mut repart_exec = RepartitionExec::try_new(input, partitioning)?;
1771-
if repart.preserve_order {
1791+
if *preserve_order {
17721792
repart_exec = repart_exec.with_preserve_order();
17731793
}
17741794
Ok(Arc::new(repart_exec))

‎datafusion/physical-plan/src/union.rs‎

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,17 @@ impl ExecutionPlan for UnionExec {
558558
ctx: &crate::proto::ExecutionPlanEncodeCtx<'_>,
559559
) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
560560
use datafusion_proto_models::protobuf;
561-
let inputs = ctx.encode_children(self.inputs())?;
561+
// Destructure exhaustively (no `..`) so that adding a field to
562+
// `UnionExec` is a compile error here until it is either serialized or
563+
// explicitly documented as not needing to be.
564+
let Self {
565+
inputs,
566+
// Runtime metrics, not part of the plan shape.
567+
metrics: _,
568+
// Derived plan properties, recomputed on decode.
569+
cache: _,
570+
} = self;
571+
let inputs = ctx.encode_children(inputs)?;
562572
Ok(Some(protobuf::PhysicalPlanNode {
563573
physical_plan_type: Some(
564574
protobuf::physical_plan_node::PhysicalPlanType::Union(
@@ -581,8 +591,10 @@ impl UnionExec {
581591
protobuf::physical_plan_node::PhysicalPlanType::Union,
582592
"UnionExec",
583593
);
584-
let inputs = union
585-
.inputs
594+
// Destructure exhaustively so that a new field on `UnionExecNode` is a
595+
// compile error here rather than a silently dropped field.
596+
let protobuf::UnionExecNode { inputs } = union;
597+
let inputs = inputs
586598
.iter()
587599
.map(|input| ctx.decode_child(input))
588600
.collect::<Result<Vec<_>>>()?;
@@ -805,7 +817,17 @@ impl ExecutionPlan for InterleaveExec {
805817
ctx: &crate::proto::ExecutionPlanEncodeCtx<'_>,
806818
) -> Result<Option<datafusion_proto_models::protobuf::PhysicalPlanNode>> {
807819
use datafusion_proto_models::protobuf;
808-
let inputs = ctx.encode_children(self.inputs())?;
820+
// Destructure exhaustively (no `..`) so that adding a field to
821+
// `InterleaveExec` is a compile error here until it is either
822+
// serialized or explicitly documented as not needing to be.
823+
let Self {
824+
inputs,
825+
// Runtime metrics, not part of the plan shape.
826+
metrics: _,
827+
// Derived plan properties, recomputed on decode.
828+
cache: _,
829+
} = self;
830+
let inputs = ctx.encode_children(inputs)?;
809831
Ok(Some(protobuf::PhysicalPlanNode {
810832
physical_plan_type: Some(
811833
protobuf::physical_plan_node::PhysicalPlanType::Interleave(
@@ -828,8 +850,10 @@ impl InterleaveExec {
828850
protobuf::physical_plan_node::PhysicalPlanType::Interleave,
829851
"InterleaveExec",
830852
);
831-
let inputs = interleave
832-
.inputs
853+
// Destructure exhaustively so that a new field on `InterleaveExecNode`
854+
// is a compile error here rather than a silently dropped field.
855+
let protobuf::InterleaveExecNode { inputs } = interleave;
856+
let inputs = inputs
833857
.iter()
834858
.map(|input| ctx.decode_child(input))
835859
.collect::<Result<Vec<_>>>()?;

0 commit comments

Comments
 (0)