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