From e8007e3325119821e1d9d21a73ff485596a12928 Mon Sep 17 00:00:00 2001
From: Denis Smirnov <sd@picodata.io>
Date: Fri, 28 Oct 2022 18:15:58 +0700
Subject: [PATCH] refactoring: tree traversal

Split different traversal iterators to separate modules.
---
 sbroad-core/src/ir/tree.rs            | 450 +-------------------------
 sbroad-core/src/ir/tree/and.rs        |  76 +++++
 sbroad-core/src/ir/tree/eq_class.rs   |  76 +++++
 sbroad-core/src/ir/tree/expression.rs | 143 ++++++++
 sbroad-core/src/ir/tree/relation.rs   |  84 +++++
 sbroad-core/src/ir/tree/subtree.rs    | 221 +++++++++++++
 6 files changed, 612 insertions(+), 438 deletions(-)
 create mode 100644 sbroad-core/src/ir/tree/and.rs
 create mode 100644 sbroad-core/src/ir/tree/eq_class.rs
 create mode 100644 sbroad-core/src/ir/tree/expression.rs
 create mode 100644 sbroad-core/src/ir/tree/relation.rs
 create mode 100644 sbroad-core/src/ir/tree/subtree.rs

diff --git a/sbroad-core/src/ir/tree.rs b/sbroad-core/src/ir/tree.rs
index 656dbeeecb..ea174a34ae 100644
--- a/sbroad-core/src/ir/tree.rs
+++ b/sbroad-core/src/ir/tree.rs
@@ -1,449 +1,23 @@
 //! IR tree traversal module.
 
+use super::{Nodes, Plan};
 use std::cell::RefCell;
-use std::cmp::Ordering;
 
-use super::expression::Expression;
-use super::operator::{Bool, Relational};
-use super::{Node, Nodes, Plan};
-
-/// Relational node's child iterator.
-///
-/// The iterator returns the next relational node in the plan tree.
-#[derive(Debug)]
-pub struct RelationalIterator<'n> {
-    current: &'n usize,
-    child: RefCell<usize>,
-    nodes: &'n Nodes,
-}
-
-/// Expression node's children iterator.
-///
-/// The iterator returns the next child for expression
-/// nodes. It is required to use `traversal` crate.
-#[derive(Debug)]
-pub struct ExpressionIterator<'n> {
-    current: &'n usize,
-    child: RefCell<usize>,
-    nodes: &'n Nodes,
-    make_row_leaf: bool,
-}
-
-/// Expression and relational nodes iterator.
-#[derive(Debug)]
-pub struct SubtreeIterator<'p> {
-    current: &'p usize,
-    child: RefCell<usize>,
-    plan: &'p Plan,
+trait TreeIterator<'nodes> {
+    fn get_current(&self) -> &'nodes usize;
+    fn get_child(&self) -> &RefCell<usize>;
+    fn get_nodes(&self) -> &'nodes Nodes;
 }
 
-/// Children iterator for "and"-ed equivalent expressions.
-///
-/// The iterator returns the next child for the chained `Bool::And`
-/// and `Bool::Eq` nodes.
-#[derive(Debug)]
-pub struct EqClassIterator<'n> {
-    current: &'n usize,
-    child: RefCell<usize>,
-    nodes: &'n Nodes,
+trait PlanTreeIterator<'plan>: TreeIterator<'plan> {
+    fn get_plan(&self) -> &'plan Plan;
 }
 
