From 3f66bb398e3af5f5ba5ce276e51fd20dc5f97bfc Mon Sep 17 00:00:00 2001
From: EmirVildanov <reddog201030@gmail.com>
Date: Wed, 21 Aug 2024 14:53:12 +0300
Subject: [PATCH] feat: move common logic to get_sq_ref_map function in explain

---
 sbroad-core/src/ir/explain.rs | 146 ++++++++++++----------------------
 1 file changed, 52 insertions(+), 94 deletions(-)

diff --git a/sbroad-core/src/ir/explain.rs b/sbroad-core/src/ir/explain.rs
index f382df5550..c8bd504fba 100644
--- a/sbroad-core/src/ir/explain.rs
+++ b/sbroad-core/src/ir/explain.rs
@@ -1044,6 +1044,35 @@ impl FullExplain {
         for LevelNode(level, id) in dft_post.iter(top_id) {
             let mut current_node = ExplainTreePart::with_level(level);
             let node = ir.get_relation_node(id)?;
+
+            let mut get_sq_ref_map = |children: &Vec<usize>, req_children_number| {
+                let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len());
+
+                // Note that subqueries are added to the stack in the `children` reveresed order
+                // because of the PostOrder traversal. That's why we apply `rev` here.
+                for sq_id in children.iter().skip(req_children_number).rev() {
+                    let sq_node = stack
+                        .pop()
+                        .unwrap_or_else(|| panic!("Rel node failed to pop a sub-query."));
+                    result.subqueries.push(sq_node);
+                    let offset = result.subqueries.len() - 1;
+                    sq_ref_map.insert(*sq_id, offset);
+                }
+
+                let mut children_to_add = Vec::with_capacity(req_children_number);
+                for _ in 0..req_children_number {
+                    children_to_add.push(stack.pop().unwrap_or_else(|| {
+                        panic!("Expected to pop required child for {current_node:?}")
+                    }));
+                }
+                children_to_add.reverse();
+                for child in children_to_add {
+                    current_node.children.push(child);
+                }
+
+                sq_ref_map
+            };
+
             current_node.current = match &node {
                 Relational::Intersect { .. } => {
                     if let (Some(right), Some(left)) = (stack.pop(), stack.pop()) {
@@ -1067,39 +1096,30 @@ impl FullExplain {
                     }
                     Some(ExplainNode::Except)
                 }
-                Relational::GroupBy(GroupByRel {
-                    gr_cols, output, ..
+                Relational::GroupBy(GroupBy {
+                    gr_cols,
+                    output,
+                    children,
+                    ..
                 }) => {
-                    let child = stack.pop().ok_or_else(|| {
-                        SbroadError::UnexpectedNumberOfValues(
-                            "Groupby node must have at least one child".into(),
-                        )
-                    })?;
-                    current_node.children.push(child);
-                    let p = GroupBy::new(ir, gr_cols, *output, &HashMap::new())?;
+                    let sq_ref_map = get_sq_ref_map(children, 1);
+                    let p = GroupBy::new(ir, gr_cols, *output, &sq_ref_map)?;
                     Some(ExplainNode::GroupBy(p))
                 }
-                Relational::OrderBy(OrderByRel {
-                    order_by_elements, ..
+                Relational::OrderBy(OrderBy {
+                    order_by_elements,
+                    children,
+                    ..
                 }) => {
-                    let child = stack.pop().ok_or_else(|| {
-                        SbroadError::UnexpectedNumberOfValues(
-                            "OrderBy node must have at least one child".into(),
-                        )
-                    })?;
-                    current_node.children.push(child);
-                    let o_b = OrderBy::new(ir, order_by_elements, &HashMap::new())?;
+                    let sq_ref_map = get_sq_ref_map(children, 1);
+                    let o_b = OrderBy::new(ir, order_by_elements, &sq_ref_map)?;
                     Some(ExplainNode::OrderBy(o_b))
                 }
-                Relational::Projection(ProjectionRel { output, .. }) => {
-                    // TODO: change this logic when we'll enable sub-queries in projection
-                    let child = stack.pop().ok_or_else(|| {
-                        SbroadError::UnexpectedNumberOfValues(
-                            "Projection node must have exactly one child".into(),
-                        )
-                    })?;
-                    current_node.children.push(child);
-                    let p = Projection::new(ir, *output, &HashMap::new())?;
+                Relational::Projection(Projection {
+                    output, children, ..
+                }) => {
+                    let sq_ref_map = get_sq_ref_map(children, 1);
+                    let p = Projection::new(ir, *output, &sq_ref_map)?;
                     Some(ExplainNode::Projection(p))
                 }
                 Relational::ScanRelation(ScanRelation {
@@ -1125,30 +1145,8 @@ impl FullExplain {
                 })
                 | Relational::Having(Having {
                     children, filter, ..
-                }) => {
-                    let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len() - 1);
-                    if let Some((_, other)) = children.split_first() {
-                        for sq_id in other.iter().rev() {
-                            let sq_node = stack.pop().ok_or_else(|| {
-                                SbroadError::UnexpectedNumberOfValues(
-                                    "Selection node failed to pop a sub-query.".into(),
-                                )
-                            })?;
-                            result.subqueries.push(sq_node);
-                            let offset = result.subqueries.len() - 1;
-                            sq_ref_map.insert(*sq_id, offset);
-                        }
-                        let child = stack.pop().ok_or_else(|| {
-                            SbroadError::UnexpectedNumberOfValues(
-                                "Selection node must have exactly one child".into(),
-                            )
-                        })?;
-                        current_node.children.push(child);
-                    } else {
-                        return Err(SbroadError::UnexpectedNumberOfValues(
-                            "Selection node doesn't have any children".into(),
-                        ));
-                    }
+                } => {
+                    let sq_ref_map = get_sq_ref_map(children, 1);
                     let filter_id = ir.undo.get_oldest(filter).map_or_else(|| *filter, |id| *id);
                     let selection = ColExpr::new(ir, filter_id, &sq_ref_map)?;
                     let explain_node = match &node {
@@ -1249,55 +1247,15 @@ impl FullExplain {
                     kind,
                     ..
                 }) => {
-                    if children.len() < 2 {
-                        return Err(SbroadError::UnexpectedNumberOfValues(
-                            "Join must have at least two children".into(),
-                        ));
-                    }
-                    let (_, subquery_ids) = children.split_at(2);
-                    let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len() - 2);
-
-                    for sq_id in subquery_ids.iter().rev() {
-                        let sq_node = stack.pop().ok_or_else(|| {
-                            SbroadError::UnexpectedNumberOfValues(
-                                "Join node failed to pop a sub-query.".into(),
-                            )
-                        })?;
-                        result.subqueries.push(sq_node);
-                        let offset = result.subqueries.len() - 1;
-                        sq_ref_map.insert(*sq_id, offset);
-                    }
-
-                    if let (Some(right), Some(left)) = (stack.pop(), stack.pop()) {
-                        current_node.children.push(left);
-                        current_node.children.push(right);
-                    } else {
-                        return Err(SbroadError::UnexpectedNumberOfValues(
-                            "Join node must have exactly two children".into(),
-                        ));
-                    }
-
+                    let sq_ref_map = get_sq_ref_map(children, 2);
                     let condition = ColExpr::new(ir, *condition, &sq_ref_map)?;
                     Some(ExplainNode::InnerJoin(InnerJoin {
                         condition,
                         kind: kind.clone(),
                     }))
                 }
-                Relational::ValuesRow(ValuesRow { data, children, .. }) => {
-                    let mut sq_ref_map: SubQueryRefMap = HashMap::with_capacity(children.len());
-
-                    for sq_id in children.iter().rev() {
-                        let sq_node = stack.pop().ok_or_else(|| {
-                            SbroadError::UnexpectedNumberOfValues(
-                                "Insert node failed to pop a sub-query.".into(),
-                            )
-                        })?;
-
-                        result.subqueries.push(sq_node);
-                        let offset = result.subqueries.len() - 1;
-                        sq_ref_map.insert(*sq_id, offset);
-                    }
-
+                Relational::ValuesRow { data, children, .. } => {
+                    let sq_ref_map = get_sq_ref_map(children, 0);
                     let row = ColExpr::new(ir, *data, &sq_ref_map)?;
 
                     Some(ExplainNode::ValueRow(row))
-- 
GitLab