Skip to content

Commit dd8760d

Browse files
timsaucerclaude
andauthored
chore: remove as_any from ExecutionPlan (#21263)
## Which issue does this PR close? This is a follow on to #20812 and #21209 but treats `ExecutionPlan`. ## Rationale for this change This PR reduces the amount of boilerplate code that users need to write for execution plans. ## What changes are included in this PR? Now that we have [trait upcasting](https://blog.rust-lang.org/2025/04/03/Rust-1.86.0/) since rust 1.86, we no longer need every implementation of these functions to have the as_any function that returns &self. This PR makes Any an supertrait and makes the appropriate casts when necessary. I have also implemented functions `is` and `downcast_ref` on the trait object for ExecutionPlan and applied this same pattern to the udf, udaf, and udwf implementations. This allows for a clean downcasting and type checking. ## Are these changes tested? Existing unit tests. ## Are there any user-facing changes? Yes, the users simply need to remove the `as_any` function. The upgrade guide is updated. --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a89b527 commit dd8760d

106 files changed

Lines changed: 403 additions & 773 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

datafusion-examples/examples/custom_data_source/adapter_serialization.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ impl PhysicalProtoConverterExtension for AdapterPreservingCodec {
317317
extension_codec: &dyn PhysicalExtensionCodec,
318318
) -> Result<PhysicalPlanNode> {
319319
// Check if this is a DataSourceExec with adapter
320-
if let Some(exec) = plan.as_any().downcast_ref::<DataSourceExec>()
320+
if let Some(exec) = plan.downcast_ref::<DataSourceExec>()
321321
&& let Some(config) =
322322
exec.data_source().as_any().downcast_ref::<FileScanConfig>()
323323
&& let Some(adapter_factory) = &config.expr_adapter_factory
@@ -481,7 +481,7 @@ fn inject_adapter_into_plan(
481481
plan: Arc<dyn ExecutionPlan>,
482482
adapter_factory: Arc<dyn PhysicalExprAdapterFactory>,
483483
) -> Result<Arc<dyn ExecutionPlan>> {
484-
if let Some(exec) = plan.as_any().downcast_ref::<DataSourceExec>()
484+
if let Some(exec) = plan.downcast_ref::<DataSourceExec>()
485485
&& let Some(config) = exec.data_source().as_any().downcast_ref::<FileScanConfig>()
486486
{
487487
let new_config = FileScanConfigBuilder::from(config.clone())
@@ -497,7 +497,7 @@ fn inject_adapter_into_plan(
497497
fn verify_adapter_in_plan(plan: &Arc<dyn ExecutionPlan>, label: &str) -> bool {
498498
// Walk the plan tree to find DataSourceExec with adapter
499499
fn check_plan(plan: &dyn ExecutionPlan) -> bool {
500-
if let Some(exec) = plan.as_any().downcast_ref::<DataSourceExec>()
500+
if let Some(exec) = plan.downcast_ref::<DataSourceExec>()
501501
&& let Some(config) =
502502
exec.data_source().as_any().downcast_ref::<FileScanConfig>()
503503
&& config.expr_adapter_factory.is_some()

datafusion-examples/examples/custom_data_source/custom_datasource.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,6 @@ impl ExecutionPlan for CustomExec {
235235
"CustomExec"
236236
}
237237

238-
fn as_any(&self) -> &dyn Any {
239-
self
240-
}
241-
242238
fn properties(&self) -> &Arc<PlanProperties> {
243239
&self.cache
244240
}

datafusion-examples/examples/data_io/parquet_exec_visitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ impl ExecutionPlanVisitor for ParquetExecVisitor {
104104
/// or `post_visit` (visit each node after its children/inputs)
105105
fn pre_visit(&mut self, plan: &dyn ExecutionPlan) -> Result<bool, Self::Error> {
106106
// If needed match on a specific `ExecutionPlan` node type
107-
if let Some(data_source_exec) = plan.as_any().downcast_ref::<DataSourceExec>()
107+
if let Some(data_source_exec) = plan.downcast_ref::<DataSourceExec>()
108108
&& let Some((file_config, _)) =
109109
data_source_exec.downcast_to_file_source::<ParquetSource>()
110110
{

datafusion-examples/examples/execution_monitoring/memory_pool_execution_plan.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ use datafusion::physical_plan::{
4343
};
4444
use datafusion::prelude::*;
4545
use futures::stream::{StreamExt, TryStreamExt};
46-
use std::any::Any;
4746
use std::fmt;
4847
use std::sync::Arc;
4948

@@ -226,10 +225,6 @@ impl ExecutionPlan for BufferingExecutionPlan {
226225
"BufferingExecutionPlan"
227226
}
228227

229-
fn as_any(&self) -> &dyn Any {
230-
self
231-
}
232-
233228
fn schema(&self) -> SchemaRef {
234229
self.schema.clone()
235230
}

datafusion-examples/examples/proto/composed_extension_codec.rs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
//! DeltaScan
3333
//! ```
3434
35-
use std::any::Any;
3635
use std::fmt::Debug;
3736
use std::sync::Arc;
3837

@@ -103,10 +102,6 @@ impl ExecutionPlan for ParentExec {
103102
"ParentExec"
104103
}
105104

106-
fn as_any(&self) -> &dyn Any {
107-
self
108-
}
109-
110105
fn properties(&self) -> &Arc<datafusion::physical_plan::PlanProperties> {
111106
unreachable!()
112107
}
@@ -161,7 +156,7 @@ impl PhysicalExtensionCodec for ParentPhysicalExtensionCodec {
161156
}
162157

163158
fn try_encode(&self, node: Arc<dyn ExecutionPlan>, buf: &mut Vec<u8>) -> Result<()> {
164-
if node.as_any().downcast_ref::<ParentExec>().is_some() {
159+
if node.is::<ParentExec>() {
165160
buf.extend_from_slice("ParentExec".as_bytes());
166161
Ok(())
167162
} else {
@@ -188,10 +183,6 @@ impl ExecutionPlan for ChildExec {
188183
"ChildExec"
189184
}
190185

191-
fn as_any(&self) -> &dyn Any {
192-
self
193-
}
194-
195186
fn properties(&self) -> &Arc<datafusion::physical_plan::PlanProperties> {
196187
unreachable!()
197188
}
@@ -244,7 +235,7 @@ impl PhysicalExtensionCodec for ChildPhysicalExtensionCodec {
244235
}
245236

246237
fn try_encode(&self, node: Arc<dyn ExecutionPlan>, buf: &mut Vec<u8>) -> Result<()> {
247-
if node.as_any().downcast_ref::<ChildExec>().is_some() {
238+
if node.is::<ChildExec>() {
248239
buf.extend_from_slice("ChildExec".as_bytes());
249240
Ok(())
250241
} else {

datafusion-examples/examples/proto/expression_deduplication.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ pub async fn expression_deduplication() -> Result<()> {
124124

125125
// Step 5: check that we deduplicated expressions
126126
println!("Step 5: Checking for deduplicated expressions...");
127-
let Some(filter_exec) = deserialized_plan.as_any().downcast_ref::<FilterExec>()
128-
else {
127+
let Some(filter_exec) = deserialized_plan.downcast_ref::<FilterExec>() else {
129128
panic!("Deserialized plan is not a FilterExec");
130129
};
131130
let predicate = Arc::clone(filter_exec.predicate());

datafusion-examples/examples/relation_planner/table_sample.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
//! ```
8181
8282
use std::{
83-
any::Any,
8483
fmt::{self, Debug, Formatter},
8584
hash::{Hash, Hasher},
8685
pin::Pin,
@@ -682,10 +681,6 @@ impl ExecutionPlan for SampleExec {
682681
"SampleExec"
683682
}
684683

685-
fn as_any(&self) -> &dyn Any {
686-
self
687-
}
688-
689684
fn properties(&self) -> &Arc<PlanProperties> {
690685
&self.cache
691686
}

datafusion/catalog/src/memory/table.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -594,10 +594,6 @@ impl ExecutionPlan for DmlResultExec {
594594
"DmlResultExec"
595595
}
596596

597-
fn as_any(&self) -> &dyn Any {
598-
self
599-
}
600-
601597
fn schema(&self) -> SchemaRef {
602598
Arc::clone(&self.schema)
603599
}

datafusion/core/src/datasource/listing/table.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ impl ListingTableConfigExt for ListingTableConfig {
107107

108108
#[cfg(test)]
109109
mod tests {
110+
110111
#[cfg(feature = "parquet")]
111112
use crate::datasource::file_format::parquet::ParquetFormat;
112113
use crate::datasource::listing::table::ListingTableConfigExt;
@@ -404,7 +405,7 @@ mod tests {
404405
.await
405406
.expect("Empty execution plan");
406407

407-
assert!(scan.as_any().is::<EmptyExec>());
408+
assert!(scan.is::<EmptyExec>());
408409
assert_eq!(
409410
columns(&scan.schema()),
410411
vec!["a".to_owned(), "p1".to_owned()]

datafusion/core/src/physical_planner.rs

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3632,7 +3632,6 @@ mod tests {
36323632

36333633
let execution_plan = plan(&logical_plan).await?;
36343634
let final_hash_agg = execution_plan
3635-
.as_any()
36363635
.downcast_ref::<AggregateExec>()
36373636
.expect("hash aggregate");
36383637
assert_eq!(
@@ -3660,7 +3659,6 @@ mod tests {
36603659

36613660
let execution_plan = plan(&logical_plan).await?;
36623661
let final_hash_agg = execution_plan
3663-
.as_any()
36643662
.downcast_ref::<AggregateExec>()
36653663
.expect("hash aggregate");
36663664
assert_eq!(
@@ -3795,7 +3793,7 @@ mod tests {
37953793
.unwrap();
37963794

37973795
let plan = plan(&logical_plan).await.unwrap();
3798-
if let Some(plan) = plan.as_any().downcast_ref::<ExplainExec>() {
3796+
if let Some(plan) = plan.downcast_ref::<ExplainExec>() {
37993797
let stringified_plans = plan.stringified_plans();
38003798
assert!(stringified_plans.len() >= 4);
38013799
assert!(
@@ -3863,7 +3861,7 @@ mod tests {
38633861
.handle_explain(&explain, &ctx.state())
38643862
.await
38653863
.unwrap();
3866-
if let Some(plan) = plan.as_any().downcast_ref::<ExplainExec>() {
3864+
if let Some(plan) = plan.downcast_ref::<ExplainExec>() {
38673865
let stringified_plans = plan.stringified_plans();
38683866
assert_eq!(stringified_plans.len(), 1);
38693867
assert_eq!(stringified_plans[0].plan.as_str(), "Test Err");
@@ -4003,10 +4001,6 @@ mod tests {
40034001
}
40044002

40054003
/// Return a reference to Any that can be used for downcasting
4006-
fn as_any(&self) -> &dyn Any {
4007-
self
4008-
}
4009-
40104004
fn properties(&self) -> &Arc<PlanProperties> {
40114005
&self.cache
40124006
}
@@ -4169,9 +4163,6 @@ digraph {
41694163
fn schema(&self) -> SchemaRef {
41704164
Arc::new(Schema::empty())
41714165
}
4172-
fn as_any(&self) -> &dyn Any {
4173-
unimplemented!()
4174-
}
41754166
fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
41764167
self.0.iter().collect::<Vec<_>>()
41774168
}
@@ -4224,9 +4215,6 @@ digraph {
42244215
) -> Result<Arc<dyn ExecutionPlan>> {
42254216
unimplemented!()
42264217
}
4227-
fn as_any(&self) -> &dyn Any {
4228-
unimplemented!()
4229-
}
42304218
fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
42314219
unimplemented!()
42324220
}
@@ -4351,9 +4339,6 @@ digraph {
43514339
) -> Result<Arc<dyn ExecutionPlan>> {
43524340
unimplemented!()
43534341
}
4354-
fn as_any(&self) -> &dyn Any {
4355-
unimplemented!()
4356-
}
43574342
fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
43584343
vec![]
43594344
}
@@ -4765,6 +4750,6 @@ digraph {
47654750
.unwrap();
47664751

47674752
assert_eq!(plan.schema(), schema);
4768-
assert!(plan.as_any().is::<EmptyExec>());
4753+
assert!(plan.is::<EmptyExec>());
47694754
}
47704755
}

0 commit comments

Comments
 (0)