-/// Children iterator for "and"-ed expression chains.
-///
-/// The iterator returns the next child for the chained `Bool::And` nodes.
-#[derive(Debug)]
-pub struct AndIterator<'n> {
-    current: &'n usize,
-    child: RefCell<usize>,
-    nodes: &'n Nodes,
-}
-
-impl<'n> Nodes {
-    #[must_use]
-    pub fn expr_iter(&'n self, current: &'n usize, make_row_leaf: bool) -> ExpressionIterator<'n> {
-        ExpressionIterator {
-            current,
-            child: RefCell::new(0),
-            nodes: self,
-            make_row_leaf,
-        }
-    }
-
-    #[must_use]
-    pub fn eq_iter(&'n self, current: &'n usize) -> EqClassIterator<'n> {
-        EqClassIterator {
-            current,
-            child: RefCell::new(0),
-            nodes: self,
-        }
-    }
-
-    #[must_use]
-    pub fn and_iter(&'n self, current: &'n usize) -> AndIterator<'n> {
-        AndIterator {
-            current,
-            child: RefCell::new(0),
-            nodes: self,
-        }
-    }
-
-    #[must_use]
-    pub fn rel_iter(&'n self, current: &'n usize) -> RelationalIterator<'n> {
-        RelationalIterator {
-            current,
-            child: RefCell::new(0),
-            nodes: self,
-        }
-    }
-}
-
-impl<'p> Plan {
-    #[must_use]
-    pub fn subtree_iter(&'p self, current: &'p usize) -> SubtreeIterator<'p> {
-        SubtreeIterator {
-            current,
-            child: RefCell::new(0),
-            plan: self,
-        }
-    }
-}
-
-impl<'n> Iterator for ExpressionIterator<'n> {
-    type Item = &'n usize;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.nodes.arena.get(*self.current) {
-            Some(Node::Expression(
-                Expression::Alias { child, .. }
-                | Expression::Cast { child, .. }
-                | Expression::Unary { child, .. },
-            )) => {
-                let child_step = *self.child.borrow();
-                if child_step == 0 {
-                    *self.child.borrow_mut() += 1;
-                    return Some(child);
-                }
-                None
-            }
-            Some(Node::Expression(
-                Expression::Bool { left, right, .. } | Expression::Concat { left, right },
-            )) => {
-                let child_step = *self.child.borrow();
-                if child_step == 0 {
-                    *self.child.borrow_mut() += 1;
-                    return Some(left);
-                } else if child_step == 1 {
-                    *self.child.borrow_mut() += 1;
-                    return Some(right);
-                }
-                None
-            }
-            Some(Node::Expression(Expression::Row { list, .. })) => {
-                let child_step = *self.child.borrow();
-                let mut is_leaf = false;
-
-                // Check on the first step, if the row contains only leaf nodes.
-                if child_step == 0 {
-                    is_leaf = true;
-                    for col in list {
-                        if !matches!(
-                            self.nodes.arena.get(*col),
-                            Some(Node::Expression(
-                                Expression::Reference { .. } | Expression::Constant { .. }
-                            ))
-                        ) {
-                            is_leaf = false;
-                            break;
-                        }
-                    }
-                }
-
-                // If the row contains only leaf nodes (or we don't want to go deeper
-                // into the row tree for some reasons), skip traversal.
-                if !is_leaf || !self.make_row_leaf {
-                    match list.get(child_step) {
-                        None => return None,
-                        Some(child) => {
-                            *self.child.borrow_mut() += 1;
-                            return Some(child);
-                        }
-                    }
-                }
-
-                None
-            }
-            Some(Node::Expression(Expression::StableFunction { children, .. })) => {
-                let child_step = *self.child.borrow();
-                match children.get(child_step) {
-                    None => None,
-                    Some(child) => {
-                        *self.child.borrow_mut() += 1;
-                        Some(child)
-                    }
-                }
-            }
-            Some(
-                Node::Expression(Expression::Constant { .. } | Expression::Reference { .. })
-                | Node::Relational(_)
-                | Node::Parameter,
-            )
-            | None => None,
-        }
-    }
-}
-
-impl<'n> Iterator for EqClassIterator<'n> {
-    type Item = &'n usize;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(Node::Expression(Expression::Bool {
-            left, op, right, ..
-        })) = self.nodes.arena.get(*self.current)
-        {
-            if (*op != Bool::And) && (*op != Bool::Eq) {
-                return None;
-            }
-            let child_step = *self.child.borrow();
-            if child_step == 0 {
-                *self.child.borrow_mut() += 1;
-                return Some(left);
-            } else if child_step == 1 {
-                *self.child.borrow_mut() += 1;
-                return Some(right);
-            }
-            None
-        } else {
-            None
-        }
-    }
-}
-
-impl<'n> Iterator for AndIterator<'n> {
-    type Item = &'n usize;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        let node = self.nodes.arena.get(*self.current);
-        if let Some(Node::Expression(Expression::Bool {
-            left, op, right, ..
-        })) = node
-        {
-            if *op != Bool::And {
-                return None;
-            }
-            let child_step = *self.child.borrow();
-            if child_step == 0 {
-                *self.child.borrow_mut() += 1;
-                return Some(left);
-            } else if child_step == 1 {
-                *self.child.borrow_mut() += 1;
-                return Some(right);
-            }
-            None
-        } else {
-            None
-        }
-    }
-}
-
-impl<'n> Iterator for RelationalIterator<'n> {
-    type Item = &'n usize;
-
-    fn next(&mut self) -> Option<Self::Item> {
-        match self.nodes.arena.get(*self.current) {
-            Some(Node::Relational(
-                Relational::Except { children, .. }
-                | Relational::InnerJoin { children, .. }
-                | Relational::Insert { children, .. }
-                | Relational::Motion { children, .. }
-                | Relational::Projection { children, .. }
-                | Relational::ScanSubQuery { children, .. }
-                | Relational::Selection { children, .. }
-                | Relational::UnionAll { children, .. }
-                | Relational::Values { children, .. }
-                | Relational::ValuesRow { children, .. },
-            )) => {
-                let step = *self.child.borrow();
-                if step < children.len() {
-                    *self.child.borrow_mut() += 1;
-                    return children.get(step);
-                }
-                None
-            }
-            Some(
-                Node::Relational(Relational::ScanRelation { .. })
-                | Node::Expression(_)
-                | Node::Parameter,
-            )
-            | None => None,
-        }
-    }
-}
-
-impl<'p> Iterator for SubtreeIterator<'p> {
-    type Item = &'p usize;
-
-    #[allow(clippy::too_many_lines)]
-    fn next(&mut self) -> Option<Self::Item> {
-        if let Some(child) = self.plan.nodes.arena.get(*self.current) {
-            return match child {
-                Node::Parameter => None,
-                Node::Expression(exp) => match exp {
-                    Expression::Alias { child, .. }
-                    | Expression::Cast { child, .. }
-                    | Expression::Unary { child, .. } => {
-                        let step = *self.child.borrow();
-                        *self.child.borrow_mut() += 1;
-                        if step == 0 {
-                            return Some(child);
-                        }
-                        None
-                    }
-                    Expression::Bool { left, right, .. } | Expression::Concat { left, right } => {
-                        let child_step = *self.child.borrow();
-                        if child_step == 0 {
-                            *self.child.borrow_mut() += 1;
-                            return Some(left);
-                        } else if child_step == 1 {
-                            *self.child.borrow_mut() += 1;
-                            return Some(right);
-                        }
-                        None
-                    }
-                    Expression::Row { list, .. }
-                    | Expression::StableFunction { children: list, .. } => {
-                        let child_step = *self.child.borrow();
-                        return match list.get(child_step) {
-                            None => None,
-                            Some(child) => {
-                                *self.child.borrow_mut() += 1;
-                                Some(child)
-                            }
-                        };
-                    }
-                    Expression::Constant { .. } => None,
-                    Expression::Reference { .. } => {
-                        let step = *self.child.borrow();
-                        if step == 0 {
-                            *self.child.borrow_mut() += 1;
-
-                            // At first we need to detect the place where the reference is used:
-                            // for selection filter or a join condition, we need to check whether
-                            // the reference points to an **additional** sub-query and then traverse
-                            // into it. Otherwise, stop traversal.
-                            let parent_id = match exp.get_parent() {
-                                Ok(parent_id) => parent_id,
-                                Err(_) => return None,
-                            };
-                            if let Ok(rel_id) =
-                                self.plan.get_relational_from_reference_node(*self.current)
-                            {
-                                match self.plan.get_relation_node(*rel_id) {
-                                    Ok(rel_node) if rel_node.is_subquery() => {
-                                        // Check if the sub-query is an additional one.
-                                        let parent = self.plan.get_relation_node(parent_id);
-                                        let mut is_additional = false;
-                                        if let Ok(Relational::Selection { children, .. }) = parent {
-                                            if children.iter().skip(1).any(|&c| c == *rel_id) {
-                                                is_additional = true;
-                                            }
-                                        }
-                                        if let Ok(Relational::InnerJoin { children, .. }) = parent {
-                                            if children.iter().skip(2).any(|&c| c == *rel_id) {
-                                                is_additional = true;
-                                            }
-                                        }
-                                        if is_additional {
-                                            return Some(rel_id);
-                                        }
-                                    }
-                                    _ => {}
-                                }
-                            }
-                        }
-                        None
-                    }
-                },
-
-                Node::Relational(r) => match r {
-                    Relational::InnerJoin {
-                        children,
-                        condition,
-                        ..
-                    } => {
-                        let step = *self.child.borrow();
-
-                        *self.child.borrow_mut() += 1;
-                        match step.cmp(&2) {
-                            Ordering::Less => {
-                                return children.get(step);
-                            }
-                            Ordering::Equal => {
-                                return Some(condition);
-                            }
-                            Ordering::Greater => None,
-                        }
-                    }
-
-                    Relational::Except { children, .. }
-                    | Relational::Insert { children, .. }
-                    | Relational::Motion { children, .. }
-                    | Relational::ScanSubQuery { children, .. }
-                    | Relational::UnionAll { children, .. } => {
-                        let step = *self.child.borrow();
-                        if step < children.len() {
-                            *self.child.borrow_mut() += 1;
-                            return children.get(step);
-                        }
-                        None
-                    }
-                    Relational::Values {
-                        output, children, ..
-                    }
-                    | Relational::Projection {
-                        output, children, ..
-                    } => {
-                        let step = *self.child.borrow();
-                        *self.child.borrow_mut() += 1;
-                        if step == 0 {
-                            return Some(output);
-                        }
-                        if step <= children.len() {
-                            return children.get(step - 1);
-                        }
-                        None
-                    }
-                    Relational::Selection {
-                        children, filter, ..
-                    } => {
-                        let step = *self.child.borrow();
-
-                        *self.child.borrow_mut() += 1;
-                        match step.cmp(&1) {
-                            Ordering::Less => {
-                                return children.get(step);
-                            }
-                            Ordering::Equal => {
-                                return Some(filter);
-                            }
-                            Ordering::Greater => None,
-                        }
-                    }
-                    Relational::ValuesRow { data, .. } => {
-                        let step = *self.child.borrow();
-
-                        *self.child.borrow_mut() += 1;
-                        if step == 0 {
-                            return Some(data);
-                        }
-                        None
-                    }
-                    Relational::ScanRelation { .. } => None,
-                },
-            };
-        }
-        None
-    }
-}
+pub mod and;
+pub mod eq_class;
+pub mod expression;
+pub mod relation;
+pub mod subtree;
 
 #[cfg(test)]
 mod tests;
