Skip to content

Commit 6d93a85

Browse files
berkaysynnadamustafasrepo
authored andcommitted
Refactors on TreeNode Implementations (apache#8395)
* minor changes * PipelineStatePropagator tree refactor * Remove duplications by children_unbounded() * Remove on-the-fly tree construction * Minor changes --------- Co-authored-by: Mustafa Akur <mustafa.akur@synnada.ai>
1 parent d214ebe commit 6d93a85

5 files changed

Lines changed: 65 additions & 71 deletions

File tree

datafusion/core/src/physical_optimizer/join_selection.rs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,7 @@ fn hash_join_convert_symmetric_subrule(
434434
config_options: &ConfigOptions,
435435
) -> Option<Result<PipelineStatePropagator>> {
436436
if let Some(hash_join) = input.plan.as_any().downcast_ref::<HashJoinExec>() {
437-
let ub_flags = &input.children_unbounded;
437+
let ub_flags = input.children_unbounded();
438438
let (left_unbounded, right_unbounded) = (ub_flags[0], ub_flags[1]);
439439
input.unbounded = left_unbounded || right_unbounded;
440440
let result = if left_unbounded && right_unbounded {
@@ -511,7 +511,7 @@ fn hash_join_swap_subrule(
511511
_config_options: &ConfigOptions,
512512
) -> Option<Result<PipelineStatePropagator>> {
513513
if let Some(hash_join) = input.plan.as_any().downcast_ref::<HashJoinExec>() {
514-
let ub_flags = &input.children_unbounded;
514+
let ub_flags = input.children_unbounded();
515515
let (left_unbounded, right_unbounded) = (ub_flags[0], ub_flags[1]);
516516
input.unbounded = left_unbounded || right_unbounded;
517517
let result = if left_unbounded
@@ -577,7 +577,7 @@ fn apply_subrules(
577577
}
578578
let is_unbounded = input
579579
.plan
580-
.unbounded_output(&input.children_unbounded)
580+
.unbounded_output(&input.children_unbounded())
581581
// Treat the case where an operator can not run on unbounded data as
582582
// if it can and it outputs unbounded data. Do not raise an error yet.
583583
// Such operators may be fixed, adjusted or replaced later on during
@@ -1253,6 +1253,7 @@ mod hash_join_tests {
12531253
use arrow::record_batch::RecordBatch;
12541254
use datafusion_common::utils::DataPtr;
12551255
use datafusion_common::JoinType;
1256+
use datafusion_physical_plan::empty::EmptyExec;
12561257
use std::sync::Arc;
12571258

12581259
struct TestCase {
@@ -1620,10 +1621,22 @@ mod hash_join_tests {
16201621
false,
16211622
)?;
16221623

1624+
let children = vec![
1625+
PipelineStatePropagator {
1626+
plan: Arc::new(EmptyExec::new(false, Arc::new(Schema::empty()))),
1627+
unbounded: left_unbounded,
1628+
children: vec![],
1629+
},
1630+
PipelineStatePropagator {
1631+
plan: Arc::new(EmptyExec::new(false, Arc::new(Schema::empty()))),
1632+
unbounded: right_unbounded,
1633+
children: vec![],
1634+
},
1635+
];
16231636
let initial_hash_join_state = PipelineStatePropagator {
16241637
plan: Arc::new(join),
16251638
unbounded: false,
1626-
children_unbounded: vec![left_unbounded, right_unbounded],
1639+
children,
16271640
};
16281641

16291642
let optimized_hash_join =

datafusion/core/src/physical_optimizer/pipeline_checker.rs

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -70,29 +70,36 @@ impl PhysicalOptimizerRule for PipelineChecker {
7070
pub struct PipelineStatePropagator {
7171
pub(crate) plan: Arc<dyn ExecutionPlan>,
7272
pub(crate) unbounded: bool,
73-
pub(crate) children_unbounded: Vec<bool>,
73+
pub(crate) children: Vec<PipelineStatePropagator>,
7474
}
7575

7676
impl PipelineStatePropagator {
7777
/// Constructs a new, default pipelining state.
7878
pub fn new(plan: Arc<dyn ExecutionPlan>) -> Self {
79-
let length = plan.children().len();
79+
let children = plan.children();
8080
PipelineStatePropagator {
8181
plan,
8282
unbounded: false,
83-
children_unbounded: vec![false; length],
83+
children: children.into_iter().map(Self::new).collect(),
8484
}
8585
}
86+
87+
/// Returns the children unboundedness information.
88+
pub fn children_unbounded(&self) -> Vec<bool> {
89+
self.children
90+
.iter()
91+
.map(|c| c.unbounded)
92+
.collect::<Vec<_>>()
93+
}
8694
}
8795

8896
impl TreeNode for PipelineStatePropagator {
8997
fn apply_children<F>(&self, op: &mut F) -> Result<VisitRecursion>
9098
where
9199
F: FnMut(&Self) -> Result<VisitRecursion>,
92100
{
93-
let children = self.plan.children();
94-
for child in children {
95-
match op(&PipelineStatePropagator::new(child))? {
101+
for child in &self.children {
102+
match op(child)? {
96103
VisitRecursion::Continue => {}
97104
VisitRecursion::Skip => return Ok(VisitRecursion::Continue),
98105
VisitRecursion::Stop => return Ok(VisitRecursion::Stop),
@@ -106,25 +113,18 @@ impl TreeNode for PipelineStatePropagator {
106113
where
107114
F: FnMut(Self) -> Result<Self>,
108115
{
109-
let children = self.plan.children();
110-
if !children.is_empty() {
111-
let new_children = children
116+
if !self.children.is_empty() {
117+
let new_children = self
118+
.children
112119
.into_iter()
113-
.map(PipelineStatePropagator::new)
114120
.map(transform)
115121
.collect::<Result<Vec<_>>>()?;
116-
let children_unbounded = new_children
117-
.iter()
118-
.map(|c| c.unbounded)
119-
.collect::<Vec<bool>>();
120-
let children_plans = new_children
121-
.into_iter()
122-
.map(|child| child.plan)
123-
.collect::<Vec<_>>();
122+
let children_plans = new_children.iter().map(|c| c.plan.clone()).collect();
123+
124124
Ok(PipelineStatePropagator {
125125
plan: with_new_children_if_necessary(self.plan, children_plans)?.into(),
126126
unbounded: self.unbounded,
127-
children_unbounded,
127+
children: new_children,
128128
})
129129
} else {
130130
Ok(self)
@@ -149,7 +149,7 @@ pub fn check_finiteness_requirements(
149149
}
150150
input
151151
.plan
152-
.unbounded_output(&input.children_unbounded)
152+
.unbounded_output(&input.children_unbounded())
153153
.map(|value| {
154154
input.unbounded = value;
155155
Transformed::Yes(input)

datafusion/physical-expr/src/equivalence.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ fn update_ordering(
15201520
node.state = SortProperties::Ordered(options);
15211521
} else if !node.expr.children().is_empty() {
15221522
// We have an intermediate (non-leaf) node, account for its children:
1523-
node.state = node.expr.get_ordering(&node.children_states);
1523+
node.state = node.expr.get_ordering(&node.children_state());
15241524
} else if node.expr.as_any().is::<Literal>() {
15251525
// We have a Literal, which is the other possible leaf node type:
15261526
node.state = node.expr.get_ordering(&[]);

datafusion/physical-expr/src/sort_properties.rs

Lines changed: 21 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717

1818
use std::{ops::Neg, sync::Arc};
1919

20-
use crate::PhysicalExpr;
2120
use arrow_schema::SortOptions;
21+
22+
use crate::PhysicalExpr;
2223
use datafusion_common::tree_node::{TreeNode, VisitRecursion};
2324
use datafusion_common::Result;
2425

25-
use itertools::Itertools;
26-
2726
/// To propagate [`SortOptions`] across the [`PhysicalExpr`], it is insufficient
2827
/// to simply use `Option<SortOptions>`: There must be a differentiation between
2928
/// unordered columns and literal values, since literals may not break the ordering
@@ -35,11 +34,12 @@ use itertools::Itertools;
3534
/// sorted data; however the ((a_ordered + 999) + c_ordered) expression can. Therefore,
3635
/// we need two different variants for literals and unordered columns as literals are
3736
/// often more ordering-friendly under most mathematical operations.
38-
#[derive(PartialEq, Debug, Clone, Copy)]
37+
#[derive(PartialEq, Debug, Clone, Copy, Default)]
3938
pub enum SortProperties {
4039
/// Use the ordinary [`SortOptions`] struct to represent ordered data:
4140
Ordered(SortOptions),
4241
// This alternative represents unordered data:
42+
#[default]
4343
Unordered,
4444
// Singleton is used for single-valued literal numbers:
4545
Singleton,
@@ -151,34 +151,24 @@ impl Neg for SortProperties {
151151
pub struct ExprOrdering {
152152
pub expr: Arc<dyn PhysicalExpr>,
153153
pub state: SortProperties,
154-
pub children_states: Vec<SortProperties>,
154+
pub children: Vec<ExprOrdering>,
155155
}
156156

157157
impl ExprOrdering {
158158
/// Creates a new [`ExprOrdering`] with [`SortProperties::Unordered`] states
159159
/// for `expr` and its children.
160160
pub fn new(expr: Arc<dyn PhysicalExpr>) -> Self {
161-
let size = expr.children().len();
161+
let children = expr.children();
162162
Self {
163163
expr,
164-
state: SortProperties::Unordered,
165-
children_states: vec![SortProperties::Unordered; size],
164+
state: Default::default(),
165+
children: children.into_iter().map(Self::new).collect(),
166166
}
167167
}
168168

169-
/// Updates this [`ExprOrdering`]'s children states with the given states.
170-
pub fn with_new_children(mut self, children_states: Vec<SortProperties>) -> Self {
171-
self.children_states = children_states;
172-
self
173-
}
174-
175-
/// Creates new [`ExprOrdering`] objects for each child of the expression.
176-
pub fn children_expr_orderings(&self) -> Vec<ExprOrdering> {
177-
self.expr
178-
.children()
179-
.into_iter()
180-
.map(ExprOrdering::new)
181-
.collect()
169+
/// Get a reference to each child state.
170+
pub fn children_state(&self) -> Vec<SortProperties> {
171+
self.children.iter().map(|c| c.state).collect()
182172
}
183173
}
184174

@@ -187,8 +177,8 @@ impl TreeNode for ExprOrdering {
187177
where
188178
F: FnMut(&Self) -> Result<VisitRecursion>,
189179
{
190-
for child in self.children_expr_orderings() {
191-
match op(&child)? {
180+
for child in &self.children {
181+
match op(child)? {
192182
VisitRecursion::Continue => {}
193183
VisitRecursion::Skip => return Ok(VisitRecursion::Continue),
194184
VisitRecursion::Stop => return Ok(VisitRecursion::Stop),
@@ -197,25 +187,19 @@ impl TreeNode for ExprOrdering {
197187
Ok(VisitRecursion::Continue)
198188
}
199189

200-
fn map_children<F>(self, transform: F) -> Result<Self>
190+
fn map_children<F>(mut self, transform: F) -> Result<Self>
201191
where
202192
F: FnMut(Self) -> Result<Self>,
203193
{
204-
if self.children_states.is_empty() {
194+
if self.children.is_empty() {
205195
Ok(self)
206196
} else {
207-
let child_expr_orderings = self.children_expr_orderings();
208-
// After mapping over the children, the function `F` applies to the
209-
// current object and updates its state.
210-
Ok(self.with_new_children(
211-
child_expr_orderings
212-
.into_iter()
213-
// Update children states after this transformation:
214-
.map(transform)
215-
// Extract the state (i.e. sort properties) information:
216-
.map_ok(|c| c.state)
217-
.collect::<Result<Vec<_>>>()?,
218-
))
197+
self.children = self
198+
.children
199+
.into_iter()
200+
.map(transform)
201+
.collect::<Result<Vec<_>>>()?;
202+
Ok(self)
219203
}
220204
}
221205
}

datafusion/physical-expr/src/utils.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -129,23 +129,20 @@ pub struct ExprTreeNode<T> {
129129

130130
impl<T> ExprTreeNode<T> {
131131
pub fn new(expr: Arc<dyn PhysicalExpr>) -> Self {
132+
let children = expr.children();
132133
ExprTreeNode {
133134
expr,
134135
data: None,
135-
child_nodes: vec![],
136+
child_nodes: children.into_iter().map(Self::new).collect_vec(),
136137
}
137138
}
138139

139140
pub fn expression(&self) -> &Arc<dyn PhysicalExpr> {
140141
&self.expr
141142
}
142143

143-
pub fn children(&self) -> Vec<ExprTreeNode<T>> {
144-
self.expr
145-
.children()
146-
.into_iter()
147-
.map(ExprTreeNode::new)
148-
.collect()
144+
pub fn children(&self) -> &[ExprTreeNode<T>] {
145+
&self.child_nodes
149146
}
150147
}
151148

@@ -155,7 +152,7 @@ impl<T: Clone> TreeNode for ExprTreeNode<T> {
155152
F: FnMut(&Self) -> Result<VisitRecursion>,
156153
{
157154
for child in self.children() {
158-
match op(&child)? {
155+
match op(child)? {
159156
VisitRecursion::Continue => {}
160157
VisitRecursion::Skip => return Ok(VisitRecursion::Continue),
161158
VisitRecursion::Stop => return Ok(VisitRecursion::Stop),
@@ -170,7 +167,7 @@ impl<T: Clone> TreeNode for ExprTreeNode<T> {
170167
F: FnMut(Self) -> Result<Self>,
171168
{
172169
self.child_nodes = self
173-
.children()
170+
.child_nodes
174171
.into_iter()
175172
.map(transform)
176173
.collect::<Result<Vec<_>>>()?;

0 commit comments

Comments
 (0)