use crate::planning::plan::core::nodes::base::plan_node_traits::{
BinaryInputNode, MultipleInputNode, SingleInputNode,
};
use crate::planning::plan::PlanNodeEnum;
pub fn rewrite_children(
node: &PlanNodeEnum,
f: &mut impl FnMut(&PlanNodeEnum) -> PlanNodeEnum,
) -> PlanNodeEnum {
use PlanNodeEnum::*;
macro_rules! rewrite_single {
($n:expr) => {{
let mut cloned = $n.clone();
let new_input = f(cloned.input());
cloned.set_input(new_input);
cloned
}};
}
macro_rules! rewrite_binary {
($n:expr) => {{
let mut cloned = $n.clone();
let new_left = f(cloned.left_input());
let new_right = f(cloned.right_input());
cloned.set_left_input(new_left);
cloned.set_right_input(new_right);
cloned
}};
}
macro_rules! rewrite_multiple {
($n:expr) => {{
let mut cloned = $n.clone();
for child in cloned.inputs_mut() {
*child = f(child);
}
cloned
}};
}
match node {
Project(n) => Project(rewrite_single!(n)),
Filter(n) => Filter(rewrite_single!(n)),
Sort(n) => Sort(rewrite_single!(n)),
Limit(n) => Limit(rewrite_single!(n)),
TopN(n) => TopN(rewrite_single!(n)),
Sample(n) => Sample(rewrite_single!(n)),
Dedup(n) => Dedup(rewrite_single!(n)),
DataCollect(n) => DataCollect(rewrite_single!(n)),
Aggregate(n) => Aggregate(rewrite_single!(n)),
Window(n) => Window(rewrite_single!(n)),
Unwind(n) => Unwind(rewrite_single!(n)),
Assign(n) => Assign(rewrite_single!(n)),
PatternApply(n) => PatternApply(rewrite_single!(n)),
CorrelatedApply(n) => CorrelatedApply(rewrite_single!(n)),
RollUpApply(n) => RollUpApply(rewrite_single!(n)),
Materialize(n) => Materialize(rewrite_single!(n)),
Traverse(n) => Traverse(rewrite_single!(n)),
InnerJoin(n) => InnerJoin(rewrite_binary!(n)),
LeftJoin(n) => LeftJoin(rewrite_binary!(n)),
RightJoin(n) => RightJoin(rewrite_binary!(n)),
CrossJoin(n) => CrossJoin(rewrite_binary!(n)),
FullOuterJoin(n) => FullOuterJoin(rewrite_binary!(n)),
SemiJoin(n) => SemiJoin(rewrite_binary!(n)),
Apply(n) => Apply(rewrite_binary!(n)),
BiExpand(n) => BiExpand(rewrite_binary!(n)),
BiTraverse(n) => BiTraverse(rewrite_binary!(n)),
Expand(n) => Expand(rewrite_multiple!(n)),
ExpandAll(n) => ExpandAll(rewrite_multiple!(n)),
AppendVertices(n) => AppendVertices(rewrite_multiple!(n)),
Union(n) => {
let mut cloned = n.clone();
for child in cloned.dependencies_mut() {
*child = f(child);
}
let first = cloned.dependencies()[0].clone();
*cloned.input_mut() = first;
Union(cloned)
}
Minus(n) => {
let mut cloned = n.clone();
for child in cloned.dependencies_mut() {
*child = f(child);
}
let first = cloned.dependencies()[0].clone();
*cloned.input_mut() = first;
Minus(cloned)
}
Intersect(n) => {
let mut cloned = n.clone();
for child in cloned.dependencies_mut() {
*child = f(child);
}
let first = cloned.dependencies()[0].clone();
*cloned.input_mut() = first;
Intersect(cloned)
}
_ => node.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::planning::plan::core::nodes::control_flow::start_node::StartNode;
use crate::planning::plan::core::nodes::graph_operations::graph_operations_node::UnionNode;
use crate::planning::plan::core::nodes::graph_operations::set_operations_node::{
IntersectNode, MinusNode,
};
fn assert_two_deps_rewritten<F>(make_node: F)
where
F: Fn(
PlanNodeEnum,
PlanNodeEnum,
) -> Result<PlanNodeEnum, crate::planning::planner::PlannerError>,
{
let left = PlanNodeEnum::Start(StartNode::new());
let right = PlanNodeEnum::Start(StartNode::new());
let node = make_node(left, right).expect("node builds");
let mut visited = 0usize;
let rewritten = rewrite_children(&node, &mut |child: &PlanNodeEnum| {
visited += 1;
child.clone()
});
assert_eq!(visited, 2, "both deps must be rewritten");
let (deps_len, second_input_is_start) = match rewritten {
PlanNodeEnum::Union(n) => (
n.dependencies().len(),
matches!(n.union_input(), PlanNodeEnum::Start(_)),
),
PlanNodeEnum::Minus(n) => (
n.dependencies().len(),
matches!(n.minus_input(), PlanNodeEnum::Start(_)),
),
PlanNodeEnum::Intersect(n) => (
n.dependencies().len(),
matches!(n.intersect_input(), PlanNodeEnum::Start(_)),
),
other => panic!("expected Union/Minus/Intersect, got {:?}", other),
};
assert_eq!(deps_len, 2, "deps must keep both subtrees");
assert!(
second_input_is_start,
"union_input/minus_input/intersect_input survived the rewrite"
);
}
#[test]
fn rewrite_children_keeps_both_union_deps() {
assert_two_deps_rewritten(|left, right| {
UnionNode::new(left, right, true).map(PlanNodeEnum::Union)
});
}
#[test]
fn rewrite_children_keeps_both_minus_deps() {
assert_two_deps_rewritten(|left, right| {
MinusNode::new(left, right).map(PlanNodeEnum::Minus)
});
}
#[test]
fn rewrite_children_keeps_both_intersect_deps() {
assert_two_deps_rewritten(|left, right| {
IntersectNode::new(left, right).map(PlanNodeEnum::Intersect)
});
}
}