diff --git a/sbroad-core/src/ir/tree/and.rs b/sbroad-core/src/ir/tree/and.rs
new file mode 100644
index 0000000000..f38259e079
--- /dev/null
+++ b/sbroad-core/src/ir/tree/and.rs
@@ -0,0 +1,76 @@
+use std::cell::RefCell;
+
+use super::TreeIterator;
+use crate::ir::expression::Expression;
+use crate::ir::operator::Bool;
+use crate::ir::{Node, Nodes};
+
+trait AndTreeIterator<'nodes>: TreeIterator<'nodes> {}
+
+/// Children iterator for "and"-ed expression chains.
+///
+/// The iterator returns the next child for the chained `Bool::And` nodes.
+#[derive(Debug)]
+pub struct AndIterator<'n> {
+    current: &'n usize,
+    child: RefCell<usize>,
+    nodes: &'n Nodes,
+}
+
+impl<'nodes> TreeIterator<'nodes> for AndIterator<'nodes> {
+    fn get_current(&self) -> &'nodes usize {
+        self.current
+    }
+
+    fn get_child(&self) -> &RefCell<usize> {
+        &self.child
+    }
+
+    fn get_nodes(&self) -> &'nodes Nodes {
+        self.nodes
+    }
+}
+
+impl<'nodes> AndTreeIterator<'nodes> for AndIterator<'nodes> {}
+
+impl<'n> Nodes {
+    #[must_use]
+    pub fn and_iter(&'n self, current: &'n usize) -> AndIterator<'n> {
+        AndIterator {
+            current,
+            child: RefCell::new(0),
+            nodes: self,
+        }
+    }
+}
+
+impl<'n> Iterator for AndIterator<'n> {
+    type Item = &'n usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        and_next(self)
+    }
+}
+
+fn and_next<'nodes>(iter: &mut impl AndTreeIterator<'nodes>) -> Option<&'nodes usize> {
+    let node = iter.get_nodes().arena.get(*iter.get_current());
+    if let Some(Node::Expression(Expression::Bool {
+        left, op, right, ..
+    })) = node
+    {
+        if *op != Bool::And {
+            return None;
+        }
+        let child_step = *iter.get_child().borrow();
+        if child_step == 0 {
+            *iter.get_child().borrow_mut() += 1;
+            return Some(left);
+        } else if child_step == 1 {
+            *iter.get_child().borrow_mut() += 1;
+            return Some(right);
+        }
+        None
+    } else {
+        None
+    }
+}
diff --git a/sbroad-core/src/ir/tree/eq_class.rs b/sbroad-core/src/ir/tree/eq_class.rs
new file mode 100644
index 0000000000..a96ad63b8c
--- /dev/null
+++ b/sbroad-core/src/ir/tree/eq_class.rs
@@ -0,0 +1,76 @@
+use std::cell::RefCell;
+
+use super::TreeIterator;
+use crate::ir::expression::Expression;
+use crate::ir::operator::Bool;
+use crate::ir::{Node, Nodes};
+
+trait EqClassTreeIterator<'nodes>: TreeIterator<'nodes> {}
+
+/// Children iterator for "and"-ed equivalent expressions.
+///
+/// The iterator returns the next child for the chained `Bool::And`
+/// and `Bool::Eq` nodes.
+#[derive(Debug)]
+pub struct EqClassIterator<'n> {
+    current: &'n usize,
+    child: RefCell<usize>,
+    nodes: &'n Nodes,
+}
+
+impl<'nodes> TreeIterator<'nodes> for EqClassIterator<'nodes> {
+    fn get_current(&self) -> &'nodes usize {
+        self.current
+    }
+
+    fn get_child(&self) -> &RefCell<usize> {
+        &self.child
+    }
+
+    fn get_nodes(&self) -> &'nodes Nodes {
+        self.nodes
+    }
+}
+
+impl<'nodes> EqClassTreeIterator<'nodes> for EqClassIterator<'nodes> {}
+
+impl<'n> Nodes {
+    #[must_use]
+    pub fn eq_iter(&'n self, current: &'n usize) -> EqClassIterator<'n> {
+        EqClassIterator {
+            current,
+            child: RefCell::new(0),
+            nodes: self,
+        }
+    }
+}
+
+impl<'n> Iterator for EqClassIterator<'n> {
+    type Item = &'n usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        eq_class_next(self)
+    }
+}
+
+fn eq_class_next<'nodes>(iter: &mut impl EqClassTreeIterator<'nodes>) -> Option<&'nodes usize> {
+    if let Some(Node::Expression(Expression::Bool {
+        left, op, right, ..
+    })) = iter.get_nodes().arena.get(*iter.get_current())
+    {
+        if (*op != Bool::And) && (*op != Bool::Eq) {
+            return None;
+        }
+        let child_step = *iter.get_child().borrow();
+        if child_step == 0 {
+            *iter.get_child().borrow_mut() += 1;
+            return Some(left);
+        } else if child_step == 1 {
+            *iter.get_child().borrow_mut() += 1;
+            return Some(right);
+        }
+        None
+    } else {
+        None
+    }
+}
diff --git a/sbroad-core/src/ir/tree/expression.rs b/sbroad-core/src/ir/tree/expression.rs
new file mode 100644
index 0000000000..aeafaebc88
--- /dev/null
+++ b/sbroad-core/src/ir/tree/expression.rs
@@ -0,0 +1,143 @@
+use std::cell::RefCell;
+
+use super::TreeIterator;
+use crate::ir::expression::Expression;
+use crate::ir::{Node, Nodes};
+
+trait ExpressionTreeIterator<'nodes>: TreeIterator<'nodes> {
+    fn get_make_row_leaf(&self) -> bool;
+}
+
+/// Expression node's children iterator.
+///
+/// The iterator returns the next child for expression
+/// nodes. It is required to use `traversal` crate.
+#[derive(Debug)]
+pub struct ExpressionIterator<'n> {
+    current: &'n usize,
+    child: RefCell<usize>,
+    nodes: &'n Nodes,
+    make_row_leaf: bool,
+}
+
+impl<'n> Nodes {
+    #[must_use]
+    pub fn expr_iter(&'n self, current: &'n usize, make_row_leaf: bool) -> ExpressionIterator<'n> {
+        ExpressionIterator {
+            current,
+            child: RefCell::new(0),
+            nodes: self,
+            make_row_leaf,
+        }
+    }
+}
+
+impl<'nodes> TreeIterator<'nodes> for ExpressionIterator<'nodes> {
+    fn get_current(&self) -> &'nodes usize {
+        self.current
+    }
+
+    fn get_child(&self) -> &RefCell<usize> {
+        &self.child
+    }
+
+    fn get_nodes(&self) -> &'nodes Nodes {
+        self.nodes
+    }
+}
+
+impl<'nodes> ExpressionTreeIterator<'nodes> for ExpressionIterator<'nodes> {
+    fn get_make_row_leaf(&self) -> bool {
+        self.make_row_leaf
+    }
+}
+
+impl<'n> Iterator for ExpressionIterator<'n> {
+    type Item = &'n usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        expression_next(self)
+    }
+}
+
+fn expression_next<'nodes>(
+    iter: &mut impl ExpressionTreeIterator<'nodes>,
+) -> Option<&'nodes usize> {
+    match iter.get_nodes().arena.get(*iter.get_current()) {
+        Some(Node::Expression(
+            Expression::Alias { child, .. }
+            | Expression::Cast { child, .. }
+            | Expression::Unary { child, .. },
+        )) => {
+            let child_step = *iter.get_child().borrow();
+            if child_step == 0 {
+                *iter.get_child().borrow_mut() += 1;
+                return Some(child);
+            }
+            None
+        }
+        Some(Node::Expression(
+            Expression::Bool { left, right, .. } | Expression::Concat { left, right },
+        )) => {
+            let child_step = *iter.get_child().borrow();
+            if child_step == 0 {
+                *iter.get_child().borrow_mut() += 1;
+                return Some(left);
+            } else if child_step == 1 {
+                *iter.get_child().borrow_mut() += 1;
+                return Some(right);
+            }
+            None
+        }
+        Some(Node::Expression(Expression::Row { list, .. })) => {
+            let child_step = *iter.get_child().borrow();
+            let mut is_leaf = false;
+
+            // Check on the first step, if the row contains only leaf nodes.
+            if child_step == 0 {
+                is_leaf = true;
+                for col in list {
+                    if !matches!(
+                        iter.get_nodes().arena.get(*col),
+                        Some(Node::Expression(
+                            Expression::Reference { .. } | Expression::Constant { .. }
+                        ))
+                    ) {
+                        is_leaf = false;
+                        break;
+                    }
+                }
+            }
+
+            // If the row contains only leaf nodes (or we don't want to go deeper
+            // into the row tree for some reasons), skip traversal.
+            if !is_leaf || !iter.get_make_row_leaf() {
+                match list.get(child_step) {
+                    None => return None,
+                    Some(child) => {
+                        *iter.get_child().borrow_mut() += 1;
+                        return Some(child);
+                    }
+                }
+            }
+
+            None
+        }
+        Some(Node::Expression(Expression::StableFunction { children, .. })) => {
+            let child_step = *iter.get_child().borrow();
+            match children.get(child_step) {
+                None => None,
+                Some(child) => {
+                    *iter.get_child().borrow_mut() += 1;
+                    Some(child)
+                }
+            }
+        }
+        Some(
+            Node::Expression(Expression::Constant { .. } | Expression::Reference { .. })
+            | Node::Relational(_)
+            | Node::Parameter,
+        )
+        | None => None,
+    }
+}
diff --git a/sbroad-core/src/ir/tree/relation.rs b/sbroad-core/src/ir/tree/relation.rs
new file mode 100644
index 0000000000..8bb0ff454c
--- /dev/null
+++ b/sbroad-core/src/ir/tree/relation.rs
@@ -0,0 +1,84 @@
+use std::cell::RefCell;
+
+use super::TreeIterator;
+use crate::ir::operator::Relational;
+use crate::ir::{Node, Nodes};
+
+trait RelationalTreeIterator<'nodes>: TreeIterator<'nodes> {}
+
+/// Relational node's child iterator.
+///
+/// The iterator returns the next relational node in the plan tree.
+#[derive(Debug)]
+pub struct RelationalIterator<'n> {
+    current: &'n usize,
+    child: RefCell<usize>,
+    nodes: &'n Nodes,
+}
+
+impl<'n> Nodes {
+    #[must_use]
+    pub fn rel_iter(&'n self, current: &'n usize) -> RelationalIterator<'n> {
+        RelationalIterator {
+            current,
+            child: RefCell::new(0),
+            nodes: self,
+        }
+    }
+}
+
+impl<'nodes> TreeIterator<'nodes> for RelationalIterator<'nodes> {
+    fn get_current(&self) -> &'nodes usize {
+        self.current
+    }
+
+    fn get_child(&self) -> &RefCell<usize> {
+        &self.child
+    }
+
+    fn get_nodes(&self) -> &'nodes Nodes {
+        self.nodes
+    }
+}
+
+impl<'nodes> RelationalTreeIterator<'nodes> for RelationalIterator<'nodes> {}
+
+impl<'n> Iterator for RelationalIterator<'n> {
+    type Item = &'n usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        relational_next(self)
+    }
+}
+
+fn relational_next<'nodes>(
+    iter: &mut impl RelationalTreeIterator<'nodes>,
+) -> Option<&'nodes usize> {
+    match iter.get_nodes().arena.get(*iter.get_current()) {
+        Some(Node::Relational(
+            Relational::Except { children, .. }
+            | Relational::InnerJoin { children, .. }
+            | Relational::Insert { children, .. }
+            | Relational::Motion { children, .. }
+            | Relational::Projection { children, .. }
+            | Relational::ScanSubQuery { children, .. }
+            | Relational::Selection { children, .. }
+            | Relational::UnionAll { children, .. }
+            | Relational::Values { children, .. }
+            | Relational::ValuesRow { children, .. },
+        )) => {
+            let step = *iter.get_child().borrow();
+            if step < children.len() {
+                *iter.get_child().borrow_mut() += 1;
+                return children.get(step);
+            }
+            None
+        }
+        Some(
+            Node::Relational(Relational::ScanRelation { .. })
+            | Node::Expression(_)
+            | Node::Parameter,
+        )
+        | None => None,
+    }
+}
diff --git a/sbroad-core/src/ir/tree/subtree.rs b/sbroad-core/src/ir/tree/subtree.rs
new file mode 100644
index 0000000000..0868168be5
--- /dev/null
+++ b/sbroad-core/src/ir/tree/subtree.rs
@@ -0,0 +1,221 @@
+use std::cell::RefCell;
+use std::cmp::Ordering;
+
+use super::{PlanTreeIterator, TreeIterator};
+use crate::ir::expression::Expression;
+use crate::ir::operator::Relational;
+use crate::ir::{Node, Nodes, Plan};
+
+trait SubtreePlanIterator<'plan>: PlanTreeIterator<'plan> {}
+
+/// Expression and relational nodes iterator.
+#[derive(Debug)]
+pub struct SubtreeIterator<'plan> {
+    current: &'plan usize,
+    child: RefCell<usize>,
+    plan: &'plan Plan,
+}
+
+impl<'nodes> TreeIterator<'nodes> for SubtreeIterator<'nodes> {
+    fn get_current(&self) -> &'nodes usize {
+        self.current
+    }
+
+    fn get_child(&self) -> &RefCell<usize> {
+        &self.child
+    }
+
+    fn get_nodes(&self) -> &'nodes Nodes {
+        &self.plan.nodes
+    }
+}
+
+impl<'plan> PlanTreeIterator<'plan> for SubtreeIterator<'plan> {
+    fn get_plan(&self) -> &'plan Plan {
+        self.plan
+    }
+}
+
+impl<'plan> SubtreePlanIterator<'plan> for SubtreeIterator<'plan> {}
+
+impl<'plan> Iterator for SubtreeIterator<'plan> {
+    type Item = &'plan usize;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        subtree_next(self)
+    }
+}
+
+impl<'plan> Plan {
+    #[must_use]
+    pub fn subtree_iter(&'plan self, current: &'plan usize) -> SubtreeIterator<'plan> {
+        SubtreeIterator {
+            current,
+            child: RefCell::new(0),
+            plan: self,
+        }
+    }
+}
+
+#[allow(clippy::too_many_lines)]
+fn subtree_next<'plan>(iter: &mut impl SubtreePlanIterator<'plan>) -> Option<&'plan usize> {
+    if let Some(child) = iter.get_nodes().arena.get(*iter.get_current()) {
+        return match child {
+            Node::Parameter => None,
+            Node::Expression(exp) => match exp {
+                Expression::Alias { child, .. }
+                | Expression::Cast { child, .. }
+                | Expression::Unary { child, .. } => {
+                    let step = *iter.get_child().borrow();
+                    *iter.get_child().borrow_mut() += 1;
+                    if step == 0 {
+                        return Some(child);
+                    }
+                    None
+                }
+                Expression::Bool { left, right, .. } | Expression::Concat { left, right } => {
+                    let child_step = *iter.get_child().borrow();
+                    if child_step == 0 {
+                        *iter.get_child().borrow_mut() += 1;
+                        return Some(left);
+                    } else if child_step == 1 {
+                        *iter.get_child().borrow_mut() += 1;
+                        return Some(right);
+                    }
+                    None
+                }
+                Expression::Row { list, .. }
+                | Expression::StableFunction { children: list, .. } => {
+                    let child_step = *iter.get_child().borrow();
+                    return match list.get(child_step) {
+                        None => None,
+                        Some(child) => {
+                            *iter.get_child().borrow_mut() += 1;
+                            Some(child)
+                        }
+                    };
+                }
+                Expression::Constant { .. } => None,
+                Expression::Reference { .. } => {
+                    let step = *iter.get_child().borrow();
+                    if step == 0 {
+                        *iter.get_child().borrow_mut() += 1;
+
+                        // At first we need to detect the place where the reference is used:
+                        // for selection filter or a join condition, we need to check whether
+                        // the reference points to an **additional** sub-query and then traverse
+                        // into it. Otherwise, stop traversal.
+                        let parent_id = match exp.get_parent() {
+                            Ok(parent_id) => parent_id,
+                            Err(_) => return None,
+                        };
+                        if let Ok(rel_id) = iter
+                            .get_plan()
+                            .get_relational_from_reference_node(*iter.get_current())
+                        {
+                            match iter.get_plan().get_relation_node(*rel_id) {
+                                Ok(rel_node) if rel_node.is_subquery() => {
+                                    // Check if the sub-query is an additional one.
+                                    let parent = iter.get_plan().get_relation_node(parent_id);
+                                    let mut is_additional = false;
+                                    if let Ok(Relational::Selection { children, .. }) = parent {
+                                        if children.iter().skip(1).any(|&c| c == *rel_id) {
+                                            is_additional = true;
+                                        }
+                                    }
+                                    if let Ok(Relational::InnerJoin { children, .. }) = parent {
+                                        if children.iter().skip(2).any(|&c| c == *rel_id) {
+                                            is_additional = true;
+                                        }
+                                    }
+                                    if is_additional {
+                                        return Some(rel_id);
+                                    }
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+                    None
+                }
+            },
+
+            Node::Relational(r) => match r {
+                Relational::InnerJoin {
+                    children,
+                    condition,
+                    ..
+                } => {
+                    let step = *iter.get_child().borrow();
+
+                    *iter.get_child().borrow_mut() += 1;
+                    match step.cmp(&2) {
+                        Ordering::Less => {
+                            return children.get(step);
+                        }
+                        Ordering::Equal => {
+                            return Some(condition);
+                        }
+                        Ordering::Greater => None,
+                    }
+                }
+
+                Relational::Except { children, .. }
+                | Relational::Insert { children, .. }
+                | Relational::Motion { children, .. }
+                | Relational::ScanSubQuery { children, .. }
+                | Relational::UnionAll { children, .. } => {
+                    let step = *iter.get_child().borrow();
+                    if step < children.len() {
+                        *iter.get_child().borrow_mut() += 1;
+                        return children.get(step);
+                    }
+                    None
+                }
+                Relational::Values {
+                    output, children, ..
+                }
+                | Relational::Projection {
+                    output, children, ..
+                } => {
+                    let step = *iter.get_child().borrow();
+                    *iter.get_child().borrow_mut() += 1;
+                    if step == 0 {
+                        return Some(output);
+                    }
+                    if step <= children.len() {
+                        return children.get(step - 1);
+                    }
+                    None
+                }
+                Relational::Selection {
+                    children, filter, ..
+                } => {
+                    let step = *iter.get_child().borrow();
+
+                    *iter.get_child().borrow_mut() += 1;
+                    match step.cmp(&1) {
+                        Ordering::Less => {
+                            return children.get(step);
+                        }
+                        Ordering::Equal => {
+                            return Some(filter);
+                        }
+                        Ordering::Greater => None,
+                    }
+                }
+                Relational::ValuesRow { data, .. } => {
+                    let step = *iter.get_child().borrow();
+
+                    *iter.get_child().borrow_mut() += 1;
+                    if step == 0 {
+                        return Some(data);
+                    }
+                    None
+                }
+                Relational::ScanRelation { .. } => None,
+            },
+        };
+    }
+    None
+}
-- 
GitLab