@@ -263,10 +263,11 @@ pub trait ExecutionPlan: Debug + DisplayAs + Send + Sync {
263263 /// &self,
264264 /// f: &mut dyn FnMut(&dyn PhysicalExpr) -> Result<TreeNodeRecursion>,
265265 /// ) -> Result<TreeNodeRecursion> {
266+ /// let mut tnr = TreeNodeRecursion::Continue;
266267 /// for expr in &self.expressions {
267- /// f(expr.as_ref())?;
268+ /// tnr = tnr.visit_sibling(|| f(expr.as_ref() ))?;
268269 /// }
269- /// Ok(TreeNodeRecursion::Continue )
270+ /// Ok(tnr )
270271 /// }
271272 /// ```
272273 fn apply_expressions (
@@ -1649,6 +1650,110 @@ mod tests {
16491650 }
16501651 }
16511652
1653+ /// A test node that holds a fixed list of expressions, used to test
1654+ /// `apply_expressions` behavior.
1655+ #[ derive( Debug ) ]
1656+ struct MultiExprExec {
1657+ exprs : Vec < Arc < dyn PhysicalExpr > > ,
1658+ }
1659+
1660+ impl DisplayAs for MultiExprExec {
1661+ fn fmt_as (
1662+ & self ,
1663+ _t : DisplayFormatType ,
1664+ _f : & mut std:: fmt:: Formatter ,
1665+ ) -> std:: fmt:: Result {
1666+ unimplemented ! ( )
1667+ }
1668+ }
1669+
1670+ impl ExecutionPlan for MultiExprExec {
1671+ fn name ( & self ) -> & ' static str {
1672+ "MultiExprExec"
1673+ }
1674+
1675+ fn as_any ( & self ) -> & dyn Any {
1676+ self
1677+ }
1678+
1679+ fn properties ( & self ) -> & PlanProperties {
1680+ unimplemented ! ( )
1681+ }
1682+
1683+ fn children ( & self ) -> Vec < & Arc < dyn ExecutionPlan > > {
1684+ vec ! [ ]
1685+ }
1686+
1687+ fn with_new_children (
1688+ self : Arc < Self > ,
1689+ _: Vec < Arc < dyn ExecutionPlan > > ,
1690+ ) -> Result < Arc < dyn ExecutionPlan > > {
1691+ unimplemented ! ( )
1692+ }
1693+
1694+ fn apply_expressions (
1695+ & self ,
1696+ f : & mut dyn FnMut ( & dyn PhysicalExpr ) -> Result < TreeNodeRecursion > ,
1697+ ) -> Result < TreeNodeRecursion > {
1698+ let mut tnr = TreeNodeRecursion :: Continue ;
1699+ for expr in & self . exprs {
1700+ tnr = tnr. visit_sibling ( || f ( expr. as_ref ( ) ) ) ?;
1701+ }
1702+ Ok ( tnr)
1703+ }
1704+
1705+ fn execute (
1706+ & self ,
1707+ _partition : usize ,
1708+ _context : Arc < TaskContext > ,
1709+ ) -> Result < SendableRecordBatchStream > {
1710+ unimplemented ! ( )
1711+ }
1712+
1713+ fn partition_statistics ( & self , _partition : Option < usize > ) -> Result < Statistics > {
1714+ unimplemented ! ( )
1715+ }
1716+ }
1717+
1718+ /// Returns a simple literal `Arc<dyn PhysicalExpr>` for use in tests.
1719+ fn lit_expr ( val : i64 ) -> Arc < dyn PhysicalExpr > {
1720+ use datafusion_physical_expr:: expressions:: Literal ;
1721+ Arc :: new ( Literal :: new ( datafusion_common:: ScalarValue :: Int64 ( Some (
1722+ val,
1723+ ) ) ) )
1724+ }
1725+
1726+ /// `apply_expressions` visits all expressions when `f` always returns `Continue`.
1727+ #[ test]
1728+ fn test_apply_expressions_continue_visits_all ( ) -> Result < ( ) > {
1729+ let plan = MultiExprExec {
1730+ exprs : vec ! [ lit_expr( 1 ) , lit_expr( 2 ) , lit_expr( 3 ) ] ,
1731+ } ;
1732+ let mut visited = 0usize ;
1733+ plan. apply_expressions ( & mut |_expr| {
1734+ visited += 1 ;
1735+ Ok ( TreeNodeRecursion :: Continue )
1736+ } ) ?;
1737+ assert_eq ! ( visited, 3 ) ;
1738+ Ok ( ( ) )
1739+ }
1740+
1741+ #[ test]
1742+ fn test_apply_expressions_stop_halts_early ( ) -> Result < ( ) > {
1743+ let plan = MultiExprExec {
1744+ exprs : vec ! [ lit_expr( 1 ) , lit_expr( 2 ) , lit_expr( 3 ) ] ,
1745+ } ;
1746+ let mut visited = 0usize ;
1747+ let tnr = plan. apply_expressions ( & mut |_expr| {
1748+ visited += 1 ;
1749+ Ok ( TreeNodeRecursion :: Stop )
1750+ } ) ?;
1751+ // Only the first expression is visited; the rest are skipped.
1752+ assert_eq ! ( visited, 1 ) ;
1753+ assert_eq ! ( tnr, TreeNodeRecursion :: Stop ) ;
1754+ Ok ( ( ) )
1755+ }
1756+
16521757 #[ test]
16531758 fn test_execution_plan_name ( ) {
16541759 let schema1 = Arc :: new ( Schema :: empty ( ) ) ;
0 commit comments