@@ -48,7 +48,7 @@ use crate::stream::RecordBatchStreamAdapter;
4848use arrow:: array:: { Array , RecordBatch } ;
4949use arrow:: datatypes:: SchemaRef ;
5050use datafusion_common:: config:: ConfigOptions ;
51- use datafusion_common:: { exec_err, Constraints , Result } ;
51+ use datafusion_common:: { exec_err, Constraints , DataFusionError , Result } ;
5252use datafusion_common_runtime:: JoinSet ;
5353use datafusion_execution:: TaskContext ;
5454use datafusion_physical_expr:: EquivalenceProperties ;
@@ -118,10 +118,11 @@ pub trait ExecutionPlan: Debug + DisplayAs + Send + Sync {
118118 /// Returns an error if this individual node does not conform to its invariants.
119119 /// These invariants are typically only checked in debug mode.
120120 ///
121- /// A default set of invariants is provided in the default implementation.
121+ /// A default set of invariants is provided in the [check_default_invariants] function.
122+ /// The default implementation of `check_invariants` calls this function.
122123 /// Extension nodes can provide their own invariants.
123- fn check_invariants ( & self , _check : InvariantLevel ) -> Result < ( ) > {
124- Ok ( ( ) )
124+ fn check_invariants ( & self , check : InvariantLevel ) -> Result < ( ) > {
125+ check_default_invariants ( self , check )
125126 }
126127
127128 /// Specifies the data distribution requirements for all the
@@ -1045,6 +1046,37 @@ impl PlanProperties {
10451046 }
10461047}
10471048
1049+ macro_rules! check_len {
1050+ ( $target: expr, $func_name: ident, $expected_len: expr) => {
1051+ let actual_len = $target. $func_name( ) . len( ) ;
1052+ if actual_len != $expected_len {
1053+ return internal_err!(
1054+ "{}::{} returned Vec with incorrect size: {} != {}" ,
1055+ $target. name( ) ,
1056+ stringify!( $func_name) ,
1057+ actual_len,
1058+ $expected_len
1059+ ) ;
1060+ }
1061+ } ;
1062+ }
1063+
1064+ /// Checks a set of invariants that apply to all ExecutionPlan implementations.
1065+ /// Returns an error if the given node does not conform.
1066+ pub fn check_default_invariants < P : ExecutionPlan + ?Sized > (
1067+ plan : & P ,
1068+ _check : InvariantLevel ,
1069+ ) -> Result < ( ) , DataFusionError > {
1070+ let children_len = plan. children ( ) . len ( ) ;
1071+
1072+ check_len ! ( plan, maintains_input_order, children_len) ;
1073+ check_len ! ( plan, required_input_ordering, children_len) ;
1074+ check_len ! ( plan, required_input_distribution, children_len) ;
1075+ check_len ! ( plan, benefits_from_input_partitioning, children_len) ;
1076+
1077+ Ok ( ( ) )
1078+ }
1079+
10481080/// Indicate whether a data exchange is needed for the input of `plan`, which will be very helpful
10491081/// especially for the distributed engine to judge whether need to deal with shuffling.
10501082/// Currently, there are 3 kinds of execution plan which needs data exchange
0 commit comments