Skip to content

Commit 8ed430d

Browse files
committed
add generic test for apply_expressions API
1 parent b4abb77 commit 8ed430d

File tree

1 file changed

+107
-2
lines changed

1 file changed

+107
-2
lines changed

datafusion/physical-plan/src/execution_plan.rs

Lines changed: 107 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)