1use std::borrow::Cow;
21use std::collections::{HashMap, HashSet};
22use std::sync::Arc;
23
24use crate::datasource::file_format::file_type_to_format;
25use crate::datasource::listing::ListingTableUrl;
26use crate::datasource::physical_plan::{FileOutputMode, FileSinkConfig};
27use crate::datasource::{DefaultTableSource, source_as_provider};
28use crate::error::{DataFusionError, Result};
29use crate::execution::context::{ExecutionProps, SessionState};
30use crate::logical_expr::utils::generate_sort_key;
31use crate::logical_expr::{
32 Aggregate, EmptyRelation, Join, Projection, Sort, TableScan, Unnest, Values, Window,
33};
34use crate::logical_expr::{
35 Expr, LogicalPlan, Partitioning as LogicalPartitioning, PlanType, Repartition,
36 UserDefinedLogicalNode,
37};
38use crate::physical_expr::{create_physical_expr, create_physical_exprs};
39use crate::physical_plan::aggregates::{AggregateExec, AggregateMode, PhysicalGroupBy};
40use crate::physical_plan::analyze::AnalyzeExec;
41use crate::physical_plan::explain::ExplainExec;
42use crate::physical_plan::filter::FilterExecBuilder;
43use crate::physical_plan::joins::utils as join_utils;
44use crate::physical_plan::joins::{
45 CrossJoinExec, HashJoinExec, NestedLoopJoinExec, PartitionMode, SortMergeJoinExec,
46};
47use crate::physical_plan::limit::{GlobalLimitExec, LocalLimitExec};
48use crate::physical_plan::projection::{ProjectionExec, ProjectionExpr};
49use crate::physical_plan::repartition::RepartitionExec;
50use crate::physical_plan::sorts::sort::SortExec;
51use crate::physical_plan::union::UnionExec;
52use crate::physical_plan::unnest::UnnestExec;
53use crate::physical_plan::windows::{BoundedWindowAggExec, WindowAggExec};
54use crate::physical_plan::{
55 ExecutionPlan, ExecutionPlanProperties, InputOrderMode, Partitioning, PhysicalExpr,
56 WindowExpr, displayable, windows,
57};
58use crate::schema_equivalence::schema_satisfied_by;
59
60use arrow::array::{RecordBatch, builder::StringBuilder};
61use arrow::compute::SortOptions;
62use arrow::datatypes::Schema;
63use arrow_schema::Field;
64use datafusion_catalog::ScanArgs;
65use datafusion_common::Column;
66use datafusion_common::display::ToStringifiedPlan;
67use datafusion_common::format::ExplainAnalyzeLevel;
68use datafusion_common::tree_node::{
69 Transformed, TreeNode, TreeNodeRecursion, TreeNodeVisitor,
70};
71use datafusion_common::{
72 DFSchema, DFSchemaRef, ScalarValue, exec_err, internal_datafusion_err, internal_err,
73 not_impl_err, plan_err,
74};
75use datafusion_common::{
76 TableReference, assert_eq_or_internal_err, assert_or_internal_err,
77};
78use datafusion_datasource::file_groups::FileGroup;
79use datafusion_datasource::memory::MemorySourceConfig;
80use datafusion_expr::dml::{CopyTo, InsertOp};
81use datafusion_expr::expr::{
82 AggregateFunction, AggregateFunctionParams, Alias, GroupingSet, NullTreatment,
83 WindowFunction, WindowFunctionParams, physical_name,
84};
85use datafusion_expr::expr_rewriter::unnormalize_cols;
86use datafusion_expr::logical_plan::builder::wrap_projection_for_join_if_necessary;
87use datafusion_expr::utils::{expr_to_columns, split_conjunction};
88use datafusion_expr::{
89 Analyze, BinaryExpr, DescribeTable, DmlStatement, Explain, ExplainFormat, Extension,
90 FetchType, Filter, JoinType, Operator, RecursiveQuery, SkipType, StringifiedPlan,
91 WindowFrame, WindowFrameBound, WriteOp,
92};
93use datafusion_physical_expr::aggregate::{AggregateExprBuilder, AggregateFunctionExpr};
94use datafusion_physical_expr::expressions::Literal;
95use datafusion_physical_expr::{
96 LexOrdering, PhysicalSortExpr, create_physical_sort_exprs,
97};
98use datafusion_physical_optimizer::PhysicalOptimizerRule;
99use datafusion_physical_plan::empty::EmptyExec;
100use datafusion_physical_plan::execution_plan::InvariantLevel;
101use datafusion_physical_plan::joins::PiecewiseMergeJoinExec;
102use datafusion_physical_plan::metrics::MetricType;
103use datafusion_physical_plan::placeholder_row::PlaceholderRowExec;
104use datafusion_physical_plan::recursive_query::RecursiveQueryExec;
105use datafusion_physical_plan::unnest::ListUnnest;
106
107use async_trait::async_trait;
108use datafusion_physical_plan::async_func::{AsyncFuncExec, AsyncMapper};
109use futures::{StreamExt, TryStreamExt};
110use itertools::{Itertools, multiunzip};
111use log::debug;
112use tokio::sync::Mutex;
113
114#[async_trait]
117pub trait PhysicalPlanner: Send + Sync {
118 async fn create_physical_plan(
120 &self,
121 logical_plan: &LogicalPlan,
122 session_state: &SessionState,
123 ) -> Result<Arc<dyn ExecutionPlan>>;
124
125 fn create_physical_expr(
132 &self,
133 expr: &Expr,
134 input_dfschema: &DFSchema,
135 session_state: &SessionState,
136 ) -> Result<Arc<dyn PhysicalExpr>>;
137}
138
139#[async_trait]
141pub trait ExtensionPlanner {
142 async fn plan_extension(
153 &self,
154 planner: &dyn PhysicalPlanner,
155 node: &dyn UserDefinedLogicalNode,
156 logical_inputs: &[&LogicalPlan],
157 physical_inputs: &[Arc<dyn ExecutionPlan>],
158 session_state: &SessionState,
159 ) -> Result<Option<Arc<dyn ExecutionPlan>>>;
160
161 async fn plan_table_scan(
227 &self,
228 _planner: &dyn PhysicalPlanner,
229 _scan: &TableScan,
230 _session_state: &SessionState,
231 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
232 Ok(None)
233 }
234}
235
236#[derive(Default)]
255pub struct DefaultPhysicalPlanner {
256 extension_planners: Vec<Arc<dyn ExtensionPlanner + Send + Sync>>,
257}
258
259#[async_trait]
260impl PhysicalPlanner for DefaultPhysicalPlanner {
261 async fn create_physical_plan(
263 &self,
264 logical_plan: &LogicalPlan,
265 session_state: &SessionState,
266 ) -> Result<Arc<dyn ExecutionPlan>> {
267 if let Some(plan) = self
268 .handle_explain_or_analyze(logical_plan, session_state)
269 .await?
270 {
271 return Ok(plan);
272 }
273 let plan = self
274 .create_initial_plan(logical_plan, session_state)
275 .await?;
276
277 self.optimize_physical_plan(plan, session_state, |_, _| {})
278 }
279
280 fn create_physical_expr(
287 &self,
288 expr: &Expr,
289 input_dfschema: &DFSchema,
290 session_state: &SessionState,
291 ) -> Result<Arc<dyn PhysicalExpr>> {
292 create_physical_expr(expr, input_dfschema, session_state.execution_props())
293 }
294}
295
296#[derive(Debug)]
297struct ExecutionPlanChild {
298 index: usize,
301 plan: Arc<dyn ExecutionPlan>,
302}
303
304#[derive(Debug)]
305enum NodeState {
306 ZeroOrOneChild,
307 TwoOrMoreChildren(Mutex<Vec<ExecutionPlanChild>>),
311}
312
313enum ChildrenContainer {
316 None,
317 One(Arc<dyn ExecutionPlan>),
318 Multiple(Vec<Arc<dyn ExecutionPlan>>),
319}
320
321impl ChildrenContainer {
322 fn one(self) -> Result<Arc<dyn ExecutionPlan>> {
323 match self {
324 Self::One(p) => Ok(p),
325 _ => internal_err!("More than one child in ChildrenContainer"),
326 }
327 }
328
329 fn two(self) -> Result<[Arc<dyn ExecutionPlan>; 2]> {
330 match self {
331 Self::Multiple(v) if v.len() == 2 => Ok(v.try_into().unwrap()),
332 _ => internal_err!("ChildrenContainer doesn't contain exactly 2 children"),
333 }
334 }
335
336 fn vec(self) -> Vec<Arc<dyn ExecutionPlan>> {
337 match self {
338 Self::None => vec![],
339 Self::One(p) => vec![p],
340 Self::Multiple(v) => v,
341 }
342 }
343}
344
345#[derive(Debug)]
346struct LogicalNode<'a> {
347 node: &'a LogicalPlan,
348 parent_index: Option<usize>,
350 state: NodeState,
351}
352
353impl DefaultPhysicalPlanner {
354 pub fn with_extension_planners(
360 extension_planners: Vec<Arc<dyn ExtensionPlanner + Send + Sync>>,
361 ) -> Self {
362 Self { extension_planners }
363 }
364
365 fn ensure_schema_matches(
366 &self,
367 logical_schema: &DFSchemaRef,
368 physical_plan: &Arc<dyn ExecutionPlan>,
369 context: &str,
370 ) -> Result<()> {
371 if !logical_schema.matches_arrow_schema(&physical_plan.schema()) {
372 return plan_err!(
373 "{} created an ExecutionPlan with mismatched schema. \
374 LogicalPlan schema: {:?}, ExecutionPlan schema: {:?}",
375 context,
376 logical_schema,
377 physical_plan.schema()
378 );
379 }
380 Ok(())
381 }
382
383 async fn create_initial_plan(
385 &self,
386 logical_plan: &LogicalPlan,
387 session_state: &SessionState,
388 ) -> Result<Arc<dyn ExecutionPlan>> {
389 let mut flat_tree = vec![];
396 let mut dfs_visit_stack = vec![(None, logical_plan)];
397 let mut flat_tree_leaf_indices = vec![];
400 while let Some((parent_index, node)) = dfs_visit_stack.pop() {
401 let current_index = flat_tree.len();
402 dfs_visit_stack
406 .extend(node.inputs().iter().map(|&n| (Some(current_index), n)));
407 let state = match node.inputs().len() {
408 0 => {
409 flat_tree_leaf_indices.push(current_index);
410 NodeState::ZeroOrOneChild
411 }
412 1 => NodeState::ZeroOrOneChild,
413 _ => {
414 let ready_children = Vec::with_capacity(node.inputs().len());
415 let ready_children = Mutex::new(ready_children);
416 NodeState::TwoOrMoreChildren(ready_children)
417 }
418 };
419 let node = LogicalNode {
420 node,
421 parent_index,
422 state,
423 };
424 flat_tree.push(node);
425 }
426 let flat_tree = Arc::new(flat_tree);
427
428 let planning_concurrency = session_state
429 .config_options()
430 .execution
431 .planning_concurrency;
432 let max_concurrency = planning_concurrency.min(flat_tree_leaf_indices.len());
436
437 let tasks = flat_tree_leaf_indices
439 .into_iter()
440 .map(|index| self.task_helper(index, Arc::clone(&flat_tree), session_state));
441 let mut outputs = futures::stream::iter(tasks)
442 .buffer_unordered(max_concurrency)
443 .try_collect::<Vec<_>>()
444 .await?
445 .into_iter()
446 .flatten()
447 .collect::<Vec<_>>();
448 assert_eq_or_internal_err!(
450 outputs.len(),
451 1,
452 "Failed to convert LogicalPlan to ExecutionPlan: More than one root detected"
453 );
454 let plan = outputs.pop().unwrap();
455 Ok(plan)
456 }
457
458 async fn task_helper<'a>(
464 &'a self,
465 leaf_starter_index: usize,
466 flat_tree: Arc<Vec<LogicalNode<'a>>>,
467 session_state: &'a SessionState,
468 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
469 let mut node = flat_tree.get(leaf_starter_index).ok_or_else(|| {
471 internal_datafusion_err!(
472 "Invalid index whilst creating initial physical plan"
473 )
474 })?;
475 let mut plan = self
476 .map_logical_node_to_physical(
477 node.node,
478 session_state,
479 ChildrenContainer::None,
480 )
481 .await?;
482 let mut current_index = leaf_starter_index;
483 while let Some(parent_index) = node.parent_index {
485 node = flat_tree.get(parent_index).ok_or_else(|| {
486 internal_datafusion_err!(
487 "Invalid index whilst creating initial physical plan"
488 )
489 })?;
490 match &node.state {
491 NodeState::ZeroOrOneChild => {
492 plan = self
493 .map_logical_node_to_physical(
494 node.node,
495 session_state,
496 ChildrenContainer::One(plan),
497 )
498 .await?;
499 }
500 NodeState::TwoOrMoreChildren(children) => {
502 let mut children: Vec<ExecutionPlanChild> = {
503 let mut guard = children.lock().await;
504 guard.push(ExecutionPlanChild {
507 index: current_index,
508 plan,
509 });
510 if guard.len() < node.node.inputs().len() {
511 return Ok(None);
514 }
515
516 std::mem::take(guard.as_mut())
521 };
522
523 children.sort_unstable_by_key(|epc| std::cmp::Reverse(epc.index));
529 let children = children.into_iter().map(|epc| epc.plan).collect();
530 let children = ChildrenContainer::Multiple(children);
531 plan = self
532 .map_logical_node_to_physical(node.node, session_state, children)
533 .await?;
534 }
535 }
536 current_index = parent_index;
537 }
538 Ok(Some(plan))
540 }
541
542 async fn map_logical_node_to_physical(
544 &self,
545 node: &LogicalPlan,
546 session_state: &SessionState,
547 children: ChildrenContainer,
548 ) -> Result<Arc<dyn ExecutionPlan>> {
549 let exec_node: Arc<dyn ExecutionPlan> = match node {
550 LogicalPlan::TableScan(scan) => {
552 let TableScan {
553 source,
554 projection,
555 filters,
556 fetch,
557 projected_schema,
558 ..
559 } = scan;
560
561 if let Ok(source) = source_as_provider(source) {
562 let filters = unnormalize_cols(filters.iter().cloned());
566 let filters_vec = filters.into_iter().collect::<Vec<_>>();
567 let opts = ScanArgs::default()
568 .with_projection(projection.as_deref())
569 .with_filters(Some(&filters_vec))
570 .with_limit(*fetch);
571 let res = source.scan_with_args(session_state, opts).await?;
572 Arc::clone(res.plan())
573 } else {
574 let mut maybe_plan = None;
575 for planner in &self.extension_planners {
576 if maybe_plan.is_some() {
577 break;
578 }
579
580 maybe_plan =
581 planner.plan_table_scan(self, scan, session_state).await?;
582 }
583
584 let plan = match maybe_plan {
585 Some(plan) => plan,
586 None => {
587 return plan_err!(
588 "No installed planner was able to plan TableScan for custom TableSource: {:?}",
589 scan.table_name
590 );
591 }
592 };
593 let context =
594 format!("Extension planner for table scan {}", scan.table_name);
595 self.ensure_schema_matches(projected_schema, &plan, &context)?;
596 plan
597 }
598 }
599 LogicalPlan::Values(Values { values, schema }) => {
600 let exprs = values
601 .iter()
602 .map(|row| {
603 row.iter()
604 .map(|expr| {
605 self.create_physical_expr(expr, schema, session_state)
606 })
607 .collect::<Result<Vec<Arc<dyn PhysicalExpr>>>>()
608 })
609 .collect::<Result<Vec<_>>>()?;
610 MemorySourceConfig::try_new_as_values(Arc::clone(schema.inner()), exprs)?
611 as _
612 }
613 LogicalPlan::EmptyRelation(EmptyRelation {
614 produce_one_row: false,
615 schema,
616 }) => Arc::new(EmptyExec::new(Arc::clone(schema.inner()))),
617 LogicalPlan::EmptyRelation(EmptyRelation {
618 produce_one_row: true,
619 schema,
620 }) => Arc::new(PlaceholderRowExec::new(Arc::clone(schema.inner()))),
621 LogicalPlan::DescribeTable(DescribeTable {
622 schema,
623 output_schema,
624 }) => {
625 let output_schema = Arc::clone(output_schema.inner());
626 self.plan_describe(&Arc::clone(schema), output_schema)?
627 }
628
629 LogicalPlan::Copy(CopyTo {
631 input,
632 output_url,
633 file_type,
634 partition_by,
635 options: source_option_tuples,
636 output_schema: _,
637 }) => {
638 let original_url = output_url.clone();
639 let input_exec = children.one()?;
640 let parsed_url = ListingTableUrl::parse(output_url)?;
641 let object_store_url = parsed_url.object_store();
642
643 let schema = Arc::clone(input.schema().inner());
644
645 let table_partition_cols = partition_by
649 .iter()
650 .map(|s| (s.to_string(), arrow::datatypes::DataType::Null))
651 .collect::<Vec<_>>();
652
653 let keep_partition_by_columns = match source_option_tuples
654 .get("execution.keep_partition_by_columns")
655 .map(|v| v.trim())
656 {
657 None => {
658 session_state
659 .config()
660 .options()
661 .execution
662 .keep_partition_by_columns
663 }
664 Some("true") => true,
665 Some("false") => false,
666 Some(value) => {
667 return Err(DataFusionError::Configuration(format!(
668 "provided value for 'execution.keep_partition_by_columns' was not recognized: \"{value}\""
669 )));
670 }
671 };
672
673 let file_output_mode = match source_option_tuples
675 .get("single_file_output")
676 .map(|v| v.trim())
677 {
678 None => FileOutputMode::Automatic,
679 Some("true") => FileOutputMode::SingleFile,
680 Some("false") => FileOutputMode::Directory,
681 Some(value) => {
682 return Err(DataFusionError::Configuration(format!(
683 "provided value for 'single_file_output' was not recognized: \"{value}\""
684 )));
685 }
686 };
687
688 let format_options: HashMap<String, String> = source_option_tuples
690 .iter()
691 .filter(|(k, _)| k.as_str() != "single_file_output")
692 .map(|(k, v)| (k.clone(), v.clone()))
693 .collect();
694
695 let sink_format = file_type_to_format(file_type)?
696 .create(session_state, &format_options)?;
697
698 let file_extension = match sink_format.compression_type() {
700 Some(compression_type) => sink_format
701 .get_ext_with_compression(&compression_type)
702 .unwrap_or_else(|_| sink_format.get_ext()),
703 None => sink_format.get_ext(),
704 };
705
706 let config = FileSinkConfig {
708 original_url,
709 object_store_url,
710 table_paths: vec![parsed_url],
711 file_group: FileGroup::default(),
712 output_schema: schema,
713 table_partition_cols,
714 insert_op: InsertOp::Append,
715 keep_partition_by_columns,
716 file_extension,
717 file_output_mode,
718 };
719
720 let ordering = input_exec.properties().output_ordering().cloned();
721
722 sink_format
723 .create_writer_physical_plan(
724 input_exec,
725 session_state,
726 config,
727 ordering.map(Into::into),
728 )
729 .await?
730 }
731 LogicalPlan::Dml(DmlStatement {
732 target,
733 op: WriteOp::Insert(insert_op),
734 ..
735 }) => {
736 if let Some(provider) =
737 target.as_any().downcast_ref::<DefaultTableSource>()
738 {
739 let input_exec = children.one()?;
740 provider
741 .table_provider
742 .insert_into(session_state, input_exec, *insert_op)
743 .await?
744 } else {
745 return exec_err!(
746 "Table source can't be downcasted to DefaultTableSource"
747 );
748 }
749 }
750 LogicalPlan::Dml(DmlStatement {
751 table_name,
752 target,
753 op: WriteOp::Delete,
754 input,
755 ..
756 }) => {
757 if let Some(provider) =
758 target.as_any().downcast_ref::<DefaultTableSource>()
759 {
760 let filters = extract_dml_filters(input, table_name)?;
761 provider
762 .table_provider
763 .delete_from(session_state, filters)
764 .await
765 .map_err(|e| {
766 e.context(format!("DELETE operation on table '{table_name}'"))
767 })?
768 } else {
769 return exec_err!(
770 "Table source can't be downcasted to DefaultTableSource"
771 );
772 }
773 }
774 LogicalPlan::Dml(DmlStatement {
775 table_name,
776 target,
777 op: WriteOp::Update,
778 input,
779 ..
780 }) => {
781 if let Some(provider) =
782 target.as_any().downcast_ref::<DefaultTableSource>()
783 {
784 let filters = extract_dml_filters(input, table_name)?;
787 let assignments = extract_update_assignments(input)?;
789 provider
790 .table_provider
791 .update(session_state, assignments, filters)
792 .await
793 .map_err(|e| {
794 e.context(format!("UPDATE operation on table '{table_name}'"))
795 })?
796 } else {
797 return exec_err!(
798 "Table source can't be downcasted to DefaultTableSource"
799 );
800 }
801 }
802 LogicalPlan::Dml(DmlStatement {
803 table_name,
804 target,
805 op: WriteOp::Truncate,
806 ..
807 }) => {
808 if let Some(provider) =
809 target.as_any().downcast_ref::<DefaultTableSource>()
810 {
811 provider
812 .table_provider
813 .truncate(session_state)
814 .await
815 .map_err(|e| {
816 e.context(format!(
817 "TRUNCATE operation on table '{table_name}'"
818 ))
819 })?
820 } else {
821 return exec_err!(
822 "Table source can't be downcasted to DefaultTableSource"
823 );
824 }
825 }
826 LogicalPlan::Window(Window { window_expr, .. }) => {
827 assert_or_internal_err!(
828 !window_expr.is_empty(),
829 "Impossibly got empty window expression"
830 );
831
832 let input_exec = children.one()?;
833
834 let get_sort_keys = |expr: &Expr| match expr {
835 Expr::WindowFunction(window_fun) => {
836 let WindowFunctionParams {
837 partition_by,
838 order_by,
839 ..
840 } = &window_fun.as_ref().params;
841 generate_sort_key(partition_by, order_by)
842 }
843 Expr::Alias(Alias { expr, .. }) => {
844 match &**expr {
846 Expr::WindowFunction(window_fun) => {
847 let WindowFunctionParams {
848 partition_by,
849 order_by,
850 ..
851 } = &window_fun.as_ref().params;
852 generate_sort_key(partition_by, order_by)
853 }
854 _ => unreachable!(),
855 }
856 }
857 _ => unreachable!(),
858 };
859 let sort_keys = get_sort_keys(&window_expr[0])?;
860 if window_expr.len() > 1 {
861 debug_assert!(
862 window_expr[1..]
863 .iter()
864 .all(|expr| get_sort_keys(expr).unwrap() == sort_keys),
865 "all window expressions shall have the same sort keys, as guaranteed by logical planning"
866 );
867 }
868
869 let logical_schema = node.schema();
870 let window_expr = window_expr
871 .iter()
872 .map(|e| {
873 create_window_expr(
874 e,
875 logical_schema,
876 session_state.execution_props(),
877 )
878 })
879 .collect::<Result<Vec<_>>>()?;
880
881 let can_repartition = session_state.config().target_partitions() > 1
882 && session_state.config().repartition_window_functions();
883
884 let uses_bounded_memory =
885 window_expr.iter().all(|e| e.uses_bounded_memory());
886 if uses_bounded_memory {
889 Arc::new(BoundedWindowAggExec::try_new(
890 window_expr,
891 input_exec,
892 InputOrderMode::Sorted,
893 can_repartition,
894 )?)
895 } else {
896 Arc::new(WindowAggExec::try_new(
897 window_expr,
898 input_exec,
899 can_repartition,
900 )?)
901 }
902 }
903 LogicalPlan::Aggregate(Aggregate {
904 input,
905 group_expr,
906 aggr_expr,
907 ..
908 }) => {
909 let options = session_state.config().options();
910 let input_exec = children.one()?;
912 let physical_input_schema = input_exec.schema();
913 let logical_input_schema = input.as_ref().schema();
914 let physical_input_schema_from_logical = logical_input_schema.inner();
915
916 if !options.execution.skip_physical_aggregate_schema_check
917 && !schema_satisfied_by(
918 physical_input_schema_from_logical,
919 &physical_input_schema,
920 )
921 {
922 let mut differences = Vec::new();
923
924 if physical_input_schema.metadata()
925 != physical_input_schema_from_logical.metadata()
926 {
927 differences.push(format!(
928 "schema metadata differs: (physical) {:?} vs (logical) {:?}",
929 physical_input_schema.metadata(),
930 physical_input_schema_from_logical.metadata()
931 ));
932 }
933
934 if physical_input_schema.fields().len()
935 != physical_input_schema_from_logical.fields().len()
936 {
937 differences.push(format!(
938 "Different number of fields: (physical) {} vs (logical) {}",
939 physical_input_schema.fields().len(),
940 physical_input_schema_from_logical.fields().len()
941 ));
942 }
943 for (i, (physical_field, logical_field)) in physical_input_schema
944 .fields()
945 .iter()
946 .zip(physical_input_schema_from_logical.fields())
947 .enumerate()
948 {
949 if physical_field.name() != logical_field.name() {
950 differences.push(format!(
951 "field name at index {}: (physical) {} vs (logical) {}",
952 i,
953 physical_field.name(),
954 logical_field.name()
955 ));
956 }
957 if physical_field.data_type() != logical_field.data_type() {
958 differences.push(format!("field data type at index {} [{}]: (physical) {} vs (logical) {}", i, physical_field.name(), physical_field.data_type(), logical_field.data_type()));
959 }
960 if physical_field.is_nullable() && !logical_field.is_nullable() {
961 differences.push(format!("field nullability at index {} [{}]: (physical) {} vs (logical) {}", i, physical_field.name(), physical_field.is_nullable(), logical_field.is_nullable()));
962 }
963 if physical_field.metadata() != logical_field.metadata() {
964 differences.push(format!(
965 "field metadata at index {} [{}]: (physical) {:?} vs (logical) {:?}",
966 i,
967 physical_field.name(),
968 physical_field.metadata(),
969 logical_field.metadata()
970 ));
971 }
972 }
973 return internal_err!(
974 "Physical input schema should be the same as the one converted from logical input schema. Differences: {}",
975 differences.iter().map(|s| format!("\n\t- {s}")).join("")
976 );
977 }
978
979 let groups = self.create_grouping_physical_expr(
980 group_expr,
981 logical_input_schema,
982 &physical_input_schema,
983 session_state,
984 )?;
985
986 let agg_filter = aggr_expr
987 .iter()
988 .map(|e| {
989 create_aggregate_expr_and_maybe_filter(
990 e,
991 logical_input_schema,
992 &physical_input_schema,
993 session_state.execution_props(),
994 )
995 })
996 .collect::<Result<Vec<_>>>()?;
997
998 let (mut aggregates, filters, _order_bys): (Vec<_>, Vec<_>, Vec<_>) =
999 multiunzip(agg_filter);
1000
1001 let mut async_exprs = Vec::new();
1002 let num_input_columns = physical_input_schema.fields().len();
1003
1004 for agg_func in &mut aggregates {
1005 match self.try_plan_async_exprs(
1006 num_input_columns,
1007 PlannedExprResult::Expr(agg_func.expressions()),
1008 physical_input_schema.as_ref(),
1009 )? {
1010 PlanAsyncExpr::Async(
1011 async_map,
1012 PlannedExprResult::Expr(physical_exprs),
1013 ) => {
1014 async_exprs.extend(async_map.async_exprs);
1015
1016 if let Some(new_agg_func) = agg_func.with_new_expressions(
1017 physical_exprs,
1018 agg_func
1019 .order_bys()
1020 .iter()
1021 .cloned()
1022 .map(|x| x.expr)
1023 .collect(),
1024 ) {
1025 *agg_func = Arc::new(new_agg_func);
1026 } else {
1027 return internal_err!("Failed to plan async expression");
1028 }
1029 }
1030 PlanAsyncExpr::Sync(PlannedExprResult::Expr(_)) => {
1031 }
1033 _ => {
1034 return internal_err!(
1035 "Unexpected result from try_plan_async_exprs"
1036 );
1037 }
1038 }
1039 }
1040 let input_exec = if !async_exprs.is_empty() {
1041 Arc::new(AsyncFuncExec::try_new(async_exprs, input_exec)?)
1042 } else {
1043 input_exec
1044 };
1045
1046 let initial_aggr = Arc::new(AggregateExec::try_new(
1047 AggregateMode::Partial,
1048 groups.clone(),
1049 aggregates,
1050 filters.clone(),
1051 input_exec,
1052 Arc::clone(&physical_input_schema),
1053 )?);
1054
1055 let can_repartition = !groups.is_empty()
1056 && session_state.config().target_partitions() > 1
1057 && session_state.config().repartition_aggregations();
1058
1059 let updated_aggregates = initial_aggr.aggr_expr().to_vec();
1065
1066 let next_partition_mode = if can_repartition {
1067 AggregateMode::FinalPartitioned
1069 } else {
1070 AggregateMode::Final
1073 };
1074
1075 let final_grouping_set = initial_aggr.group_expr().as_final();
1076
1077 Arc::new(AggregateExec::try_new(
1078 next_partition_mode,
1079 final_grouping_set,
1080 updated_aggregates,
1081 filters,
1082 initial_aggr,
1083 Arc::clone(&physical_input_schema),
1084 )?)
1085 }
1086 LogicalPlan::Projection(Projection { input, expr, .. }) => self
1087 .create_project_physical_exec(
1088 session_state,
1089 children.one()?,
1090 input,
1091 expr,
1092 )?,
1093 LogicalPlan::Filter(Filter {
1094 predicate, input, ..
1095 }) => {
1096 let physical_input = children.one()?;
1097 let input_dfschema = input.schema();
1098
1099 let runtime_expr =
1100 self.create_physical_expr(predicate, input_dfschema, session_state)?;
1101
1102 let input_schema = input.schema();
1103 let filter = match self.try_plan_async_exprs(
1104 input_schema.fields().len(),
1105 PlannedExprResult::Expr(vec![runtime_expr]),
1106 input_schema.as_arrow(),
1107 )? {
1108 PlanAsyncExpr::Sync(PlannedExprResult::Expr(runtime_expr)) => {
1109 FilterExecBuilder::new(
1110 Arc::clone(&runtime_expr[0]),
1111 physical_input,
1112 )
1113 .with_batch_size(session_state.config().batch_size())
1114 .build()?
1115 }
1116 PlanAsyncExpr::Async(
1117 async_map,
1118 PlannedExprResult::Expr(runtime_expr),
1119 ) => {
1120 let async_exec = AsyncFuncExec::try_new(
1121 async_map.async_exprs,
1122 physical_input,
1123 )?;
1124 FilterExecBuilder::new(
1125 Arc::clone(&runtime_expr[0]),
1126 Arc::new(async_exec),
1127 )
1128 .apply_projection(Some(
1131 (0..input.schema().fields().len()).collect::<Vec<_>>(),
1132 ))?
1133 .with_batch_size(session_state.config().batch_size())
1134 .build()?
1135 }
1136 _ => {
1137 return internal_err!(
1138 "Unexpected result from try_plan_async_exprs"
1139 );
1140 }
1141 };
1142
1143 let selectivity = session_state
1144 .config()
1145 .options()
1146 .optimizer
1147 .default_filter_selectivity;
1148 Arc::new(filter.with_default_selectivity(selectivity)?)
1149 }
1150 LogicalPlan::Repartition(Repartition {
1151 input,
1152 partitioning_scheme,
1153 }) => {
1154 let physical_input = children.one()?;
1155 let input_dfschema = input.as_ref().schema();
1156 let physical_partitioning = match partitioning_scheme {
1157 LogicalPartitioning::RoundRobinBatch(n) => {
1158 Partitioning::RoundRobinBatch(*n)
1159 }
1160 LogicalPartitioning::Hash(expr, n) => {
1161 let runtime_expr = expr
1162 .iter()
1163 .map(|e| {
1164 self.create_physical_expr(
1165 e,
1166 input_dfschema,
1167 session_state,
1168 )
1169 })
1170 .collect::<Result<Vec<_>>>()?;
1171 Partitioning::Hash(runtime_expr, *n)
1172 }
1173 LogicalPartitioning::DistributeBy(_) => {
1174 return not_impl_err!(
1175 "Physical plan does not support DistributeBy partitioning"
1176 );
1177 }
1178 };
1179 Arc::new(RepartitionExec::try_new(
1180 physical_input,
1181 physical_partitioning,
1182 )?)
1183 }
1184 LogicalPlan::Sort(Sort {
1185 expr, input, fetch, ..
1186 }) => {
1187 let physical_input = children.one()?;
1188 let input_dfschema = input.as_ref().schema();
1189 let sort_exprs = create_physical_sort_exprs(
1190 expr,
1191 input_dfschema,
1192 session_state.execution_props(),
1193 )?;
1194 let Some(ordering) = LexOrdering::new(sort_exprs) else {
1195 return internal_err!(
1196 "SortExec requires at least one sort expression"
1197 );
1198 };
1199 let new_sort = SortExec::new(ordering, physical_input).with_fetch(*fetch);
1200 Arc::new(new_sort)
1201 }
1202 LogicalPlan::Subquery(_) => todo!(),
1203 LogicalPlan::SubqueryAlias(_) => children.one()?,
1204 LogicalPlan::Limit(limit) => {
1205 let input = children.one()?;
1206 let SkipType::Literal(skip) = limit.get_skip_type()? else {
1207 return not_impl_err!(
1208 "Unsupported OFFSET expression: {:?}",
1209 limit.skip
1210 );
1211 };
1212 let FetchType::Literal(fetch) = limit.get_fetch_type()? else {
1213 return not_impl_err!(
1214 "Unsupported LIMIT expression: {:?}",
1215 limit.fetch
1216 );
1217 };
1218
1219 let input = if input.output_partitioning().partition_count() == 1 {
1221 input
1222 } else {
1223 if let Some(fetch) = fetch {
1226 Arc::new(LocalLimitExec::new(input, fetch + skip))
1227 } else {
1228 input
1229 }
1230 };
1231
1232 Arc::new(GlobalLimitExec::new(input, skip, fetch))
1233 }
1234 LogicalPlan::Unnest(Unnest {
1235 list_type_columns,
1236 struct_type_columns,
1237 schema,
1238 options,
1239 ..
1240 }) => {
1241 let input = children.one()?;
1242 let schema = Arc::clone(schema.inner());
1243 let list_column_indices = list_type_columns
1244 .iter()
1245 .map(|(index, unnesting)| ListUnnest {
1246 index_in_input_schema: *index,
1247 depth: unnesting.depth,
1248 })
1249 .collect();
1250 Arc::new(UnnestExec::new(
1251 input,
1252 list_column_indices,
1253 struct_type_columns.clone(),
1254 schema,
1255 options.clone(),
1256 )?)
1257 }
1258
1259 LogicalPlan::Join(Join {
1261 left: original_left,
1262 right: original_right,
1263 on: keys,
1264 filter,
1265 join_type,
1266 null_equality,
1267 null_aware,
1268 schema: join_schema,
1269 ..
1270 }) => {
1271 let [physical_left, physical_right] = children.two()?;
1272
1273 let has_expr_join_key = keys.iter().any(|(l, r)| {
1275 !(matches!(l, Expr::Column(_)) && matches!(r, Expr::Column(_)))
1276 });
1277 let (new_logical, physical_left, physical_right) = if has_expr_join_key {
1278 let (left_keys, right_keys): (Vec<_>, Vec<_>) =
1281 keys.iter().cloned().unzip();
1282
1283 let (left, left_col_keys, left_projected) =
1284 wrap_projection_for_join_if_necessary(
1285 &left_keys,
1286 original_left.as_ref().clone(),
1287 )?;
1288 let (right, right_col_keys, right_projected) =
1289 wrap_projection_for_join_if_necessary(
1290 &right_keys,
1291 original_right.as_ref().clone(),
1292 )?;
1293 let column_on = (left_col_keys, right_col_keys);
1294
1295 let left = Arc::new(left);
1296 let right = Arc::new(right);
1297 let (new_join, requalified) = Join::try_new_with_project_input(
1298 node,
1299 Arc::clone(&left),
1300 Arc::clone(&right),
1301 column_on,
1302 )?;
1303
1304 let new_join = LogicalPlan::Join(new_join);
1305
1306 let physical_left = match (left_projected, left.as_ref()) {
1309 (
1311 true,
1312 LogicalPlan::Projection(Projection { input, expr, .. }),
1313 ) => self.create_project_physical_exec(
1314 session_state,
1315 physical_left,
1316 input,
1317 expr,
1318 )?,
1319 _ => physical_left,
1320 };
1321 let physical_right = match (right_projected, right.as_ref()) {
1322 (
1324 true,
1325 LogicalPlan::Projection(Projection { input, expr, .. }),
1326 ) => self.create_project_physical_exec(
1327 session_state,
1328 physical_right,
1329 input,
1330 expr,
1331 )?,
1332 _ => physical_right,
1333 };
1334
1335 if left_projected || right_projected {
1337 let qualified_join_schema = if requalified {
1342 Arc::new(qualify_join_schema_sides(
1343 join_schema,
1344 original_left,
1345 original_right,
1346 )?)
1347 } else {
1348 Arc::clone(join_schema)
1349 };
1350
1351 let final_join_result = qualified_join_schema
1352 .iter()
1353 .map(Expr::from)
1354 .collect::<Vec<_>>();
1355 let projection = LogicalPlan::Projection(Projection::try_new(
1356 final_join_result,
1357 Arc::new(new_join),
1358 )?);
1359 (Cow::Owned(projection), physical_left, physical_right)
1361 } else {
1362 (Cow::Owned(new_join), physical_left, physical_right)
1364 }
1365 } else {
1366 (Cow::Borrowed(node), physical_left, physical_right)
1368 };
1369
1370 let (left, right, keys, new_project) = match new_logical.as_ref() {
1372 LogicalPlan::Projection(Projection { input, expr, .. }) => {
1373 if let LogicalPlan::Join(Join {
1374 left, right, on, ..
1375 }) = input.as_ref()
1376 {
1377 (left, right, on, Some((input, expr)))
1378 } else {
1379 unreachable!()
1380 }
1381 }
1382 LogicalPlan::Join(Join {
1383 left, right, on, ..
1384 }) => (left, right, on, None),
1385 _ => unreachable!(),
1387 };
1388
1389 let left_df_schema = left.schema();
1391 let right_df_schema = right.schema();
1392 let execution_props = session_state.execution_props();
1393 let join_on = keys
1394 .iter()
1395 .map(|(l, r)| {
1396 let l = create_physical_expr(l, left_df_schema, execution_props)?;
1397 let r =
1398 create_physical_expr(r, right_df_schema, execution_props)?;
1399 Ok((l, r))
1400 })
1401 .collect::<Result<join_utils::JoinOn>>()?;
1402
1403 let mut num_range_filters = 0;
1405 let mut range_filters: Vec<Expr> = Vec::new();
1406 let mut total_filters = 0;
1407
1408 let join_filter = match filter {
1409 Some(expr) => {
1410 let split_expr = split_conjunction(expr);
1411 for expr in split_expr.iter() {
1412 match *expr {
1413 Expr::BinaryExpr(BinaryExpr {
1414 left: _,
1415 right: _,
1416 op,
1417 }) => {
1418 if matches!(
1419 op,
1420 Operator::Lt
1421 | Operator::LtEq
1422 | Operator::Gt
1423 | Operator::GtEq
1424 ) {
1425 range_filters.push((**expr).clone());
1426 num_range_filters += 1;
1427 }
1428 total_filters += 1;
1429 }
1430 _ => {
1434 total_filters += 1;
1435 }
1436 }
1437 }
1438
1439 let cols = expr.column_refs();
1441
1442 let left_field_indices = cols
1444 .iter()
1445 .filter_map(|c| left_df_schema.index_of_column(c).ok())
1446 .sorted()
1447 .collect::<Vec<_>>();
1448 let right_field_indices = cols
1449 .iter()
1450 .filter_map(|c| right_df_schema.index_of_column(c).ok())
1451 .sorted()
1452 .collect::<Vec<_>>();
1453
1454 let (filter_df_fields, filter_fields): (Vec<_>, Vec<_>) =
1456 left_field_indices
1457 .clone()
1458 .into_iter()
1459 .map(|i| {
1460 (
1461 left_df_schema.qualified_field(i),
1462 physical_left.schema().field(i).clone(),
1463 )
1464 })
1465 .chain(right_field_indices.clone().into_iter().map(|i| {
1466 (
1467 right_df_schema.qualified_field(i),
1468 physical_right.schema().field(i).clone(),
1469 )
1470 }))
1471 .unzip();
1472 let filter_df_fields = filter_df_fields
1473 .into_iter()
1474 .map(|(qualifier, field)| {
1475 (qualifier.cloned(), Arc::clone(field))
1476 })
1477 .collect();
1478
1479 let metadata: HashMap<_, _> = left_df_schema
1480 .metadata()
1481 .clone()
1482 .into_iter()
1483 .chain(right_df_schema.metadata().clone())
1484 .collect();
1485
1486 let filter_df_schema = DFSchema::new_with_metadata(
1489 filter_df_fields,
1490 metadata.clone(),
1491 )?;
1492 let filter_schema =
1493 Schema::new_with_metadata(filter_fields, metadata);
1494
1495 let filter_expr = create_physical_expr(
1496 expr,
1497 &filter_df_schema,
1498 session_state.execution_props(),
1499 )?;
1500 let column_indices = join_utils::JoinFilter::build_column_indices(
1501 left_field_indices,
1502 right_field_indices,
1503 );
1504
1505 Some(join_utils::JoinFilter::new(
1506 filter_expr,
1507 column_indices,
1508 Arc::new(filter_schema),
1509 ))
1510 }
1511 _ => None,
1512 };
1513
1514 let prefer_hash_join =
1515 session_state.config_options().optimizer.prefer_hash_join;
1516
1517 let join: Arc<dyn ExecutionPlan> = if join_on.is_empty() {
1519 if join_filter.is_none() && *join_type == JoinType::Inner {
1520 Arc::new(CrossJoinExec::new(physical_left, physical_right))
1522 } else if num_range_filters == 1
1523 && total_filters == 1
1524 && !matches!(
1525 join_type,
1526 JoinType::LeftSemi
1527 | JoinType::RightSemi
1528 | JoinType::LeftAnti
1529 | JoinType::RightAnti
1530 | JoinType::LeftMark
1531 | JoinType::RightMark
1532 )
1533 && session_state
1534 .config_options()
1535 .optimizer
1536 .enable_piecewise_merge_join
1537 {
1538 let Expr::BinaryExpr(be) = &range_filters[0] else {
1539 return plan_err!(
1540 "Unsupported expression for PWMJ: Expected `Expr::BinaryExpr`"
1541 );
1542 };
1543
1544 let mut op = be.op;
1545 if !matches!(
1546 op,
1547 Operator::Lt | Operator::LtEq | Operator::Gt | Operator::GtEq
1548 ) {
1549 return plan_err!(
1550 "Unsupported operator for PWMJ: {:?}. Expected one of <, <=, >, >=",
1551 op
1552 );
1553 }
1554
1555 fn reverse_ineq(op: Operator) -> Operator {
1556 match op {
1557 Operator::Lt => Operator::Gt,
1558 Operator::LtEq => Operator::GtEq,
1559 Operator::Gt => Operator::Lt,
1560 Operator::GtEq => Operator::LtEq,
1561 _ => op,
1562 }
1563 }
1564
1565 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
1566 enum Side {
1567 Left,
1568 Right,
1569 Both,
1570 }
1571
1572 let side_of = |e: &Expr| -> Result<Side> {
1573 let cols = e.column_refs();
1574 let any_left = cols
1575 .iter()
1576 .any(|c| left_df_schema.index_of_column(c).is_ok());
1577 let any_right = cols
1578 .iter()
1579 .any(|c| right_df_schema.index_of_column(c).is_ok());
1580
1581 Ok(match (any_left, any_right) {
1582 (true, false) => Side::Left,
1583 (false, true) => Side::Right,
1584 (true, true) => Side::Both,
1585 _ => unreachable!(),
1586 })
1587 };
1588
1589 let mut lhs_logical = &be.left;
1590 let mut rhs_logical = &be.right;
1591
1592 let left_side = side_of(lhs_logical)?;
1593 let right_side = side_of(rhs_logical)?;
1594 if left_side == Side::Both || right_side == Side::Both {
1595 return Ok(Arc::new(NestedLoopJoinExec::try_new(
1596 physical_left,
1597 physical_right,
1598 join_filter,
1599 join_type,
1600 None,
1601 )?));
1602 }
1603
1604 if left_side == Side::Right && right_side == Side::Left {
1605 std::mem::swap(&mut lhs_logical, &mut rhs_logical);
1606 op = reverse_ineq(op);
1607 } else if !(left_side == Side::Left && right_side == Side::Right)
1608 {
1609 return plan_err!(
1610 "Unsupported operator for PWMJ: {:?}. Expected one of <, <=, >, >=",
1611 op
1612 );
1613 }
1614
1615 let on_left = create_physical_expr(
1616 lhs_logical,
1617 left_df_schema,
1618 session_state.execution_props(),
1619 )?;
1620 let on_right = create_physical_expr(
1621 rhs_logical,
1622 right_df_schema,
1623 session_state.execution_props(),
1624 )?;
1625
1626 Arc::new(PiecewiseMergeJoinExec::try_new(
1627 physical_left,
1628 physical_right,
1629 (on_left, on_right),
1630 op,
1631 *join_type,
1632 session_state.config().target_partitions(),
1633 )?)
1634 } else {
1635 Arc::new(NestedLoopJoinExec::try_new(
1637 physical_left,
1638 physical_right,
1639 join_filter,
1640 join_type,
1641 None,
1642 )?)
1643 }
1644 } else if session_state.config().target_partitions() > 1
1645 && session_state.config().repartition_joins()
1646 && !prefer_hash_join
1647 {
1648 let join_on_len = join_on.len();
1650 Arc::new(SortMergeJoinExec::try_new(
1651 physical_left,
1652 physical_right,
1653 join_on,
1654 join_filter,
1655 *join_type,
1656 vec![SortOptions::default(); join_on_len],
1657 *null_equality,
1658 )?)
1659 } else if session_state.config().target_partitions() > 1
1660 && session_state.config().repartition_joins()
1661 && prefer_hash_join
1662 && !*null_aware
1663 {
1665 Arc::new(HashJoinExec::try_new(
1666 physical_left,
1667 physical_right,
1668 join_on,
1669 join_filter,
1670 join_type,
1671 None,
1672 PartitionMode::Auto,
1673 *null_equality,
1674 *null_aware,
1675 )?)
1676 } else {
1677 Arc::new(HashJoinExec::try_new(
1678 physical_left,
1679 physical_right,
1680 join_on,
1681 join_filter,
1682 join_type,
1683 None,
1684 PartitionMode::CollectLeft,
1685 *null_equality,
1686 *null_aware,
1687 )?)
1688 };
1689
1690 if let Some((input, expr)) = new_project {
1693 self.create_project_physical_exec(session_state, join, input, expr)?
1694 } else {
1695 join
1696 }
1697 }
1698 LogicalPlan::RecursiveQuery(RecursiveQuery {
1699 name, is_distinct, ..
1700 }) => {
1701 let [static_term, recursive_term] = children.two()?;
1702 Arc::new(RecursiveQueryExec::try_new(
1703 name.clone(),
1704 static_term,
1705 recursive_term,
1706 *is_distinct,
1707 )?)
1708 }
1709
1710 LogicalPlan::Union(_) => UnionExec::try_new(children.vec())?,
1712 LogicalPlan::Extension(Extension { node }) => {
1713 let mut maybe_plan = None;
1714 let children = children.vec();
1715 for planner in &self.extension_planners {
1716 if maybe_plan.is_some() {
1717 break;
1718 }
1719
1720 let logical_input = node.inputs();
1721 maybe_plan = planner
1722 .plan_extension(
1723 self,
1724 node.as_ref(),
1725 &logical_input,
1726 &children,
1727 session_state,
1728 )
1729 .await?;
1730 }
1731
1732 let plan = match maybe_plan {
1733 Some(v) => Ok(v),
1734 _ => plan_err!(
1735 "No installed planner was able to convert the custom node to an execution plan: {:?}",
1736 node
1737 ),
1738 }?;
1739
1740 let context = format!("Extension planner for {node:?}");
1741 self.ensure_schema_matches(node.schema(), &plan, &context)?;
1742 plan
1743 }
1744
1745 LogicalPlan::Statement(statement) => {
1747 let name = statement.name();
1749 return not_impl_err!("Unsupported logical plan: Statement({name})");
1750 }
1751 LogicalPlan::Dml(dml) => {
1752 return not_impl_err!("Unsupported logical plan: Dml({0})", dml.op);
1754 }
1755 LogicalPlan::Ddl(ddl) => {
1756 let name = ddl.name();
1761 return not_impl_err!("Unsupported logical plan: {name}");
1762 }
1763 LogicalPlan::Explain(_) => {
1764 return internal_err!(
1765 "Unsupported logical plan: Explain must be root of the plan"
1766 );
1767 }
1768 LogicalPlan::Distinct(_) => {
1769 return internal_err!(
1770 "Unsupported logical plan: Distinct should be replaced to Aggregate"
1771 );
1772 }
1773 LogicalPlan::Analyze(_) => {
1774 return internal_err!(
1775 "Unsupported logical plan: Analyze must be root of the plan"
1776 );
1777 }
1778 };
1779 Ok(exec_node)
1780 }
1781
1782 fn create_grouping_physical_expr(
1783 &self,
1784 group_expr: &[Expr],
1785 input_dfschema: &DFSchema,
1786 input_schema: &Schema,
1787 session_state: &SessionState,
1788 ) -> Result<PhysicalGroupBy> {
1789 if group_expr.len() == 1 {
1790 match &group_expr[0] {
1791 Expr::GroupingSet(GroupingSet::GroupingSets(grouping_sets)) => {
1792 merge_grouping_set_physical_expr(
1793 grouping_sets,
1794 input_dfschema,
1795 input_schema,
1796 session_state,
1797 )
1798 }
1799 Expr::GroupingSet(GroupingSet::Cube(exprs)) => create_cube_physical_expr(
1800 exprs,
1801 input_dfschema,
1802 input_schema,
1803 session_state,
1804 ),
1805 Expr::GroupingSet(GroupingSet::Rollup(exprs)) => {
1806 create_rollup_physical_expr(
1807 exprs,
1808 input_dfschema,
1809 input_schema,
1810 session_state,
1811 )
1812 }
1813 expr => Ok(PhysicalGroupBy::new_single(vec![tuple_err((
1814 self.create_physical_expr(expr, input_dfschema, session_state),
1815 physical_name(expr),
1816 ))?])),
1817 }
1818 } else if group_expr.is_empty() {
1819 Ok(PhysicalGroupBy::new(vec![], vec![], vec![], false))
1822 } else {
1823 Ok(PhysicalGroupBy::new_single(
1824 group_expr
1825 .iter()
1826 .map(|e| {
1827 tuple_err((
1828 self.create_physical_expr(e, input_dfschema, session_state),
1829 physical_name(e),
1830 ))
1831 })
1832 .collect::<Result<Vec<_>>>()?,
1833 ))
1834 }
1835 }
1836}
1837
1838fn merge_grouping_set_physical_expr(
1849 grouping_sets: &[Vec<Expr>],
1850 input_dfschema: &DFSchema,
1851 input_schema: &Schema,
1852 session_state: &SessionState,
1853) -> Result<PhysicalGroupBy> {
1854 let num_groups = grouping_sets.len();
1855 let mut all_exprs: Vec<Expr> = vec![];
1856 let mut grouping_set_expr: Vec<(Arc<dyn PhysicalExpr>, String)> = vec![];
1857 let mut null_exprs: Vec<(Arc<dyn PhysicalExpr>, String)> = vec![];
1858
1859 for expr in grouping_sets.iter().flatten() {
1860 if !all_exprs.contains(expr) {
1861 all_exprs.push(expr.clone());
1862
1863 grouping_set_expr.push(get_physical_expr_pair(
1864 expr,
1865 input_dfschema,
1866 session_state,
1867 )?);
1868
1869 null_exprs.push(get_null_physical_expr_pair(
1870 expr,
1871 input_dfschema,
1872 input_schema,
1873 session_state,
1874 )?);
1875 }
1876 }
1877
1878 let mut merged_sets: Vec<Vec<bool>> = Vec::with_capacity(num_groups);
1879
1880 for expr_group in grouping_sets.iter() {
1881 let group: Vec<bool> = all_exprs
1882 .iter()
1883 .map(|expr| !expr_group.contains(expr))
1884 .collect();
1885
1886 merged_sets.push(group)
1887 }
1888
1889 Ok(PhysicalGroupBy::new(
1890 grouping_set_expr,
1891 null_exprs,
1892 merged_sets,
1893 true,
1894 ))
1895}
1896
1897fn create_cube_physical_expr(
1900 exprs: &[Expr],
1901 input_dfschema: &DFSchema,
1902 input_schema: &Schema,
1903 session_state: &SessionState,
1904) -> Result<PhysicalGroupBy> {
1905 let num_of_exprs = exprs.len();
1906 let num_groups = num_of_exprs * num_of_exprs;
1907
1908 let mut null_exprs: Vec<(Arc<dyn PhysicalExpr>, String)> =
1909 Vec::with_capacity(num_of_exprs);
1910 let mut all_exprs: Vec<(Arc<dyn PhysicalExpr>, String)> =
1911 Vec::with_capacity(num_of_exprs);
1912
1913 for expr in exprs {
1914 null_exprs.push(get_null_physical_expr_pair(
1915 expr,
1916 input_dfschema,
1917 input_schema,
1918 session_state,
1919 )?);
1920
1921 all_exprs.push(get_physical_expr_pair(expr, input_dfschema, session_state)?)
1922 }
1923
1924 let mut groups: Vec<Vec<bool>> = Vec::with_capacity(num_groups);
1925
1926 groups.push(vec![false; num_of_exprs]);
1927
1928 for null_count in 1..=num_of_exprs {
1929 for null_idx in (0..num_of_exprs).combinations(null_count) {
1930 let mut next_group: Vec<bool> = vec![false; num_of_exprs];
1931 null_idx.into_iter().for_each(|i| next_group[i] = true);
1932 groups.push(next_group);
1933 }
1934 }
1935
1936 Ok(PhysicalGroupBy::new(all_exprs, null_exprs, groups, true))
1937}
1938
1939fn create_rollup_physical_expr(
1942 exprs: &[Expr],
1943 input_dfschema: &DFSchema,
1944 input_schema: &Schema,
1945 session_state: &SessionState,
1946) -> Result<PhysicalGroupBy> {
1947 let num_of_exprs = exprs.len();
1948
1949 let mut null_exprs: Vec<(Arc<dyn PhysicalExpr>, String)> =
1950 Vec::with_capacity(num_of_exprs);
1951 let mut all_exprs: Vec<(Arc<dyn PhysicalExpr>, String)> =
1952 Vec::with_capacity(num_of_exprs);
1953
1954 let mut groups: Vec<Vec<bool>> = Vec::with_capacity(num_of_exprs + 1);
1955
1956 for expr in exprs {
1957 null_exprs.push(get_null_physical_expr_pair(
1958 expr,
1959 input_dfschema,
1960 input_schema,
1961 session_state,
1962 )?);
1963
1964 all_exprs.push(get_physical_expr_pair(expr, input_dfschema, session_state)?)
1965 }
1966
1967 for total in 0..=num_of_exprs {
1968 let mut group: Vec<bool> = Vec::with_capacity(num_of_exprs);
1969
1970 for index in 0..num_of_exprs {
1971 if index < total {
1972 group.push(false);
1973 } else {
1974 group.push(true);
1975 }
1976 }
1977
1978 groups.push(group)
1979 }
1980
1981 Ok(PhysicalGroupBy::new(all_exprs, null_exprs, groups, true))
1982}
1983
1984fn get_null_physical_expr_pair(
1986 expr: &Expr,
1987 input_dfschema: &DFSchema,
1988 input_schema: &Schema,
1989 session_state: &SessionState,
1990) -> Result<(Arc<dyn PhysicalExpr>, String)> {
1991 let physical_expr =
1992 create_physical_expr(expr, input_dfschema, session_state.execution_props())?;
1993 let physical_name = physical_name(&expr.clone())?;
1994
1995 let data_type = physical_expr.data_type(input_schema)?;
1996 let null_value: ScalarValue = (&data_type).try_into()?;
1997
1998 let null_value = Literal::new(null_value);
1999 Ok((Arc::new(null_value), physical_name))
2000}
2001
2002fn qualify_join_schema_sides(
2009 join_schema: &DFSchema,
2010 left: &LogicalPlan,
2011 right: &LogicalPlan,
2012) -> Result<DFSchema> {
2013 let left_fields = left.schema().fields();
2014 let right_fields = right.schema().fields();
2015 let join_fields = join_schema.fields();
2016
2017 assert_eq_or_internal_err!(
2019 join_fields.len(),
2020 left_fields.len() + right_fields.len(),
2021 "Join schema field count must match left and right field count."
2022 );
2023
2024 for (i, (field, expected)) in join_fields
2026 .iter()
2027 .zip(left_fields.iter().chain(right_fields.iter()))
2028 .enumerate()
2029 {
2030 assert_eq_or_internal_err!(
2031 field.name(),
2032 expected.name(),
2033 "Field name mismatch at index {}",
2034 i
2035 );
2036 }
2037
2038 let qualifiers = join_fields
2040 .iter()
2041 .enumerate()
2042 .map(|(i, _)| {
2043 if i < left_fields.len() {
2044 Some(TableReference::Bare {
2045 table: Arc::from("left"),
2046 })
2047 } else {
2048 Some(TableReference::Bare {
2049 table: Arc::from("right"),
2050 })
2051 }
2052 })
2053 .collect();
2054
2055 join_schema.with_field_specific_qualified_schema(qualifiers)
2056}
2057
2058fn get_physical_expr_pair(
2059 expr: &Expr,
2060 input_dfschema: &DFSchema,
2061 session_state: &SessionState,
2062) -> Result<(Arc<dyn PhysicalExpr>, String)> {
2063 let physical_expr =
2064 create_physical_expr(expr, input_dfschema, session_state.execution_props())?;
2065 let physical_name = physical_name(expr)?;
2066 Ok((physical_expr, physical_name))
2067}
2068
2069fn extract_dml_filters(
2091 input: &Arc<LogicalPlan>,
2092 target: &TableReference,
2093) -> Result<Vec<Expr>> {
2094 let mut filters = Vec::new();
2095 let mut allowed_refs = vec![target.clone()];
2096
2097 input.apply(|node| {
2099 if let LogicalPlan::SubqueryAlias(alias) = node
2100 && let LogicalPlan::TableScan(scan) = alias.input.as_ref()
2102 && scan.table_name.resolved_eq(target)
2103 {
2104 allowed_refs.push(TableReference::bare(alias.alias.to_string()));
2105 }
2106 Ok(TreeNodeRecursion::Continue)
2107 })?;
2108
2109 input.apply(|node| {
2110 match node {
2111 LogicalPlan::Filter(filter) => {
2112 for predicate in split_conjunction(&filter.predicate) {
2114 if predicate_is_on_target_multi(predicate, &allowed_refs)? {
2115 filters.push(predicate.clone());
2116 }
2117 }
2118 }
2119 LogicalPlan::TableScan(TableScan {
2120 table_name,
2121 filters: scan_filters,
2122 ..
2123 }) => {
2124 if table_name.resolved_eq(target) {
2128 for filter in scan_filters {
2129 filters.extend(split_conjunction(filter).into_iter().cloned());
2130 }
2131 }
2132 }
2133 LogicalPlan::EmptyRelation(_)
2135 | LogicalPlan::Values(_)
2136 | LogicalPlan::DescribeTable(_)
2137 | LogicalPlan::Explain(_)
2138 | LogicalPlan::Analyze(_)
2139 | LogicalPlan::Distinct(_)
2140 | LogicalPlan::Extension(_)
2141 | LogicalPlan::Statement(_)
2142 | LogicalPlan::Dml(_)
2143 | LogicalPlan::Ddl(_)
2144 | LogicalPlan::Copy(_)
2145 | LogicalPlan::Unnest(_)
2146 | LogicalPlan::RecursiveQuery(_) => {
2147 }
2149 LogicalPlan::Projection(_)
2151 | LogicalPlan::SubqueryAlias(_)
2152 | LogicalPlan::Limit(_)
2153 | LogicalPlan::Sort(_)
2154 | LogicalPlan::Union(_)
2155 | LogicalPlan::Join(_)
2156 | LogicalPlan::Repartition(_)
2157 | LogicalPlan::Aggregate(_)
2158 | LogicalPlan::Window(_)
2159 | LogicalPlan::Subquery(_) => {
2160 }
2163 }
2164 Ok(TreeNodeRecursion::Continue)
2165 })?;
2166
2167 let mut seen_filters = HashSet::new();
2175 filters
2176 .into_iter()
2177 .try_fold(Vec::new(), |mut deduped, filter| {
2178 let unqualified = strip_column_qualifiers(filter).map_err(|e| {
2179 e.context(format!(
2180 "Failed to strip column qualifiers for DML filter on table '{target}'"
2181 ))
2182 })?;
2183 if seen_filters.insert(unqualified.clone()) {
2184 deduped.push(unqualified);
2185 }
2186 Ok(deduped)
2187 })
2188}
2189
2190fn predicate_is_on_target_multi(
2196 expr: &Expr,
2197 allowed_refs: &[TableReference],
2198) -> Result<bool> {
2199 let mut columns = HashSet::new();
2200 expr_to_columns(expr, &mut columns)?;
2201
2202 Ok(!columns.iter().any(|column| {
2207 column.relation.as_ref().is_some_and(|relation| {
2208 !allowed_refs
2209 .iter()
2210 .any(|allowed| relation.resolved_eq(allowed))
2211 })
2212 }))
2213}
2214
2215fn strip_column_qualifiers(expr: Expr) -> Result<Expr> {
2219 expr.transform(|e| {
2220 if let Expr::Column(col) = &e
2221 && col.relation.is_some()
2222 {
2223 return Ok(Transformed::yes(Expr::Column(Column::new_unqualified(
2225 col.name.clone(),
2226 ))));
2227 }
2228 Ok(Transformed::no(e))
2229 })
2230 .map(|t| t.data)
2231}
2232
2233fn extract_update_assignments(input: &Arc<LogicalPlan>) -> Result<Vec<(String, Expr)>> {
2239 let mut assignments = Vec::new();
2246
2247 if let LogicalPlan::Projection(projection) = input.as_ref() {
2249 for expr in &projection.expr {
2250 if let Expr::Alias(alias) = expr {
2251 let column_name = alias.name.clone();
2254 if !is_identity_assignment(&alias.expr, &column_name) {
2257 let stripped_expr = strip_column_qualifiers((*alias.expr).clone())?;
2259 assignments.push((column_name, stripped_expr));
2260 }
2261 }
2262 }
2263 } else {
2264 input.apply(|node| {
2266 if let LogicalPlan::Projection(projection) = node {
2267 for expr in &projection.expr {
2268 if let Expr::Alias(alias) = expr {
2269 let column_name = alias.name.clone();
2270 if !is_identity_assignment(&alias.expr, &column_name) {
2271 let stripped_expr =
2272 strip_column_qualifiers((*alias.expr).clone())?;
2273 assignments.push((column_name, stripped_expr));
2274 }
2275 }
2276 }
2277 return Ok(TreeNodeRecursion::Stop);
2278 }
2279 Ok(TreeNodeRecursion::Continue)
2280 })?;
2281 }
2282
2283 Ok(assignments)
2284}
2285
2286fn is_identity_assignment(expr: &Expr, column_name: &str) -> bool {
2289 match expr {
2290 Expr::Column(col) => col.name == column_name,
2291 _ => false,
2292 }
2293}
2294
2295pub fn is_window_frame_bound_valid(window_frame: &WindowFrame) -> bool {
2301 match (&window_frame.start_bound, &window_frame.end_bound) {
2302 (WindowFrameBound::Following(_), WindowFrameBound::Preceding(_))
2303 | (WindowFrameBound::Following(_), WindowFrameBound::CurrentRow)
2304 | (WindowFrameBound::CurrentRow, WindowFrameBound::Preceding(_)) => false,
2305 (WindowFrameBound::Preceding(lhs), WindowFrameBound::Preceding(rhs)) => {
2306 !rhs.is_null() && (lhs.is_null() || (lhs >= rhs))
2307 }
2308 (WindowFrameBound::Following(lhs), WindowFrameBound::Following(rhs)) => {
2309 !lhs.is_null() && (rhs.is_null() || (lhs <= rhs))
2310 }
2311 _ => true,
2312 }
2313}
2314
2315pub fn create_window_expr_with_name(
2317 e: &Expr,
2318 name: impl Into<String>,
2319 logical_schema: &DFSchema,
2320 execution_props: &ExecutionProps,
2321) -> Result<Arc<dyn WindowExpr>> {
2322 let name = name.into();
2323 let physical_schema = Arc::clone(logical_schema.inner());
2324 match e {
2325 Expr::WindowFunction(window_fun) => {
2326 let WindowFunction {
2327 fun,
2328 params:
2329 WindowFunctionParams {
2330 args,
2331 partition_by,
2332 order_by,
2333 window_frame,
2334 null_treatment,
2335 distinct,
2336 filter,
2337 },
2338 } = window_fun.as_ref();
2339 let physical_args =
2340 create_physical_exprs(args, logical_schema, execution_props)?;
2341 let partition_by =
2342 create_physical_exprs(partition_by, logical_schema, execution_props)?;
2343 let order_by =
2344 create_physical_sort_exprs(order_by, logical_schema, execution_props)?;
2345
2346 if !is_window_frame_bound_valid(window_frame) {
2347 return plan_err!(
2348 "Invalid window frame: start bound ({}) cannot be larger than end bound ({})",
2349 window_frame.start_bound,
2350 window_frame.end_bound
2351 );
2352 }
2353
2354 let window_frame = Arc::new(window_frame.clone());
2355 let ignore_nulls = null_treatment.unwrap_or(NullTreatment::RespectNulls)
2356 == NullTreatment::IgnoreNulls;
2357 let physical_filter = filter
2358 .as_ref()
2359 .map(|f| create_physical_expr(f, logical_schema, execution_props))
2360 .transpose()?;
2361
2362 windows::create_window_expr(
2363 fun,
2364 name,
2365 &physical_args,
2366 &partition_by,
2367 &order_by,
2368 window_frame,
2369 physical_schema,
2370 ignore_nulls,
2371 *distinct,
2372 physical_filter,
2373 )
2374 }
2375 other => plan_err!("Invalid window expression '{other:?}'"),
2376 }
2377}
2378
2379pub fn create_window_expr(
2381 e: &Expr,
2382 logical_schema: &DFSchema,
2383 execution_props: &ExecutionProps,
2384) -> Result<Arc<dyn WindowExpr>> {
2385 let (name, e) = match e {
2387 Expr::Alias(Alias { expr, name, .. }) => (name.clone(), expr.as_ref()),
2388 _ => (e.schema_name().to_string(), e),
2389 };
2390 create_window_expr_with_name(e, name, logical_schema, execution_props)
2391}
2392
2393type AggregateExprWithOptionalArgs = (
2394 Arc<AggregateFunctionExpr>,
2395 Option<Arc<dyn PhysicalExpr>>,
2397 Vec<PhysicalSortExpr>,
2399);
2400
2401pub fn create_aggregate_expr_with_name_and_maybe_filter(
2403 e: &Expr,
2404 name: Option<String>,
2405 human_displan: String,
2406 logical_input_schema: &DFSchema,
2407 physical_input_schema: &Schema,
2408 execution_props: &ExecutionProps,
2409) -> Result<AggregateExprWithOptionalArgs> {
2410 match e {
2411 Expr::AggregateFunction(AggregateFunction {
2412 func,
2413 params:
2414 AggregateFunctionParams {
2415 args,
2416 distinct,
2417 filter,
2418 order_by,
2419 null_treatment,
2420 },
2421 }) => {
2422 let name = if let Some(name) = name {
2423 name
2424 } else {
2425 physical_name(e)?
2426 };
2427
2428 let physical_args =
2429 create_physical_exprs(args, logical_input_schema, execution_props)?;
2430 let filter = match filter {
2431 Some(e) => Some(create_physical_expr(
2432 e,
2433 logical_input_schema,
2434 execution_props,
2435 )?),
2436 None => None,
2437 };
2438
2439 let ignore_nulls = null_treatment.unwrap_or(NullTreatment::RespectNulls)
2440 == NullTreatment::IgnoreNulls;
2441
2442 let (agg_expr, filter, order_bys) = {
2443 let order_bys = create_physical_sort_exprs(
2444 order_by,
2445 logical_input_schema,
2446 execution_props,
2447 )?;
2448
2449 let agg_expr =
2450 AggregateExprBuilder::new(func.to_owned(), physical_args.to_vec())
2451 .order_by(order_bys.clone())
2452 .schema(Arc::new(physical_input_schema.to_owned()))
2453 .alias(name)
2454 .human_display(human_displan)
2455 .with_ignore_nulls(ignore_nulls)
2456 .with_distinct(*distinct)
2457 .build()
2458 .map(Arc::new)?;
2459
2460 (agg_expr, filter, order_bys)
2461 };
2462
2463 Ok((agg_expr, filter, order_bys))
2464 }
2465 other => internal_err!("Invalid aggregate expression '{other:?}'"),
2466 }
2467}
2468
2469pub fn create_aggregate_expr_and_maybe_filter(
2471 e: &Expr,
2472 logical_input_schema: &DFSchema,
2473 physical_input_schema: &Schema,
2474 execution_props: &ExecutionProps,
2475) -> Result<AggregateExprWithOptionalArgs> {
2476 let (name, human_display, e) = match e {
2480 Expr::Alias(Alias { name, .. }) => {
2481 let unaliased = e.clone().unalias_nested().data;
2482 (Some(name.clone()), e.human_display().to_string(), unaliased)
2483 }
2484 Expr::AggregateFunction(_) => (
2485 Some(e.schema_name().to_string()),
2486 e.human_display().to_string(),
2487 e.clone(),
2488 ),
2489 _ => (None, String::default(), e.clone()),
2490 };
2491
2492 create_aggregate_expr_with_name_and_maybe_filter(
2493 &e,
2494 name,
2495 human_display,
2496 logical_input_schema,
2497 physical_input_schema,
2498 execution_props,
2499 )
2500}
2501
2502impl DefaultPhysicalPlanner {
2503 async fn handle_explain_or_analyze(
2509 &self,
2510 logical_plan: &LogicalPlan,
2511 session_state: &SessionState,
2512 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
2513 let execution_plan = match logical_plan {
2514 LogicalPlan::Explain(e) => self.handle_explain(e, session_state).await?,
2515 LogicalPlan::Analyze(a) => self.handle_analyze(a, session_state).await?,
2516 _ => return Ok(None),
2517 };
2518 Ok(Some(execution_plan))
2519 }
2520
2521 async fn handle_explain(
2523 &self,
2524 e: &Explain,
2525 session_state: &SessionState,
2526 ) -> Result<Arc<dyn ExecutionPlan>> {
2527 use PlanType::*;
2528 let mut stringified_plans = vec![];
2529
2530 let config = &session_state.config_options().explain;
2531 let explain_format = &e.explain_format;
2532
2533 if !e.logical_optimization_succeeded {
2534 return Ok(Arc::new(ExplainExec::new(
2535 Arc::clone(e.schema.inner()),
2536 e.stringified_plans.clone(),
2537 true,
2538 )));
2539 }
2540
2541 match explain_format {
2542 ExplainFormat::Indent => { }
2543 ExplainFormat::Tree => {
2544 let physical_plan = self
2546 .create_initial_plan(e.plan.as_ref(), session_state)
2547 .await?;
2548
2549 let optimized_plan = self.optimize_physical_plan(
2550 physical_plan,
2551 session_state,
2552 |_plan, _optimizer| {},
2553 )?;
2554
2555 stringified_plans.push(StringifiedPlan::new(
2556 FinalPhysicalPlan,
2557 displayable(optimized_plan.as_ref())
2558 .set_tree_maximum_render_width(config.tree_maximum_render_width)
2559 .tree_render()
2560 .to_string(),
2561 ));
2562 }
2563 ExplainFormat::PostgresJSON => {
2564 stringified_plans.push(StringifiedPlan::new(
2565 FinalLogicalPlan,
2566 e.plan.display_pg_json().to_string(),
2567 ));
2568 }
2569 ExplainFormat::Graphviz => {
2570 stringified_plans.push(StringifiedPlan::new(
2571 FinalLogicalPlan,
2572 e.plan.display_graphviz().to_string(),
2573 ));
2574 }
2575 };
2576
2577 if !stringified_plans.is_empty() {
2578 return Ok(Arc::new(ExplainExec::new(
2579 Arc::clone(e.schema.inner()),
2580 stringified_plans,
2581 e.verbose,
2582 )));
2583 }
2584
2585 if !config.physical_plan_only {
2588 stringified_plans.clone_from(&e.stringified_plans);
2589 if e.logical_optimization_succeeded {
2590 stringified_plans.push(e.plan.to_stringified(FinalLogicalPlan));
2591 }
2592 }
2593
2594 if !config.logical_plan_only && e.logical_optimization_succeeded {
2595 match self
2596 .create_initial_plan(e.plan.as_ref(), session_state)
2597 .await
2598 {
2599 Ok(input) => {
2600 stringified_plans.push(StringifiedPlan::new(
2602 InitialPhysicalPlan,
2603 displayable(input.as_ref())
2604 .set_show_statistics(config.show_statistics)
2605 .set_show_schema(config.show_schema)
2606 .indent(e.verbose)
2607 .to_string(),
2608 ));
2609
2610 if e.verbose {
2613 if !config.show_statistics {
2614 stringified_plans.push(StringifiedPlan::new(
2615 InitialPhysicalPlanWithStats,
2616 displayable(input.as_ref())
2617 .set_show_statistics(true)
2618 .indent(e.verbose)
2619 .to_string(),
2620 ));
2621 }
2622 if !config.show_schema {
2623 stringified_plans.push(StringifiedPlan::new(
2624 InitialPhysicalPlanWithSchema,
2625 displayable(input.as_ref())
2626 .set_show_schema(true)
2627 .indent(e.verbose)
2628 .to_string(),
2629 ));
2630 }
2631 }
2632
2633 let optimized_plan = self.optimize_physical_plan(
2634 input,
2635 session_state,
2636 |plan, optimizer| {
2637 let optimizer_name = optimizer.name().to_string();
2638 let plan_type = OptimizedPhysicalPlan { optimizer_name };
2639 stringified_plans.push(StringifiedPlan::new(
2640 plan_type,
2641 displayable(plan)
2642 .set_show_statistics(config.show_statistics)
2643 .set_show_schema(config.show_schema)
2644 .indent(e.verbose)
2645 .to_string(),
2646 ));
2647 },
2648 );
2649 match optimized_plan {
2650 Ok(input) => {
2651 stringified_plans.push(StringifiedPlan::new(
2653 FinalPhysicalPlan,
2654 displayable(input.as_ref())
2655 .set_show_statistics(config.show_statistics)
2656 .set_show_schema(config.show_schema)
2657 .indent(e.verbose)
2658 .to_string(),
2659 ));
2660
2661 if e.verbose {
2664 if !config.show_statistics {
2665 stringified_plans.push(StringifiedPlan::new(
2666 FinalPhysicalPlanWithStats,
2667 displayable(input.as_ref())
2668 .set_show_statistics(true)
2669 .indent(e.verbose)
2670 .to_string(),
2671 ));
2672 }
2673 if !config.show_schema {
2674 stringified_plans.push(StringifiedPlan::new(
2675 FinalPhysicalPlanWithSchema,
2676 displayable(input.as_ref())
2679 .set_show_schema(true)
2680 .indent(e.verbose)
2681 .to_string(),
2682 ));
2683 }
2684 }
2685 }
2686 Err(DataFusionError::Context(optimizer_name, e)) => {
2687 let plan_type = OptimizedPhysicalPlan { optimizer_name };
2688 stringified_plans
2689 .push(StringifiedPlan::new(plan_type, e.to_string()))
2690 }
2691 Err(e) => return Err(e),
2692 }
2693 }
2694 Err(err) => {
2695 stringified_plans.push(StringifiedPlan::new(
2696 PhysicalPlanError,
2697 err.strip_backtrace(),
2698 ));
2699 }
2700 }
2701 }
2702
2703 Ok(Arc::new(ExplainExec::new(
2704 Arc::clone(e.schema.inner()),
2705 stringified_plans,
2706 e.verbose,
2707 )))
2708 }
2709
2710 async fn handle_analyze(
2711 &self,
2712 a: &Analyze,
2713 session_state: &SessionState,
2714 ) -> Result<Arc<dyn ExecutionPlan>> {
2715 let input = self.create_physical_plan(&a.input, session_state).await?;
2716 let schema = Arc::clone(a.schema.inner());
2717 let show_statistics = session_state.config_options().explain.show_statistics;
2718 let analyze_level = session_state.config_options().explain.analyze_level;
2719 let metric_types = match analyze_level {
2720 ExplainAnalyzeLevel::Summary => vec![MetricType::SUMMARY],
2721 ExplainAnalyzeLevel::Dev => vec![MetricType::SUMMARY, MetricType::DEV],
2722 };
2723 Ok(Arc::new(AnalyzeExec::new(
2724 a.verbose,
2725 show_statistics,
2726 metric_types,
2727 input,
2728 schema,
2729 )))
2730 }
2731
2732 #[expect(clippy::needless_pass_by_value)]
2735 pub fn optimize_physical_plan<F>(
2736 &self,
2737 plan: Arc<dyn ExecutionPlan>,
2738 session_state: &SessionState,
2739 mut observer: F,
2740 ) -> Result<Arc<dyn ExecutionPlan>>
2741 where
2742 F: FnMut(&dyn ExecutionPlan, &dyn PhysicalOptimizerRule),
2743 {
2744 let optimizers = session_state.physical_optimizers();
2745 debug!(
2746 "Input physical plan:\n{}\n",
2747 displayable(plan.as_ref()).indent(false)
2748 );
2749 debug!(
2750 "Detailed input physical plan:\n{}",
2751 displayable(plan.as_ref()).indent(true)
2752 );
2753
2754 InvariantChecker(InvariantLevel::Always).check(&plan)?;
2757
2758 let mut new_plan = Arc::clone(&plan);
2759 for optimizer in optimizers {
2760 let before_schema = new_plan.schema();
2761 new_plan = optimizer
2762 .optimize(new_plan, session_state.config_options())
2763 .map_err(|e| {
2764 DataFusionError::Context(optimizer.name().to_string(), Box::new(e))
2765 })?;
2766
2767 OptimizationInvariantChecker::new(optimizer)
2769 .check(&new_plan, &before_schema)?;
2770
2771 debug!(
2772 "Optimized physical plan by {}:\n{}\n",
2773 optimizer.name(),
2774 displayable(new_plan.as_ref()).indent(false)
2775 );
2776 observer(new_plan.as_ref(), optimizer.as_ref())
2777 }
2778
2779 InvariantChecker(InvariantLevel::Executable).check(&new_plan)?;
2782
2783 debug!(
2784 "Optimized physical plan:\n{}\n",
2785 displayable(new_plan.as_ref()).indent(false)
2786 );
2787
2788 debug!(
2793 "Detailed optimized physical plan:\n{}\n",
2794 displayable(new_plan.as_ref()).indent(true)
2795 );
2796 Ok(new_plan)
2797 }
2798
2799 fn plan_describe(
2801 &self,
2802 table_schema: &Arc<Schema>,
2803 output_schema: Arc<Schema>,
2804 ) -> Result<Arc<dyn ExecutionPlan>> {
2805 let mut column_names = StringBuilder::new();
2806 let mut data_types = StringBuilder::new();
2807 let mut is_nullables = StringBuilder::new();
2808 for field in table_schema.fields() {
2809 column_names.append_value(field.name());
2810
2811 let data_type = field.data_type();
2813 data_types.append_value(format!("{data_type}"));
2814
2815 let nullable_str = if field.is_nullable() { "YES" } else { "NO" };
2817 is_nullables.append_value(nullable_str);
2818 }
2819
2820 let record_batch = RecordBatch::try_new(
2821 output_schema,
2822 vec![
2823 Arc::new(column_names.finish()),
2824 Arc::new(data_types.finish()),
2825 Arc::new(is_nullables.finish()),
2826 ],
2827 )?;
2828
2829 let schema = record_batch.schema();
2830 let partitions = vec![vec![record_batch]];
2831 let projection = None;
2832 let mem_exec = MemorySourceConfig::try_new_exec(&partitions, schema, projection)?;
2833 Ok(mem_exec)
2834 }
2835
2836 fn create_project_physical_exec(
2837 &self,
2838 session_state: &SessionState,
2839 input_exec: Arc<dyn ExecutionPlan>,
2840 input: &Arc<LogicalPlan>,
2841 expr: &[Expr],
2842 ) -> Result<Arc<dyn ExecutionPlan>> {
2843 let input_logical_schema = input.as_ref().schema();
2844 let input_physical_schema = input_exec.schema();
2845 let physical_exprs = expr
2846 .iter()
2847 .map(|e| {
2848 let physical_name = if let Expr::Column(col) = e {
2863 match input_logical_schema.index_of_column(col) {
2864 Ok(idx) => {
2865 Ok(input_exec.schema().field(idx).name().to_string())
2867 }
2868 Err(_) => physical_name(e),
2871 }
2872 } else {
2873 physical_name(e)
2874 };
2875
2876 let physical_expr =
2877 self.create_physical_expr(e, input_logical_schema, session_state);
2878
2879 tuple_err((physical_expr, physical_name))
2880 })
2881 .collect::<Result<Vec<_>>>()?;
2882
2883 let num_input_columns = input_exec.schema().fields().len();
2884
2885 match self.try_plan_async_exprs(
2886 num_input_columns,
2887 PlannedExprResult::ExprWithName(physical_exprs),
2888 input_physical_schema.as_ref(),
2889 )? {
2890 PlanAsyncExpr::Sync(PlannedExprResult::ExprWithName(physical_exprs)) => {
2891 let proj_exprs: Vec<ProjectionExpr> = physical_exprs
2892 .into_iter()
2893 .map(|(expr, alias)| ProjectionExpr { expr, alias })
2894 .collect();
2895 Ok(Arc::new(ProjectionExec::try_new(proj_exprs, input_exec)?))
2896 }
2897 PlanAsyncExpr::Async(
2898 async_map,
2899 PlannedExprResult::ExprWithName(physical_exprs),
2900 ) => {
2901 let async_exec =
2902 AsyncFuncExec::try_new(async_map.async_exprs, input_exec)?;
2903 let proj_exprs: Vec<ProjectionExpr> = physical_exprs
2904 .into_iter()
2905 .map(|(expr, alias)| ProjectionExpr { expr, alias })
2906 .collect();
2907 let new_proj_exec =
2908 ProjectionExec::try_new(proj_exprs, Arc::new(async_exec))?;
2909 Ok(Arc::new(new_proj_exec))
2910 }
2911 _ => internal_err!("Unexpected PlanAsyncExpressions variant"),
2912 }
2913 }
2914
2915 fn try_plan_async_exprs(
2916 &self,
2917 num_input_columns: usize,
2918 physical_expr: PlannedExprResult,
2919 schema: &Schema,
2920 ) -> Result<PlanAsyncExpr> {
2921 let mut async_map = AsyncMapper::new(num_input_columns);
2922 match &physical_expr {
2923 PlannedExprResult::ExprWithName(exprs) => {
2924 exprs
2925 .iter()
2926 .try_for_each(|(expr, _)| async_map.find_references(expr, schema))?;
2927 }
2928 PlannedExprResult::Expr(exprs) => {
2929 exprs
2930 .iter()
2931 .try_for_each(|expr| async_map.find_references(expr, schema))?;
2932 }
2933 }
2934
2935 if async_map.is_empty() {
2936 return Ok(PlanAsyncExpr::Sync(physical_expr));
2937 }
2938
2939 let new_exprs = match physical_expr {
2940 PlannedExprResult::ExprWithName(exprs) => PlannedExprResult::ExprWithName(
2941 exprs
2942 .iter()
2943 .map(|(expr, column_name)| {
2944 let new_expr = Arc::clone(expr)
2945 .transform_up(|e| Ok(async_map.map_expr(e)))?;
2946 Ok((new_expr.data, column_name.to_string()))
2947 })
2948 .collect::<Result<_>>()?,
2949 ),
2950 PlannedExprResult::Expr(exprs) => PlannedExprResult::Expr(
2951 exprs
2952 .iter()
2953 .map(|expr| {
2954 let new_expr = Arc::clone(expr)
2955 .transform_up(|e| Ok(async_map.map_expr(e)))?;
2956 Ok(new_expr.data)
2957 })
2958 .collect::<Result<_>>()?,
2959 ),
2960 };
2961 Ok(PlanAsyncExpr::Async(async_map, new_exprs))
2963 }
2964}
2965
2966#[derive(Debug)]
2967enum PlannedExprResult {
2968 ExprWithName(Vec<(Arc<dyn PhysicalExpr>, String)>),
2969 Expr(Vec<Arc<dyn PhysicalExpr>>),
2970}
2971
2972#[derive(Debug)]
2973enum PlanAsyncExpr {
2974 Sync(PlannedExprResult),
2975 Async(AsyncMapper, PlannedExprResult),
2976}
2977
2978fn tuple_err<T, R>(value: (Result<T>, Result<R>)) -> Result<(T, R)> {
2979 match value {
2980 (Ok(e), Ok(e1)) => Ok((e, e1)),
2981 (Err(e), Ok(_)) => Err(e),
2982 (Ok(_), Err(e1)) => Err(e1),
2983 (Err(e), Err(_)) => Err(e),
2984 }
2985}
2986
2987struct OptimizationInvariantChecker<'a> {
2988 rule: &'a Arc<dyn PhysicalOptimizerRule + Send + Sync>,
2989}
2990
2991impl<'a> OptimizationInvariantChecker<'a> {
2992 pub fn new(rule: &'a Arc<dyn PhysicalOptimizerRule + Send + Sync>) -> Self {
2994 Self { rule }
2995 }
2996
2997 pub fn check(
3003 &mut self,
3004 plan: &Arc<dyn ExecutionPlan>,
3005 previous_schema: &Arc<Schema>,
3006 ) -> Result<()> {
3007 if self.rule.schema_check()
3009 && !is_allowed_schema_change(previous_schema.as_ref(), plan.schema().as_ref())
3010 {
3011 internal_err!(
3012 "PhysicalOptimizer rule '{}' failed. Schema mismatch. Expected original schema: {}, got new schema: {}",
3013 self.rule.name(),
3014 previous_schema,
3015 plan.schema()
3016 )?
3017 }
3018
3019 #[cfg(debug_assertions)]
3021 plan.visit(self)?;
3022
3023 Ok(())
3024 }
3025}
3026
3027fn is_allowed_schema_change(old: &Schema, new: &Schema) -> bool {
3036 if new.metadata != old.metadata {
3037 return false;
3038 }
3039
3040 if new.fields.len() != old.fields.len() {
3041 return false;
3042 }
3043
3044 let new_fields = new.fields.iter().map(|f| f.as_ref());
3045 let old_fields = old.fields.iter().map(|f| f.as_ref());
3046 old_fields
3047 .zip(new_fields)
3048 .all(|(old, new)| is_allowed_field_change(old, new))
3049}
3050
3051fn is_allowed_field_change(old_field: &Field, new_field: &Field) -> bool {
3052 new_field.name() == old_field.name()
3053 && new_field.data_type() == old_field.data_type()
3054 && new_field.metadata() == old_field.metadata()
3055 && (new_field.is_nullable() == old_field.is_nullable()
3056 || !new_field.is_nullable())
3057}
3058
3059impl<'n> TreeNodeVisitor<'n> for OptimizationInvariantChecker<'_> {
3060 type Node = Arc<dyn ExecutionPlan>;
3061
3062 fn f_down(&mut self, node: &'n Self::Node) -> Result<TreeNodeRecursion> {
3063 node.check_invariants(InvariantLevel::Always).map_err(|e|
3066 e.context(format!("Invariant for ExecutionPlan node '{}' failed for PhysicalOptimizer rule '{}'", node.name(), self.rule.name()))
3067 )?;
3068 Ok(TreeNodeRecursion::Continue)
3069 }
3070}
3071
3072struct InvariantChecker(InvariantLevel);
3074
3075impl InvariantChecker {
3076 pub fn check(&mut self, plan: &Arc<dyn ExecutionPlan>) -> Result<()> {
3078 plan.visit(self)?;
3080
3081 Ok(())
3082 }
3083}
3084
3085impl<'n> TreeNodeVisitor<'n> for InvariantChecker {
3086 type Node = Arc<dyn ExecutionPlan>;
3087
3088 fn f_down(&mut self, node: &'n Self::Node) -> Result<TreeNodeRecursion> {
3089 node.check_invariants(self.0).map_err(|e| {
3090 e.context(format!(
3091 "Invariant for ExecutionPlan node '{}' failed",
3092 node.name()
3093 ))
3094 })?;
3095 Ok(TreeNodeRecursion::Continue)
3096 }
3097}
3098
3099#[cfg(test)]
3100mod tests {
3101 use std::any::Any;
3102 use std::cmp::Ordering;
3103 use std::fmt::{self, Debug};
3104 use std::ops::{BitAnd, Not};
3105
3106 use super::*;
3107 use crate::datasource::MemTable;
3108 use crate::datasource::file_format::options::CsvReadOptions;
3109 use crate::physical_plan::{
3110 DisplayAs, DisplayFormatType, PlanProperties, SendableRecordBatchStream,
3111 expressions,
3112 };
3113 use crate::prelude::{SessionConfig, SessionContext};
3114 use crate::test_util::{scan_empty, scan_empty_with_partitions};
3115
3116 use crate::execution::session_state::SessionStateBuilder;
3117 use arrow::array::{ArrayRef, DictionaryArray, Int32Array};
3118 use arrow::datatypes::{DataType, Field, Int32Type};
3119 use arrow_schema::SchemaRef;
3120 use datafusion_common::config::ConfigOptions;
3121 use datafusion_common::{
3122 DFSchemaRef, TableReference, ToDFSchema as _, assert_contains,
3123 };
3124 use datafusion_execution::TaskContext;
3125 use datafusion_execution::runtime_env::RuntimeEnv;
3126 use datafusion_expr::builder::subquery_alias;
3127 use datafusion_expr::{
3128 LogicalPlanBuilder, TableSource, UserDefinedLogicalNodeCore, col, lit,
3129 };
3130 use datafusion_functions_aggregate::count::count_all;
3131 use datafusion_functions_aggregate::expr_fn::sum;
3132 use datafusion_physical_expr::EquivalenceProperties;
3133 use datafusion_physical_plan::execution_plan::{Boundedness, EmissionType};
3134
3135 fn make_session_state() -> SessionState {
3136 let runtime = Arc::new(RuntimeEnv::default());
3137 let config = SessionConfig::new().with_target_partitions(4);
3138 let config = config.set_bool("datafusion.optimizer.skip_failed_rules", false);
3139 SessionStateBuilder::new()
3140 .with_config(config)
3141 .with_runtime_env(runtime)
3142 .with_default_features()
3143 .build()
3144 }
3145
3146 async fn plan(logical_plan: &LogicalPlan) -> Result<Arc<dyn ExecutionPlan>> {
3147 let session_state = make_session_state();
3148 let logical_plan = session_state.optimize(logical_plan)?;
3150 let planner = DefaultPhysicalPlanner::default();
3151 planner
3152 .create_physical_plan(&logical_plan, &session_state)
3153 .await
3154 }
3155
3156 #[tokio::test]
3157 async fn test_all_operators() -> Result<()> {
3158 let logical_plan = test_csv_scan()
3159 .await?
3160 .filter(col("c7").lt(lit(5_u8)))?
3162 .project(vec![col("c1"), col("c2")])?
3163 .aggregate(vec![col("c1")], vec![sum(col("c2"))])?
3164 .sort(vec![col("c1").sort(true, true)])?
3165 .limit(3, Some(10))?
3166 .build()?;
3167
3168 let exec_plan = plan(&logical_plan).await?;
3169
3170 let expected = r#"BinaryExpr { left: Column { name: "c7", index: 2 }, op: Lt, right: Literal { value: Int64(5), field: Field { name: "lit", data_type: Int64 } }, fail_on_overflow: false"#;
3174
3175 assert_contains!(format!("{exec_plan:?}"), expected);
3176 Ok(())
3177 }
3178
3179 #[tokio::test]
3180 async fn test_create_cube_expr() -> Result<()> {
3181 let logical_plan = test_csv_scan().await?.build()?;
3182
3183 let plan = plan(&logical_plan).await?;
3184
3185 let exprs = vec![col("c1"), col("c2"), col("c3")];
3186
3187 let physical_input_schema = plan.schema();
3188 let physical_input_schema = physical_input_schema.as_ref();
3189 let logical_input_schema = logical_plan.schema();
3190 let session_state = make_session_state();
3191
3192 let cube = create_cube_physical_expr(
3193 &exprs,
3194 logical_input_schema,
3195 physical_input_schema,
3196 &session_state,
3197 );
3198
3199 insta::assert_debug_snapshot!(cube, @r#"
3200 Ok(
3201 PhysicalGroupBy {
3202 expr: [
3203 (
3204 Column {
3205 name: "c1",
3206 index: 0,
3207 },
3208 "c1",
3209 ),
3210 (
3211 Column {
3212 name: "c2",
3213 index: 1,
3214 },
3215 "c2",
3216 ),
3217 (
3218 Column {
3219 name: "c3",
3220 index: 2,
3221 },
3222 "c3",
3223 ),
3224 ],
3225 null_expr: [
3226 (
3227 Literal {
3228 value: Utf8(NULL),
3229 field: Field {
3230 name: "lit",
3231 data_type: Utf8,
3232 nullable: true,
3233 },
3234 },
3235 "c1",
3236 ),
3237 (
3238 Literal {
3239 value: Int64(NULL),
3240 field: Field {
3241 name: "lit",
3242 data_type: Int64,
3243 nullable: true,
3244 },
3245 },
3246 "c2",
3247 ),
3248 (
3249 Literal {
3250 value: Int64(NULL),
3251 field: Field {
3252 name: "lit",
3253 data_type: Int64,
3254 nullable: true,
3255 },
3256 },
3257 "c3",
3258 ),
3259 ],
3260 groups: [
3261 [
3262 false,
3263 false,
3264 false,
3265 ],
3266 [
3267 true,
3268 false,
3269 false,
3270 ],
3271 [
3272 false,
3273 true,
3274 false,
3275 ],
3276 [
3277 false,
3278 false,
3279 true,
3280 ],
3281 [
3282 true,
3283 true,
3284 false,
3285 ],
3286 [
3287 true,
3288 false,
3289 true,
3290 ],
3291 [
3292 false,
3293 true,
3294 true,
3295 ],
3296 [
3297 true,
3298 true,
3299 true,
3300 ],
3301 ],
3302 has_grouping_set: true,
3303 },
3304 )
3305 "#);
3306
3307 Ok(())
3308 }
3309
3310 #[tokio::test]
3311 async fn test_create_rollup_expr() -> Result<()> {
3312 let logical_plan = test_csv_scan().await?.build()?;
3313
3314 let plan = plan(&logical_plan).await?;
3315
3316 let exprs = vec![col("c1"), col("c2"), col("c3")];
3317
3318 let physical_input_schema = plan.schema();
3319 let physical_input_schema = physical_input_schema.as_ref();
3320 let logical_input_schema = logical_plan.schema();
3321 let session_state = make_session_state();
3322
3323 let rollup = create_rollup_physical_expr(
3324 &exprs,
3325 logical_input_schema,
3326 physical_input_schema,
3327 &session_state,
3328 );
3329
3330 insta::assert_debug_snapshot!(rollup, @r#"
3331 Ok(
3332 PhysicalGroupBy {
3333 expr: [
3334 (
3335 Column {
3336 name: "c1",
3337 index: 0,
3338 },
3339 "c1",
3340 ),
3341 (
3342 Column {
3343 name: "c2",
3344 index: 1,
3345 },
3346 "c2",
3347 ),
3348 (
3349 Column {
3350 name: "c3",
3351 index: 2,
3352 },
3353 "c3",
3354 ),
3355 ],
3356 null_expr: [
3357 (
3358 Literal {
3359 value: Utf8(NULL),
3360 field: Field {
3361 name: "lit",
3362 data_type: Utf8,
3363 nullable: true,
3364 },
3365 },
3366 "c1",
3367 ),
3368 (
3369 Literal {
3370 value: Int64(NULL),
3371 field: Field {
3372 name: "lit",
3373 data_type: Int64,
3374 nullable: true,
3375 },
3376 },
3377 "c2",
3378 ),
3379 (
3380 Literal {
3381 value: Int64(NULL),
3382 field: Field {
3383 name: "lit",
3384 data_type: Int64,
3385 nullable: true,
3386 },
3387 },
3388 "c3",
3389 ),
3390 ],
3391 groups: [
3392 [
3393 true,
3394 true,
3395 true,
3396 ],
3397 [
3398 false,
3399 true,
3400 true,
3401 ],
3402 [
3403 false,
3404 false,
3405 true,
3406 ],
3407 [
3408 false,
3409 false,
3410 false,
3411 ],
3412 ],
3413 has_grouping_set: true,
3414 },
3415 )
3416 "#);
3417
3418 Ok(())
3419 }
3420
3421 #[tokio::test]
3422 async fn test_create_not() -> Result<()> {
3423 let schema = Schema::new(vec![Field::new("a", DataType::Boolean, true)]);
3424 let dfschema = DFSchema::try_from(schema.clone())?;
3425
3426 let planner = DefaultPhysicalPlanner::default();
3427
3428 let expr = planner.create_physical_expr(
3429 &col("a").not(),
3430 &dfschema,
3431 &make_session_state(),
3432 )?;
3433 let expected = expressions::not(expressions::col("a", &schema)?)?;
3434
3435 assert_eq!(format!("{expr:?}"), format!("{expected:?}"));
3436
3437 Ok(())
3438 }
3439
3440 #[tokio::test]
3441 async fn test_with_csv_plan() -> Result<()> {
3442 let logical_plan = test_csv_scan()
3443 .await?
3444 .filter(col("c7").lt(col("c12")))?
3445 .limit(3, None)?
3446 .build()?;
3447
3448 let plan = plan(&logical_plan).await?;
3449
3450 let _expected = "predicate: BinaryExpr { left: TryCastExpr { expr: Column { name: \"c7\", index: 6 }, cast_type: Float64 }, op: Lt, right: Column { name: \"c12\", index: 11 } }";
3453 let plan_debug_str = format!("{plan:?}");
3454 assert!(plan_debug_str.contains("GlobalLimitExec"));
3455 assert!(plan_debug_str.contains("skip: 3"));
3456 Ok(())
3457 }
3458
3459 #[tokio::test]
3460 async fn error_during_extension_planning() {
3461 let session_state = make_session_state();
3462 let planner = DefaultPhysicalPlanner::with_extension_planners(vec![Arc::new(
3463 ErrorExtensionPlanner {},
3464 )]);
3465
3466 let logical_plan = LogicalPlan::Extension(Extension {
3467 node: Arc::new(NoOpExtensionNode::default()),
3468 });
3469 match planner
3470 .create_physical_plan(&logical_plan, &session_state)
3471 .await
3472 {
3473 Ok(_) => panic!("Expected planning failure"),
3474 Err(e) => assert!(e.to_string().contains("BOOM"),),
3475 }
3476 }
3477
3478 #[tokio::test]
3479 async fn test_with_zero_offset_plan() -> Result<()> {
3480 let logical_plan = test_csv_scan().await?.limit(0, None)?.build()?;
3481 let plan = plan(&logical_plan).await?;
3482 assert!(!format!("{plan:?}").contains("limit="));
3483 Ok(())
3484 }
3485
3486 #[tokio::test]
3487 async fn test_limit_with_partitions() -> Result<()> {
3488 let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
3489
3490 let logical_plan = scan_empty_with_partitions(Some("test"), &schema, None, 2)?
3491 .limit(3, Some(5))?
3492 .build()?;
3493 let plan = plan(&logical_plan).await?;
3494
3495 assert!(format!("{plan:?}").contains("GlobalLimitExec"));
3496 assert!(format!("{plan:?}").contains("skip: 3, fetch: Some(5)"));
3497
3498 Ok(())
3499 }
3500
3501 #[tokio::test]
3502 async fn errors() -> Result<()> {
3503 let bool_expr = col("c1").eq(col("c1"));
3504 let cases = vec![
3505 col("c1").eq(col("c1")),
3507 col("c3").bitand(col("c3")),
3509 col("c1").eq(col("c3")),
3511 bool_expr.clone().and(bool_expr),
3513 ];
3514 for case in cases {
3515 test_csv_scan().await?.project(vec![case.clone()]).unwrap();
3516 }
3517 Ok(())
3518 }
3519
3520 #[tokio::test]
3521 async fn default_extension_planner() {
3522 let session_state = make_session_state();
3523 let planner = DefaultPhysicalPlanner::default();
3524 let logical_plan = LogicalPlan::Extension(Extension {
3525 node: Arc::new(NoOpExtensionNode::default()),
3526 });
3527 let plan = planner
3528 .create_physical_plan(&logical_plan, &session_state)
3529 .await;
3530
3531 let expected_error = "No installed planner was able to convert the custom node to an execution plan: NoOp";
3532 match plan {
3533 Ok(_) => panic!("Expected planning failure"),
3534 Err(e) => assert!(
3535 e.to_string().contains(expected_error),
3536 "Error '{e}' did not contain expected error '{expected_error}'"
3537 ),
3538 }
3539 }
3540
3541 #[tokio::test]
3542 async fn bad_extension_planner() {
3543 let session_state = make_session_state();
3546 let planner = DefaultPhysicalPlanner::with_extension_planners(vec![Arc::new(
3547 BadExtensionPlanner {},
3548 )]);
3549
3550 let logical_plan = LogicalPlan::Extension(Extension {
3551 node: Arc::new(NoOpExtensionNode::default()),
3552 });
3553 let e = planner
3554 .create_physical_plan(&logical_plan, &session_state)
3555 .await
3556 .expect_err("planning error")
3557 .strip_backtrace();
3558
3559 insta::assert_snapshot!(e, @r#"Error during planning: Extension planner for NoOp created an ExecutionPlan with mismatched schema. LogicalPlan schema: DFSchema { inner: Schema { fields: [Field { name: "a", data_type: Int32 }], metadata: {} }, field_qualifiers: [None], functional_dependencies: FunctionalDependencies { deps: [] } }, ExecutionPlan schema: Schema { fields: [Field { name: "b", data_type: Int32 }], metadata: {} }"#);
3560 }
3561
3562 #[tokio::test]
3563 async fn in_list_types() -> Result<()> {
3564 let list = vec![lit("a"), lit(1i64)];
3566 let logical_plan = test_csv_scan()
3567 .await?
3568 .filter(col("c12").lt(lit(0.05)))?
3570 .project(vec![col("c1").in_list(list, false)])?
3571 .build()?;
3572 let execution_plan = plan(&logical_plan).await?;
3573 let expected = r#"expr: BinaryExpr { left: BinaryExpr { left: Column { name: "c1", index: 0 }, op: Eq, right: Literal { value: Utf8("a"), field: Field { name: "lit", data_type: Utf8 } }, fail_on_overflow: false }"#;
3576
3577 assert_contains!(format!("{execution_plan:?}"), expected);
3578
3579 Ok(())
3580 }
3581
3582 #[tokio::test]
3583 async fn in_list_types_struct_literal() -> Result<()> {
3584 let list = vec![struct_literal(), lit("a")];
3586
3587 let logical_plan = test_csv_scan()
3588 .await?
3589 .filter(col("c12").lt(lit(0.05)))?
3591 .project(vec![col("c12").lt_eq(lit(0.025)).in_list(list, false)])?
3592 .build()?;
3593 let e = plan(&logical_plan).await.unwrap_err().to_string();
3594
3595 assert_contains!(
3596 &e,
3597 r#"Error during planning: Can not find compatible types to compare Boolean with [Struct("foo": non-null Boolean), Utf8]"#
3598 );
3599
3600 Ok(())
3601 }
3602
3603 fn struct_literal() -> Expr {
3605 let struct_literal = ScalarValue::try_from(DataType::Struct(
3606 vec![Field::new("foo", DataType::Boolean, false)].into(),
3607 ))
3608 .unwrap();
3609
3610 lit(struct_literal)
3611 }
3612
3613 #[tokio::test]
3614 async fn hash_agg_input_schema() -> Result<()> {
3615 let logical_plan = test_csv_scan_with_name("aggregate_test_100")
3616 .await?
3617 .aggregate(vec![col("c1")], vec![sum(col("c2"))])?
3618 .build()?;
3619
3620 let execution_plan = plan(&logical_plan).await?;
3621 let final_hash_agg = execution_plan
3622 .as_any()
3623 .downcast_ref::<AggregateExec>()
3624 .expect("hash aggregate");
3625 assert_eq!(
3626 "sum(aggregate_test_100.c2)",
3627 final_hash_agg.schema().field(1).name()
3628 );
3629 assert_eq!("c2", final_hash_agg.input_schema().field(1).name());
3632
3633 Ok(())
3634 }
3635
3636 #[tokio::test]
3637 async fn hash_agg_grouping_set_input_schema() -> Result<()> {
3638 let grouping_set_expr = Expr::GroupingSet(GroupingSet::GroupingSets(vec![
3639 vec![col("c1")],
3640 vec![col("c2")],
3641 vec![col("c1"), col("c2")],
3642 ]));
3643 let logical_plan = test_csv_scan_with_name("aggregate_test_100")
3644 .await?
3645 .aggregate(vec![grouping_set_expr], vec![sum(col("c3"))])?
3646 .build()?;
3647
3648 let execution_plan = plan(&logical_plan).await?;
3649 let final_hash_agg = execution_plan
3650 .as_any()
3651 .downcast_ref::<AggregateExec>()
3652 .expect("hash aggregate");
3653 assert_eq!(
3654 "sum(aggregate_test_100.c3)",
3655 final_hash_agg.schema().field(3).name()
3656 );
3657 assert_eq!("c3", final_hash_agg.input_schema().field(2).name());
3660
3661 Ok(())
3662 }
3663
3664 #[tokio::test]
3665 async fn hash_agg_group_by_partitioned() -> Result<()> {
3666 let logical_plan = test_csv_scan()
3667 .await?
3668 .aggregate(vec![col("c1")], vec![sum(col("c2"))])?
3669 .build()?;
3670
3671 let execution_plan = plan(&logical_plan).await?;
3672 let formatted = format!("{execution_plan:?}");
3673
3674 assert!(formatted.contains("FinalPartitioned"));
3677
3678 Ok(())
3679 }
3680
3681 #[tokio::test]
3682 async fn hash_agg_group_by_partitioned_on_dicts() -> Result<()> {
3683 let dict_array: DictionaryArray<Int32Type> =
3684 vec!["A", "B", "A", "A", "C", "A"].into_iter().collect();
3685 let val_array: Int32Array = vec![1, 2, 2, 4, 1, 1].into();
3686
3687 let batch = RecordBatch::try_from_iter(vec![
3688 ("d1", Arc::new(dict_array) as ArrayRef),
3689 ("d2", Arc::new(val_array) as ArrayRef),
3690 ])
3691 .unwrap();
3692
3693 let table = MemTable::try_new(batch.schema(), vec![vec![batch]])?;
3694 let ctx = SessionContext::new();
3695
3696 let logical_plan = LogicalPlanBuilder::from(
3697 ctx.read_table(Arc::new(table))?.into_optimized_plan()?,
3698 )
3699 .aggregate(vec![col("d1")], vec![sum(col("d2"))])?
3700 .build()?;
3701
3702 let execution_plan = plan(&logical_plan).await?;
3703 let formatted = format!("{execution_plan:?}");
3704
3705 assert!(formatted.contains("FinalPartitioned"));
3708 Ok(())
3709 }
3710
3711 #[tokio::test]
3712 async fn hash_agg_grouping_set_by_partitioned() -> Result<()> {
3713 let grouping_set_expr = Expr::GroupingSet(GroupingSet::GroupingSets(vec![
3714 vec![col("c1")],
3715 vec![col("c2")],
3716 vec![col("c1"), col("c2")],
3717 ]));
3718 let logical_plan = test_csv_scan()
3719 .await?
3720 .aggregate(vec![grouping_set_expr], vec![sum(col("c3"))])?
3721 .build()?;
3722
3723 let execution_plan = plan(&logical_plan).await?;
3724 let formatted = format!("{execution_plan:?}");
3725
3726 assert!(formatted.contains("FinalPartitioned"));
3729
3730 Ok(())
3731 }
3732
3733 #[tokio::test]
3734 async fn aggregate_with_alias() -> Result<()> {
3735 let schema = Arc::new(Schema::new(vec![
3736 Field::new("c1", DataType::Utf8, false),
3737 Field::new("c2", DataType::UInt32, false),
3738 ]));
3739
3740 let logical_plan = scan_empty(None, schema.as_ref(), None)?
3741 .aggregate(vec![col("c1")], vec![sum(col("c2"))])?
3742 .project(vec![col("c1"), sum(col("c2")).alias("total_salary")])?
3743 .build()?;
3744
3745 let physical_plan = plan(&logical_plan).await?;
3746 assert_eq!("c1", physical_plan.schema().field(0).name().as_str());
3747 assert_eq!(
3748 "total_salary",
3749 physical_plan.schema().field(1).name().as_str()
3750 );
3751 Ok(())
3752 }
3753
3754 #[tokio::test]
3755 async fn test_aggregate_count_all_with_alias() -> Result<()> {
3756 let schema = Arc::new(Schema::new(vec![
3757 Field::new("c1", DataType::Utf8, false),
3758 Field::new("c2", DataType::UInt32, false),
3759 ]));
3760
3761 let logical_plan = scan_empty(None, schema.as_ref(), None)?
3762 .aggregate(Vec::<Expr>::new(), vec![count_all().alias("total_rows")])?
3763 .build()?;
3764
3765 let physical_plan = plan(&logical_plan).await?;
3766 assert_eq!(
3767 "total_rows",
3768 physical_plan.schema().field(0).name().as_str()
3769 );
3770 Ok(())
3771 }
3772
3773 #[tokio::test]
3774 async fn test_explain() {
3775 let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
3776
3777 let logical_plan = scan_empty(Some("employee"), &schema, None)
3778 .unwrap()
3779 .explain(true, false)
3780 .unwrap()
3781 .build()
3782 .unwrap();
3783
3784 let plan = plan(&logical_plan).await.unwrap();
3785 if let Some(plan) = plan.as_any().downcast_ref::<ExplainExec>() {
3786 let stringified_plans = plan.stringified_plans();
3787 assert!(stringified_plans.len() >= 4);
3788 assert!(
3789 stringified_plans
3790 .iter()
3791 .any(|p| p.plan_type == PlanType::FinalLogicalPlan)
3792 );
3793 assert!(
3794 stringified_plans
3795 .iter()
3796 .any(|p| p.plan_type == PlanType::InitialPhysicalPlan)
3797 );
3798 assert!(
3799 stringified_plans.iter().any(|p| matches!(
3800 p.plan_type,
3801 PlanType::OptimizedPhysicalPlan { .. }
3802 ))
3803 );
3804 assert!(
3805 stringified_plans
3806 .iter()
3807 .any(|p| p.plan_type == PlanType::FinalPhysicalPlan)
3808 );
3809 } else {
3810 panic!(
3811 "Plan was not an explain plan: {}",
3812 displayable(plan.as_ref()).indent(true)
3813 );
3814 }
3815 }
3816
3817 #[tokio::test]
3818 async fn test_explain_indent_err() {
3819 let planner = DefaultPhysicalPlanner::default();
3820 let ctx = SessionContext::new();
3821 let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
3822 let plan = Arc::new(
3823 scan_empty(Some("employee"), &schema, None)
3824 .unwrap()
3825 .explain(true, false)
3826 .unwrap()
3827 .build()
3828 .unwrap(),
3829 );
3830
3831 let schema = Arc::new(Schema::new(vec![
3833 Field::new("plan_type", DataType::Utf8, false),
3834 Field::new("plan", DataType::Utf8, false),
3835 ]));
3836
3837 let stringified_plans =
3839 vec![StringifiedPlan::new(PlanType::FinalLogicalPlan, "Test Err")];
3840
3841 let explain = Explain {
3842 verbose: false,
3843 explain_format: ExplainFormat::Indent,
3844 plan,
3845 stringified_plans,
3846 schema: schema.to_dfschema_ref().unwrap(),
3847 logical_optimization_succeeded: false,
3848 };
3849 let plan = planner
3850 .handle_explain(&explain, &ctx.state())
3851 .await
3852 .unwrap();
3853 if let Some(plan) = plan.as_any().downcast_ref::<ExplainExec>() {
3854 let stringified_plans = plan.stringified_plans();
3855 assert_eq!(stringified_plans.len(), 1);
3856 assert_eq!(stringified_plans[0].plan.as_str(), "Test Err");
3857 } else {
3858 panic!(
3859 "Plan was not an explain plan: {}",
3860 displayable(plan.as_ref()).indent(true)
3861 );
3862 }
3863 }
3864
3865 struct ErrorExtensionPlanner {}
3866
3867 #[async_trait]
3868 impl ExtensionPlanner for ErrorExtensionPlanner {
3869 async fn plan_extension(
3871 &self,
3872 _planner: &dyn PhysicalPlanner,
3873 _node: &dyn UserDefinedLogicalNode,
3874 _logical_inputs: &[&LogicalPlan],
3875 _physical_inputs: &[Arc<dyn ExecutionPlan>],
3876 _session_state: &SessionState,
3877 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
3878 internal_err!("BOOM")
3879 }
3880 }
3881 #[derive(PartialEq, Eq, Hash)]
3883 struct NoOpExtensionNode {
3884 schema: DFSchemaRef,
3885 }
3886
3887 impl Default for NoOpExtensionNode {
3888 fn default() -> Self {
3889 Self {
3890 schema: DFSchemaRef::new(
3891 DFSchema::from_unqualified_fields(
3892 vec![Field::new("a", DataType::Int32, false)].into(),
3893 HashMap::new(),
3894 )
3895 .unwrap(),
3896 ),
3897 }
3898 }
3899 }
3900
3901 impl Debug for NoOpExtensionNode {
3902 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3903 write!(f, "NoOp")
3904 }
3905 }
3906
3907 impl PartialOrd for NoOpExtensionNode {
3910 fn partial_cmp(&self, _other: &Self) -> Option<Ordering> {
3911 None
3912 }
3913 }
3914
3915 impl UserDefinedLogicalNodeCore for NoOpExtensionNode {
3916 fn name(&self) -> &str {
3917 "NoOp"
3918 }
3919
3920 fn inputs(&self) -> Vec<&LogicalPlan> {
3921 vec![]
3922 }
3923
3924 fn schema(&self) -> &DFSchemaRef {
3925 &self.schema
3926 }
3927
3928 fn expressions(&self) -> Vec<Expr> {
3929 vec![]
3930 }
3931
3932 fn fmt_for_explain(&self, f: &mut fmt::Formatter) -> fmt::Result {
3933 write!(f, "NoOp")
3934 }
3935
3936 fn with_exprs_and_inputs(
3937 &self,
3938 _exprs: Vec<Expr>,
3939 _inputs: Vec<LogicalPlan>,
3940 ) -> Result<Self> {
3941 unimplemented!("NoOp");
3942 }
3943
3944 fn supports_limit_pushdown(&self) -> bool {
3945 false }
3947 }
3948
3949 #[derive(Debug)]
3950 struct NoOpExecutionPlan {
3951 cache: Arc<PlanProperties>,
3952 }
3953
3954 impl NoOpExecutionPlan {
3955 fn new(schema: SchemaRef) -> Self {
3956 let cache = Self::compute_properties(schema);
3957 Self {
3958 cache: Arc::new(cache),
3959 }
3960 }
3961
3962 fn compute_properties(schema: SchemaRef) -> PlanProperties {
3964 PlanProperties::new(
3965 EquivalenceProperties::new(schema),
3966 Partitioning::UnknownPartitioning(1),
3967 EmissionType::Incremental,
3968 Boundedness::Bounded,
3969 )
3970 }
3971 }
3972
3973 impl DisplayAs for NoOpExecutionPlan {
3974 fn fmt_as(&self, t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result {
3975 match t {
3976 DisplayFormatType::Default | DisplayFormatType::Verbose => {
3977 write!(f, "NoOpExecutionPlan")
3978 }
3979 DisplayFormatType::TreeRender => {
3980 write!(f, "")
3982 }
3983 }
3984 }
3985 }
3986
3987 impl ExecutionPlan for NoOpExecutionPlan {
3988 fn name(&self) -> &'static str {
3989 "NoOpExecutionPlan"
3990 }
3991
3992 fn as_any(&self) -> &dyn Any {
3994 self
3995 }
3996
3997 fn properties(&self) -> &Arc<PlanProperties> {
3998 &self.cache
3999 }
4000
4001 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
4002 vec![]
4003 }
4004
4005 fn with_new_children(
4006 self: Arc<Self>,
4007 _children: Vec<Arc<dyn ExecutionPlan>>,
4008 ) -> Result<Arc<dyn ExecutionPlan>> {
4009 unimplemented!("NoOpExecutionPlan::with_new_children");
4010 }
4011
4012 fn execute(
4013 &self,
4014 _partition: usize,
4015 _context: Arc<TaskContext>,
4016 ) -> Result<SendableRecordBatchStream> {
4017 unimplemented!("NoOpExecutionPlan::execute");
4018 }
4019 }
4020
4021 struct BadExtensionPlanner {}
4024
4025 #[async_trait]
4026 impl ExtensionPlanner for BadExtensionPlanner {
4027 async fn plan_extension(
4029 &self,
4030 _planner: &dyn PhysicalPlanner,
4031 _node: &dyn UserDefinedLogicalNode,
4032 _logical_inputs: &[&LogicalPlan],
4033 _physical_inputs: &[Arc<dyn ExecutionPlan>],
4034 _session_state: &SessionState,
4035 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
4036 Ok(Some(Arc::new(NoOpExecutionPlan::new(SchemaRef::new(
4037 Schema::new(vec![Field::new("b", DataType::Int32, false)]),
4038 )))))
4039 }
4040 }
4041
4042 async fn test_csv_scan_with_name(name: &str) -> Result<LogicalPlanBuilder> {
4043 let ctx = SessionContext::new();
4044 let testdata = crate::test_util::arrow_test_data();
4045 let path = format!("{testdata}/csv/aggregate_test_100.csv");
4046 let options = CsvReadOptions::new().schema_infer_max_records(100);
4047 let logical_plan =
4048 match ctx.read_csv(path, options).await?.into_optimized_plan()? {
4049 LogicalPlan::TableScan(ref scan) => {
4050 let mut scan = scan.clone();
4051 let table_reference = TableReference::from(name);
4052 scan.table_name = table_reference;
4053 let new_schema = scan
4054 .projected_schema
4055 .as_ref()
4056 .clone()
4057 .replace_qualifier(name.to_string());
4058 scan.projected_schema = Arc::new(new_schema);
4059 LogicalPlan::TableScan(scan)
4060 }
4061 _ => unimplemented!(),
4062 };
4063 Ok(LogicalPlanBuilder::from(logical_plan))
4064 }
4065
4066 async fn test_csv_scan() -> Result<LogicalPlanBuilder> {
4067 let ctx = SessionContext::new();
4068 let testdata = crate::test_util::arrow_test_data();
4069 let path = format!("{testdata}/csv/aggregate_test_100.csv");
4070 let options = CsvReadOptions::new().schema_infer_max_records(100);
4071 Ok(LogicalPlanBuilder::from(
4072 ctx.read_csv(path, options).await?.into_optimized_plan()?,
4073 ))
4074 }
4075
4076 #[tokio::test]
4077 async fn test_display_plan_in_graphviz_format() {
4078 let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
4079
4080 let logical_plan = scan_empty(Some("employee"), &schema, None)
4081 .unwrap()
4082 .project(vec![col("id") + lit(2)])
4083 .unwrap()
4084 .build()
4085 .unwrap();
4086
4087 let plan = plan(&logical_plan).await.unwrap();
4088
4089 let expected_graph = r#"
4090// Begin DataFusion GraphViz Plan,
4091// display it online here: https://dreampuf.github.io/GraphvizOnline
4092
4093digraph {
4094 1[shape=box label="ProjectionExec: expr=[id@0 + 2 as employee.id + Int32(2)]", tooltip=""]
4095 2[shape=box label="EmptyExec", tooltip=""]
4096 1 -> 2 [arrowhead=none, arrowtail=normal, dir=back]
4097}
4098// End DataFusion GraphViz Plan
4099"#;
4100
4101 let generated_graph = format!("{}", displayable(&*plan).graphviz());
4102
4103 assert_eq!(expected_graph, generated_graph);
4104 }
4105
4106 #[tokio::test]
4107 async fn test_display_graphviz_with_statistics() {
4108 let schema = Schema::new(vec![Field::new("id", DataType::Int32, false)]);
4109
4110 let logical_plan = scan_empty(Some("employee"), &schema, None)
4111 .unwrap()
4112 .project(vec![col("id") + lit(2)])
4113 .unwrap()
4114 .build()
4115 .unwrap();
4116
4117 let plan = plan(&logical_plan).await.unwrap();
4118
4119 let expected_tooltip = ", tooltip=\"statistics=[";
4120
4121 let generated_graph = format!(
4122 "{}",
4123 displayable(&*plan).set_show_statistics(true).graphviz()
4124 );
4125
4126 assert_contains!(generated_graph, expected_tooltip);
4127 }
4128
4129 #[derive(Debug)]
4131 struct OkExtensionNode(Vec<Arc<dyn ExecutionPlan>>);
4132 impl ExecutionPlan for OkExtensionNode {
4133 fn name(&self) -> &str {
4134 "always ok"
4135 }
4136 fn with_new_children(
4137 self: Arc<Self>,
4138 children: Vec<Arc<dyn ExecutionPlan>>,
4139 ) -> Result<Arc<dyn ExecutionPlan>> {
4140 Ok(Arc::new(Self(children)))
4141 }
4142 fn schema(&self) -> SchemaRef {
4143 Arc::new(Schema::empty())
4144 }
4145 fn as_any(&self) -> &dyn Any {
4146 unimplemented!()
4147 }
4148 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
4149 self.0.iter().collect::<Vec<_>>()
4150 }
4151 fn properties(&self) -> &Arc<PlanProperties> {
4152 unimplemented!()
4153 }
4154 fn execute(
4155 &self,
4156 _partition: usize,
4157 _context: Arc<TaskContext>,
4158 ) -> Result<SendableRecordBatchStream> {
4159 unimplemented!()
4160 }
4161 }
4162 impl DisplayAs for OkExtensionNode {
4163 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result {
4164 write!(f, "{}", self.name())
4165 }
4166 }
4167
4168 #[derive(Debug)]
4170 struct InvariantFailsExtensionNode;
4171 impl ExecutionPlan for InvariantFailsExtensionNode {
4172 fn name(&self) -> &str {
4173 "InvariantFailsExtensionNode"
4174 }
4175 fn check_invariants(&self, check: InvariantLevel) -> Result<()> {
4176 match check {
4177 InvariantLevel::Always => plan_err!(
4178 "extension node failed it's user-defined always-invariant check"
4179 ),
4180 InvariantLevel::Executable => panic!(
4181 "the OptimizationInvariantChecker should not be checking for executableness"
4182 ),
4183 }
4184 }
4185 fn schema(&self) -> SchemaRef {
4186 Arc::new(Schema::empty())
4187 }
4188 fn with_new_children(
4189 self: Arc<Self>,
4190 _children: Vec<Arc<dyn ExecutionPlan>>,
4191 ) -> Result<Arc<dyn ExecutionPlan>> {
4192 unimplemented!()
4193 }
4194 fn as_any(&self) -> &dyn Any {
4195 unimplemented!()
4196 }
4197 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
4198 unimplemented!()
4199 }
4200 fn properties(&self) -> &Arc<PlanProperties> {
4201 unimplemented!()
4202 }
4203 fn execute(
4204 &self,
4205 _partition: usize,
4206 _context: Arc<TaskContext>,
4207 ) -> Result<SendableRecordBatchStream> {
4208 unimplemented!()
4209 }
4210 }
4211 impl DisplayAs for InvariantFailsExtensionNode {
4212 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result {
4213 write!(f, "{}", self.name())
4214 }
4215 }
4216
4217 #[derive(Debug)]
4219 struct OptimizerRuleWithSchemaCheck;
4220 impl PhysicalOptimizerRule for OptimizerRuleWithSchemaCheck {
4221 fn optimize(
4222 &self,
4223 plan: Arc<dyn ExecutionPlan>,
4224 _config: &ConfigOptions,
4225 ) -> Result<Arc<dyn ExecutionPlan>> {
4226 Ok(plan)
4227 }
4228 fn name(&self) -> &str {
4229 "OptimizerRuleWithSchemaCheck"
4230 }
4231 fn schema_check(&self) -> bool {
4232 true
4233 }
4234 }
4235
4236 #[test]
4237 fn test_optimization_invariant_checker() -> Result<()> {
4238 let rule: Arc<dyn PhysicalOptimizerRule + Send + Sync> =
4239 Arc::new(OptimizerRuleWithSchemaCheck);
4240
4241 let ok_node: Arc<dyn ExecutionPlan> = Arc::new(OkExtensionNode(vec![]));
4243 let child = Arc::clone(&ok_node);
4244 let ok_plan = Arc::clone(&ok_node).with_new_children(vec![
4245 Arc::clone(&child).with_new_children(vec![Arc::clone(&child)])?,
4246 Arc::clone(&child),
4247 ])?;
4248
4249 let equal_schema = ok_plan.schema();
4251 OptimizationInvariantChecker::new(&rule).check(&ok_plan, &equal_schema)?;
4252
4253 let different_schema =
4255 Arc::new(Schema::new(vec![Field::new("a", DataType::Boolean, false)]));
4256 let expected_err = OptimizationInvariantChecker::new(&rule)
4257 .check(&ok_plan, &different_schema)
4258 .unwrap_err();
4259 assert!(expected_err.to_string().contains("PhysicalOptimizer rule 'OptimizerRuleWithSchemaCheck' failed. Schema mismatch. Expected original schema"));
4260
4261 let failing_node: Arc<dyn ExecutionPlan> = Arc::new(InvariantFailsExtensionNode);
4263 let expected_err = OptimizationInvariantChecker::new(&rule)
4264 .check(&failing_node, &ok_plan.schema())
4265 .unwrap_err();
4266 assert!(
4267 expected_err.to_string().contains(
4268 "extension node failed it's user-defined always-invariant check"
4269 )
4270 );
4271
4272 let failing_node: Arc<dyn ExecutionPlan> = Arc::new(InvariantFailsExtensionNode);
4274 let invalid_plan = ok_node.with_new_children(vec![
4275 Arc::clone(&child).with_new_children(vec![Arc::clone(&failing_node)])?,
4276 Arc::clone(&child),
4277 ])?;
4278 let expected_err = OptimizationInvariantChecker::new(&rule)
4279 .check(&invalid_plan, &ok_plan.schema())
4280 .unwrap_err();
4281 assert!(
4282 expected_err.to_string().contains(
4283 "extension node failed it's user-defined always-invariant check"
4284 )
4285 );
4286
4287 Ok(())
4288 }
4289
4290 #[derive(Debug)]
4293 struct ExecutableInvariantFails;
4294 impl ExecutionPlan for ExecutableInvariantFails {
4295 fn name(&self) -> &str {
4296 "ExecutableInvariantFails"
4297 }
4298 fn check_invariants(&self, check: InvariantLevel) -> Result<()> {
4299 match check {
4300 InvariantLevel::Always => Ok(()),
4301 InvariantLevel::Executable => plan_err!(
4302 "extension node failed it's user-defined executable-invariant check"
4303 ),
4304 }
4305 }
4306 fn schema(&self) -> SchemaRef {
4307 Arc::new(Schema::empty())
4308 }
4309 fn with_new_children(
4310 self: Arc<Self>,
4311 _children: Vec<Arc<dyn ExecutionPlan>>,
4312 ) -> Result<Arc<dyn ExecutionPlan>> {
4313 unimplemented!()
4314 }
4315 fn as_any(&self) -> &dyn Any {
4316 unimplemented!()
4317 }
4318 fn children(&self) -> Vec<&Arc<dyn ExecutionPlan>> {
4319 vec![]
4320 }
4321 fn properties(&self) -> &Arc<PlanProperties> {
4322 unimplemented!()
4323 }
4324 fn execute(
4325 &self,
4326 _partition: usize,
4327 _context: Arc<TaskContext>,
4328 ) -> Result<SendableRecordBatchStream> {
4329 unimplemented!()
4330 }
4331 }
4332 impl DisplayAs for ExecutableInvariantFails {
4333 fn fmt_as(&self, _t: DisplayFormatType, f: &mut fmt::Formatter) -> fmt::Result {
4334 write!(f, "{}", self.name())
4335 }
4336 }
4337
4338 #[test]
4339 fn test_invariant_checker_levels() -> Result<()> {
4340 let plan: Arc<dyn ExecutionPlan> = Arc::new(ExecutableInvariantFails);
4342
4343 InvariantChecker(InvariantLevel::Always).check(&plan)?;
4345
4346 let expected_err = InvariantChecker(InvariantLevel::Executable)
4348 .check(&plan)
4349 .unwrap_err();
4350 assert!(expected_err.to_string().contains(
4351 "extension node failed it's user-defined executable-invariant check"
4352 ));
4353
4354 let failing_node: Arc<dyn ExecutionPlan> = Arc::new(ExecutableInvariantFails);
4356 let ok_node: Arc<dyn ExecutionPlan> = Arc::new(OkExtensionNode(vec![]));
4357 let child = Arc::clone(&ok_node);
4358 let plan = ok_node.with_new_children(vec![
4359 Arc::clone(&child).with_new_children(vec![Arc::clone(&failing_node)])?,
4360 Arc::clone(&child),
4361 ])?;
4362 let expected_err = InvariantChecker(InvariantLevel::Executable)
4363 .check(&plan)
4364 .unwrap_err();
4365 assert!(expected_err.to_string().contains(
4366 "extension node failed it's user-defined executable-invariant check"
4367 ));
4368
4369 Ok(())
4370 }
4371
4372 #[tokio::test]
4390 async fn subquery_alias_confusing_the_optimizer() -> Result<()> {
4391 let state = make_session_state();
4392
4393 let schema = Schema::new(vec![Field::new("a", DataType::Int32, false)]);
4394 let schema = Arc::new(schema);
4395
4396 let table = MemTable::try_new(schema.clone(), vec![vec![]])?;
4397 let table = Arc::new(table);
4398
4399 let source = DefaultTableSource::new(table);
4400 let source = Arc::new(source);
4401
4402 let left = LogicalPlanBuilder::scan("left", source.clone(), None)?;
4403 let right = LogicalPlanBuilder::scan("right", source, None)?.build()?;
4404
4405 let join_keys = (
4406 vec![Column::new(Some("left"), "a")],
4407 vec![Column::new(Some("right"), "a")],
4408 );
4409
4410 let join = left.join(right, JoinType::Full, join_keys, None)?.build()?;
4411
4412 let alias = subquery_alias(join, "alias")?;
4413
4414 let planner = DefaultPhysicalPlanner::default();
4415
4416 let logical_plan = LogicalPlanBuilder::new(alias)
4417 .aggregate(vec![col("a:1")], Vec::<Expr>::new())?
4418 .build()?;
4419 let _physical_plan = planner.create_physical_plan(&logical_plan, &state).await?;
4420
4421 let optimized_logical_plan = state.optimize(&logical_plan)?;
4422 let _optimized_physical_plan = planner
4423 .create_physical_plan(&optimized_logical_plan, &state)
4424 .await?;
4425
4426 Ok(())
4427 }
4428
4429 use crate::catalog::TableProvider;
4432 use datafusion_catalog::Session;
4433 use datafusion_expr::TableType;
4434
4435 #[derive(Debug)]
4438 struct MockSchemaTableProvider {
4439 logical_schema: SchemaRef,
4440 physical_schema: SchemaRef,
4441 }
4442
4443 #[async_trait]
4444 impl TableProvider for MockSchemaTableProvider {
4445 fn as_any(&self) -> &dyn Any {
4446 self
4447 }
4448
4449 fn schema(&self) -> SchemaRef {
4450 Arc::clone(&self.logical_schema)
4451 }
4452
4453 fn table_type(&self) -> TableType {
4454 TableType::Base
4455 }
4456
4457 async fn scan(
4458 &self,
4459 _state: &dyn Session,
4460 _projection: Option<&Vec<usize>>,
4461 _filters: &[Expr],
4462 _limit: Option<usize>,
4463 ) -> Result<Arc<dyn ExecutionPlan>> {
4464 Ok(Arc::new(NoOpExecutionPlan::new(Arc::clone(
4465 &self.physical_schema,
4466 ))))
4467 }
4468 }
4469
4470 async fn plan_with_schemas(
4472 logical_schema: SchemaRef,
4473 physical_schema: SchemaRef,
4474 query: &str,
4475 ) -> Result<Arc<dyn ExecutionPlan>> {
4476 let provider = MockSchemaTableProvider {
4477 logical_schema,
4478 physical_schema,
4479 };
4480 let ctx = SessionContext::new();
4481 ctx.register_table("test", Arc::new(provider)).unwrap();
4482
4483 ctx.sql(query).await.unwrap().create_physical_plan().await
4484 }
4485
4486 #[tokio::test]
4487 #[should_panic(expected = "NoOpExecutionPlan")]
4490 async fn test_aggregate_schema_check_passes() {
4491 let schema =
4492 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4493
4494 plan_with_schemas(
4495 Arc::clone(&schema),
4496 schema,
4497 "SELECT count(*) FROM test GROUP BY c1",
4498 )
4499 .await
4500 .unwrap();
4501 }
4502
4503 #[tokio::test]
4504 async fn test_aggregate_schema_mismatch_metadata() {
4505 let logical_schema =
4506 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4507 let physical_schema = Arc::new(
4508 Schema::new(vec![Field::new("c1", DataType::Int32, false)])
4509 .with_metadata(HashMap::from([("key".into(), "value".into())])),
4510 );
4511
4512 let err = plan_with_schemas(
4513 logical_schema,
4514 physical_schema,
4515 "SELECT count(*) FROM test GROUP BY c1",
4516 )
4517 .await
4518 .unwrap_err();
4519
4520 assert_contains!(err.to_string(), "schema metadata differs");
4521 }
4522
4523 #[tokio::test]
4524 async fn test_aggregate_schema_mismatch_field_count() {
4525 let logical_schema =
4526 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4527 let physical_schema = Arc::new(Schema::new(vec![
4528 Field::new("c1", DataType::Int32, false),
4529 Field::new("c2", DataType::Int32, false),
4530 ]));
4531
4532 let err = plan_with_schemas(
4533 logical_schema,
4534 physical_schema,
4535 "SELECT count(*) FROM test GROUP BY c1",
4536 )
4537 .await
4538 .unwrap_err();
4539
4540 assert_contains!(err.to_string(), "Different number of fields");
4541 }
4542
4543 #[tokio::test]
4544 async fn test_aggregate_schema_mismatch_field_name() {
4545 let logical_schema =
4546 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4547 let physical_schema = Arc::new(Schema::new(vec![Field::new(
4548 "different_name",
4549 DataType::Int32,
4550 false,
4551 )]));
4552
4553 let err = plan_with_schemas(
4554 logical_schema,
4555 physical_schema,
4556 "SELECT count(*) FROM test GROUP BY c1",
4557 )
4558 .await
4559 .unwrap_err();
4560
4561 assert_contains!(err.to_string(), "field name at index");
4562 }
4563
4564 #[tokio::test]
4565 async fn test_aggregate_schema_mismatch_field_type() {
4566 let logical_schema =
4567 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4568 let physical_schema =
4569 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int64, false)]));
4570
4571 let err = plan_with_schemas(
4572 logical_schema,
4573 physical_schema,
4574 "SELECT count(*) FROM test GROUP BY c1",
4575 )
4576 .await
4577 .unwrap_err();
4578
4579 assert_contains!(err.to_string(), "field data type at index");
4580 }
4581
4582 #[tokio::test]
4583 async fn test_aggregate_schema_mismatch_field_nullability() {
4584 let logical_schema =
4585 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4586 let physical_schema =
4587 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, true)]));
4588
4589 let err = plan_with_schemas(
4590 logical_schema,
4591 physical_schema,
4592 "SELECT count(*) FROM test GROUP BY c1",
4593 )
4594 .await
4595 .unwrap_err();
4596
4597 assert_contains!(err.to_string(), "field nullability at index");
4598 }
4599
4600 #[tokio::test]
4601 async fn test_aggregate_schema_mismatch_field_metadata() {
4602 let logical_schema =
4603 Arc::new(Schema::new(vec![Field::new("c1", DataType::Int32, false)]));
4604 let physical_schema = Arc::new(Schema::new(vec![
4605 Field::new("c1", DataType::Int32, false)
4606 .with_metadata(HashMap::from([("key".into(), "value".into())])),
4607 ]));
4608
4609 let err = plan_with_schemas(
4610 logical_schema,
4611 physical_schema,
4612 "SELECT count(*) FROM test GROUP BY c1",
4613 )
4614 .await
4615 .unwrap_err();
4616
4617 assert_contains!(err.to_string(), "field metadata at index");
4618 }
4619
4620 #[tokio::test]
4621 async fn test_aggregate_schema_mismatch_multiple() {
4622 let logical_schema = Arc::new(Schema::new(vec![
4623 Field::new("c1", DataType::Int32, false),
4624 Field::new("c2", DataType::Utf8, false),
4625 ]));
4626 let physical_schema = Arc::new(
4627 Schema::new(vec![
4628 Field::new("c1", DataType::Int64, true)
4629 .with_metadata(HashMap::from([("key".into(), "value".into())])),
4630 Field::new("c2", DataType::Utf8, false),
4631 ])
4632 .with_metadata(HashMap::from([(
4633 "schema_key".into(),
4634 "schema_value".into(),
4635 )])),
4636 );
4637
4638 let err = plan_with_schemas(
4639 logical_schema,
4640 physical_schema,
4641 "SELECT count(*) FROM test GROUP BY c1",
4642 )
4643 .await
4644 .unwrap_err();
4645
4646 let err_str = err.to_string();
4648 assert_contains!(&err_str, "schema metadata differs");
4649 assert_contains!(&err_str, "field data type at index");
4650 assert_contains!(&err_str, "field nullability at index");
4651 assert_contains!(&err_str, "field metadata at index");
4652 }
4653
4654 #[derive(Debug)]
4655 struct MockTableSource {
4656 schema: SchemaRef,
4657 }
4658
4659 impl TableSource for MockTableSource {
4660 fn as_any(&self) -> &dyn Any {
4661 self
4662 }
4663
4664 fn schema(&self) -> SchemaRef {
4665 Arc::clone(&self.schema)
4666 }
4667 }
4668
4669 struct MockTableScanExtensionPlanner;
4670
4671 #[async_trait]
4672 impl ExtensionPlanner for MockTableScanExtensionPlanner {
4673 async fn plan_extension(
4674 &self,
4675 _planner: &dyn PhysicalPlanner,
4676 _node: &dyn UserDefinedLogicalNode,
4677 _logical_inputs: &[&LogicalPlan],
4678 _physical_inputs: &[Arc<dyn ExecutionPlan>],
4679 _session_state: &SessionState,
4680 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
4681 Ok(None)
4682 }
4683
4684 async fn plan_table_scan(
4685 &self,
4686 _planner: &dyn PhysicalPlanner,
4687 scan: &TableScan,
4688 _session_state: &SessionState,
4689 ) -> Result<Option<Arc<dyn ExecutionPlan>>> {
4690 if scan.source.as_any().is::<MockTableSource>() {
4691 Ok(Some(Arc::new(EmptyExec::new(Arc::clone(
4692 scan.projected_schema.inner(),
4693 )))))
4694 } else {
4695 Ok(None)
4696 }
4697 }
4698 }
4699
4700 #[tokio::test]
4701 async fn test_table_scan_extension_planner() {
4702 let session_state = make_session_state();
4703 let planner = Arc::new(MockTableScanExtensionPlanner);
4704 let physical_planner =
4705 DefaultPhysicalPlanner::with_extension_planners(vec![planner]);
4706
4707 let schema = Arc::new(Schema::new(vec![Field::new("a", DataType::Int32, false)]));
4708
4709 let table_source = Arc::new(MockTableSource {
4710 schema: Arc::clone(&schema),
4711 });
4712 let logical_plan = LogicalPlanBuilder::scan("test", table_source, None)
4713 .unwrap()
4714 .build()
4715 .unwrap();
4716
4717 let plan = physical_planner
4718 .create_physical_plan(&logical_plan, &session_state)
4719 .await
4720 .unwrap();
4721
4722 assert_eq!(plan.schema(), schema);
4723 assert!(plan.as_any().is::<EmptyExec>());
4724 }
4725}