From a1d43fe4d8093dabcbe71f3ffdc0a5e82edf562c Mon Sep 17 00:00:00 2001
From: EmirVildanov <reddog201030@gmail.com>
Date: Fri, 23 Aug 2024 17:14:32 +0300
Subject: [PATCH] feat: remove `need_parentheses` flag-workaround

---
 sbroad-core/src/backend/sql/tree.rs           | 248 ++++++++++++------
 sbroad-core/src/frontend/sql/ir.rs            |   1 -
 sbroad-core/src/ir/operator.rs                |   2 -
 .../src/ir/transformation/redistribution.rs   |  20 +-
 .../redistribution/left_join.rs               |   2 +-
 .../full_motion_less_for_sub_query.yaml       |   1 -
 ...otion_non_segment_outer_for_sub_query.yaml |   1 -
 .../redistribution/multiple_sub_queries.yaml  |   2 -
 .../segment_motion_for_sub_query.yaml         |   1 -
 9 files changed, 171 insertions(+), 107 deletions(-)

diff --git a/sbroad-core/src/backend/sql/tree.rs b/sbroad-core/src/backend/sql/tree.rs
index 001aabc1e..9ec30bd10 100644
--- a/sbroad-core/src/backend/sql/tree.rs
+++ b/sbroad-core/src/backend/sql/tree.rs
@@ -568,7 +568,9 @@ impl<'p> SyntaxPlan<'p> {
             panic!("Expected plan syntax node");
         };
         if *id != plan_id {
-            // If it is a motion and we need its child instead.
+            // There are some nodes that leave their children on stack instead of themselves. E.g.:
+            // * Motion with policy Local which doesn't pop its children (see `add_motion`)
+            // * Row which references SubQuery (and leaves it on the stack instead of itself)
             let plan = self.plan.get_ir_plan();
             let node = plan.get_node(plan_id).expect("node in the plan must exist");
             match node {
@@ -599,9 +601,120 @@ impl<'p> SyntaxPlan<'p> {
 
     /// Pop syntax node identifier from the stack with a check, that the popped syntax
     /// node wraps an expected plan node.
-    fn pop_from_stack(&mut self, plan_id: NodeId) -> usize {
+    fn pop_from_stack(&mut self, plan_id: NodeId, parent_id: NodeId) -> usize {
         let sn_id = self.nodes.stack.pop().expect("stack must be non-empty");
         self.check_plan_node(sn_id, plan_id);
+
+        let requested_plan_node = self
+            .plan
+            .get_ir_plan()
+            .get_node(plan_id)
+            .expect("Plan node expected for popping.");
+
+        if let Node::Relational(Relational::Motion { children, .. }) = requested_plan_node {
+            let motion_child_id = children.first();
+            let motion_to_fix_id = if let Some(motion_child_id) = motion_child_id {
+                let motion_child_node = self
+                    .plan
+                    .get_ir_plan()
+                    .get_node(*motion_child_id)
+                    .expect("motion child id is incorrect");
+                if matches!(
+                    motion_child_node,
+                    Node::Relational(Relational::Motion { .. })
+                ) {
+                    *motion_child_id
+                } else {
+                    plan_id
+                }
+            } else {
+                plan_id
+            };
+
+            let motion_to_fix_node = self
+                .plan
+                .get_ir_plan()
+                .get_relation_node(motion_to_fix_id)
+                .expect("Plan node expected for popping.");
+            if let Relational::Motion {
+                policy, program, ..
+            } = motion_to_fix_node
+            {
+                let mut should_cover_with_parentheses = true;
+
+                let empty_table_op = program
+                    .0
+                    .iter()
+                    .find(|op| matches!(op, MotionOpcode::SerializeAsEmptyTable(_)));
+                if matches!(
+                    empty_table_op,
+                    Some(MotionOpcode::SerializeAsEmptyTable(true))
+                ) {
+                    // In `add_motion` we've created an `Inline` node for that case.
+                    return sn_id;
+                }
+                let vtable_alias = if policy.is_local()
+                    && matches!(
+                        empty_table_op,
+                        Some(MotionOpcode::SerializeAsEmptyTable(false))
+                    ) {
+                    // We don't want to generate a `SyntaxData::VTable` node
+                    // in case of Local policy with empty_table_op.
+                    // See `add_motion` function for details.
+                    return sn_id;
+                } else {
+                    let vtable = self
+                        .plan
+                        .get_motion_vtable(motion_to_fix_id)
+                        .expect("motion virtual table");
+                    vtable.get_alias().cloned()
+                };
+
+                let parent_node = self
+                    .plan
+                    .get_ir_plan()
+                    .get_node(parent_id)
+                    .expect("Motion parent plan node expected.");
+                if let Node::Relational(rel_node) = parent_node {
+                    should_cover_with_parentheses = !matches!(
+                        rel_node,
+                        Relational::Union { .. }
+                            | Relational::UnionAll { .. }
+                            | Relational::Except { .. }
+                            | Relational::ScanCte { .. }
+                            | Relational::Limit { .. }
+                    );
+
+                    if !should_cover_with_parentheses {
+                        assert!(
+                            vtable_alias.is_none(),
+                            "Virtual table under Union/UnionAll/Except/CTE must not have an alias."
+                        );
+                    }
+                }
+
+                let arena = &mut self.nodes;
+                let vtable_sn_node = SyntaxNode::new_vtable(motion_to_fix_id);
+                let fixed_children = if should_cover_with_parentheses {
+                    let mut fixed_children: Vec<usize> = vec![
+                        arena.push_sn_non_plan(SyntaxNode::new_open()),
+                        arena.push_sn_non_plan(vtable_sn_node),
+                        arena.push_sn_non_plan(SyntaxNode::new_close()),
+                    ];
+                    if let Some(name) = vtable_alias {
+                        assert!(!name.is_empty(), "Virtual table has an empty alias name");
+                        let alias = SyntaxNode::new_alias(name);
+                        fixed_children.push(arena.push_sn_non_plan(alias));
+                    }
+                    fixed_children
+                } else {
+                    vec![arena.push_sn_non_plan(vtable_sn_node)]
+                };
+                let motion_sn_node = self.nodes.get_mut_sn(sn_id);
+                motion_sn_node.right = fixed_children;
+            }
+        }
+
         sn_id
     }
 
@@ -642,11 +755,13 @@ impl<'p> SyntaxPlan<'p> {
                 Relational::ScanCte { .. } => self.add_cte(id),
                 Relational::ScanSubQuery { .. } => self.add_sq(id),
                 Relational::GroupBy { .. } => self.add_group_by(id),
-                Relational::Selection { .. } | Relational::Having { .. } => self.add_filter(id),
+                Relational::Selection { .. } | Relational::Having { .. } => {
+                    self.add_selection_having(id)
+                }
                 Relational::Except { .. }
                 | Relational::Union { .. }
                 | Relational::UnionAll { .. }
-                | Relational::Intersect { .. } => self.add_set(id),
+                | Relational::Intersect { .. } => self.add_set_rel(id),
                 Relational::ScanRelation { .. } => self.add_scan_relation(id),
                 Relational::Motion { .. } => self.add_motion(id),
                 Relational::ValuesRow { .. } => self.add_values_row(id),
@@ -700,7 +815,7 @@ impl<'p> SyntaxPlan<'p> {
             panic!("expected CTE node");
         };
         let (child, alias) = (*child, alias.clone());
-        let child_sn_id = self.pop_from_stack(child);
+        let child_sn_id = self.pop_from_stack(child, id);
         let arena = &mut self.nodes;
         let children: Vec<usize> = vec![
             arena.push_sn_non_plan(SyntaxNode::new_open()),
@@ -712,7 +827,7 @@ impl<'p> SyntaxPlan<'p> {
         arena.push_sn_plan(sn);
     }
 
-    fn add_filter(&mut self, id: NodeId) {
+    fn add_selection_having(&mut self, id: NodeId) {
         let (plan, rel) = self.prologue_rel(id);
         let (Relational::Selection(Selection {
             children, filter, ..
@@ -728,8 +843,8 @@ impl<'p> SyntaxPlan<'p> {
             Snapshot::Oldest => *plan.undo.get_oldest(filter).map_or_else(|| filter, |id| id),
         };
         let child_plan_id = *children.first().expect("FILTER child");
-        let filter_sn_id = self.pop_from_stack(filter_id);
-        let child_sn_id = self.pop_from_stack(child_plan_id);
+        let filter_sn_id = self.pop_from_stack(filter_id, id);
+        let child_sn_id = self.pop_from_stack(child_plan_id, id);
         let sn = SyntaxNode::new_pointer(id, Some(child_sn_id), vec![filter_sn_id]);
         self.nodes.push_sn_plan(sn);
     }
@@ -748,11 +863,11 @@ impl<'p> SyntaxPlan<'p> {
         let mut syntax_gr_cols = Vec::with_capacity(plan_gr_cols.len());
         // Reuse the same vector to avoid extra allocations
         // (replace plan node ids with syntax node ids).
-        for col_id in &plan_gr_cols {
-            syntax_gr_cols.push(self.pop_from_stack(*col_id));
+        for col_id in &mut sn_gr_cols {
+            *col_id = self.pop_from_stack(*col_id, id);
         }
-        let child_sn_id = self.pop_from_stack(child_plan_id);
-        let mut sn_children = Vec::with_capacity(syntax_gr_cols.len() * 2 - 1);
+        let child_sn_id = self.pop_from_stack(child_plan_id, id);
+        let mut sn_children = Vec::with_capacity(sn_gr_cols.len() * 2 - 1);
         // The columns are in reverse order, so we need to reverse them back.
         if let Some((first, others)) = syntax_gr_cols.split_first() {
             for id in others.iter().rev() {
@@ -784,9 +899,9 @@ impl<'p> SyntaxPlan<'p> {
         };
         let inner_plan_id = *children.get(1).expect("JOIN inner child");
         let outer_plan_id = *children.first().expect("JOIN outer child");
-        let cond_sn_id = self.pop_from_stack(cond_plan_id);
-        let inner_sn_id = self.pop_from_stack(inner_plan_id);
-        let outer_sn_id = self.pop_from_stack(outer_plan_id);
+        let cond_sn_id = self.pop_from_stack(cond_plan_id, id);
+        let inner_sn_id = self.pop_from_stack(inner_plan_id, id);
+        let outer_sn_id = self.pop_from_stack(outer_plan_id, id);
         let arena = &mut self.nodes;
         let sn = SyntaxNode::new_pointer(
             id,
@@ -807,7 +922,6 @@ impl<'p> SyntaxPlan<'p> {
             children,
             program,
             output,
-            need_parentheses,
             ..
         }) = motion
         else {
@@ -822,7 +936,7 @@ impl<'p> SyntaxPlan<'p> {
                 // is not valid and should never be generated in runtime, but let's leave
                 // it for testing.
                 let child_plan_id = first_child.expect("MOTION child");
-                let child_sn_id = self.pop_from_stack(child_plan_id);
+                let child_sn_id = self.pop_from_stack(child_plan_id, id);
                 let sn = SyntaxNode::new_pointer(id, Some(child_sn_id), vec![]);
                 self.nodes.push_sn_plan(sn);
                 return;
@@ -863,42 +977,18 @@ impl<'p> SyntaxPlan<'p> {
             }
             return;
         }
-        let vtable = self
-            .plan
-            .get_motion_vtable(id)
-            .expect("motion virtual table");
-
-        let vtable_alias = vtable.get_alias().cloned();
-        let need_parentheses = *need_parentheses;
 
         // Remove motion's child from the stack (if any).
         if let Some(child_id) = first_child {
-            let _ = self.pop_from_stack(child_id);
+            let _ = self.pop_from_stack(child_id, id);
         }
 
         let arena = &mut self.nodes;
 
-        let sn_children = if need_parentheses || vtable_alias.is_some() {
-            let mut children: Vec<usize> = vec![
-                arena.push_sn_non_plan(SyntaxNode::new_open()),
-                arena.push_sn_non_plan(SyntaxNode::new_vtable(id)),
-                arena.push_sn_non_plan(SyntaxNode::new_close()),
-            ];
-
-            if let Some(name) = vtable_alias {
-                assert!(
-                    !name.is_empty(),
-                    "Virtual table {vtable:?} has an empty alias name"
-                );
-                let alias = SyntaxNode::new_alias(name);
-                children.push(arena.push_sn_non_plan(alias));
-            }
-            children
-        } else {
-            vec![arena.push_sn_non_plan(SyntaxNode::new_vtable(id))]
-        };
-
-        let sn = SyntaxNode::new_pointer(id, None, sn_children);
+        // Motion node children will be fixed later based on the parent node
+        // (sometimes Motion Vtable have to be covered with parentheses and
+        // sometimes not).
+        let sn = SyntaxNode::new_pointer(id, None, vec![]);
         arena.push_sn_plan(sn);
     }
 
@@ -921,7 +1011,7 @@ impl<'p> SyntaxPlan<'p> {
                 let mut nodes = [None, None, None];
                 match elem.entity {
                     OrderByEntity::Expression { expr_id } => {
-                        let expr_sn_id = self.pop_from_stack(expr_id);
+                        let expr_sn_id = self.pop_from_stack(expr_id, id);
                         nodes[2] = Some(expr_sn_id);
                     }
                     OrderByEntity::Index { value } => {
@@ -952,7 +1042,7 @@ impl<'p> SyntaxPlan<'p> {
         // Reverse the order of the children back.
         children.reverse();
 
-        let child_sn_id = self.pop_from_stack(child_plan_id);
+        let child_sn_id = self.pop_from_stack(child_plan_id, id);
 
         let sn = SyntaxNode::new_pointer(id, Some(child_sn_id), children);
         self.nodes.push_sn_plan(sn);
@@ -968,8 +1058,8 @@ impl<'p> SyntaxPlan<'p> {
         };
         let child_plan_id = *children.first().expect("PROJECTION child");
         let output = *output;
-        let child_sn_id = self.pop_from_stack(child_plan_id);
-        let row_sn_id = self.pop_from_stack(output);
+        let child_sn_id = self.pop_from_stack(child_plan_id, id);
+        let row_sn_id = self.pop_from_stack(output, id);
         // We don't need the row node itself, only its children. Otherwise we'll produce
         // redundant parentheses between `SELECT` and `FROM`.
         let sn_from_id = self.nodes.push_sn_non_plan(SyntaxNode::new_from());
@@ -1007,7 +1097,7 @@ impl<'p> SyntaxPlan<'p> {
         arena.push_sn_plan(sn);
     }
 
-    fn add_set(&mut self, id: NodeId) {
+    fn add_set_rel(&mut self, id: NodeId) {
         let (_, set) = self.prologue_rel(id);
         let (Relational::Except(Except { left, right, .. })
         | Relational::Union(Union { left, right, .. })
@@ -1017,8 +1107,8 @@ impl<'p> SyntaxPlan<'p> {
             panic!("Expected SET node");
         };
         let (left, right) = (*left, *right);
-        let right_sn_id = self.pop_from_stack(right);
-        let left_sn_id = self.pop_from_stack(left);
+        let right_sn_id = self.pop_from_stack(right, id);
+        let left_sn_id = self.pop_from_stack(left, id);
         let sn = SyntaxNode::new_pointer(id, Some(left_sn_id), vec![right_sn_id]);
         self.nodes.push_sn_plan(sn);
     }
@@ -1033,7 +1123,7 @@ impl<'p> SyntaxPlan<'p> {
         };
         let child_plan_id = *children.first().expect("SUBQUERY child");
         let sq_alias = alias.clone();
-        let child_sn_id = self.pop_from_stack(child_plan_id);
+        let child_sn_id = self.pop_from_stack(child_plan_id, id);
 
         let arena = &mut self.nodes;
         let mut children: Vec<usize> = vec![
@@ -1053,7 +1143,7 @@ impl<'p> SyntaxPlan<'p> {
         let Relational::ValuesRow(ValuesRow { data, .. }) = row else {
             panic!("Expected VALUES ROW node");
         };
-        let data_sn_id = self.pop_from_stack(*data);
+        let data_sn_id = self.pop_from_stack(*data, id);
         let sn = SyntaxNode::new_pointer(id, None, vec![data_sn_id]);
         self.nodes.push_sn_plan(sn);
     }
@@ -1076,7 +1166,7 @@ impl<'p> SyntaxPlan<'p> {
         }
 
         // Consume the output from the stack.
-        let _ = self.pop_from_stack(output_plan_id);
+        let _ = self.pop_from_stack(output_plan_id, id);
 
         let mut nodes = Vec::with_capacity(syntax_children.len() * 2 - 1);
         // Reverse the order of the children back.
@@ -1100,7 +1190,7 @@ impl<'p> SyntaxPlan<'p> {
             panic!("expected LIMIT node");
         };
         let (limit, child) = (*limit, *child);
-        let child_sn_id = self.pop_from_stack(child);
+        let child_sn_id = self.pop_from_stack(child, id);
         let arena = &mut self.nodes;
         let children: Vec<usize> = vec![
             child_sn_id,
@@ -1118,7 +1208,7 @@ impl<'p> SyntaxPlan<'p> {
             panic!("Expected ALIAS node");
         };
         let (child, name) = (*child, name.clone());
-        let child_sn_id = self.pop_from_stack(child);
+        let child_sn_id = self.pop_from_stack(child, id);
         let plan = self.plan.get_ir_plan();
         // Do not generate an alias in SQL when a column has exactly the same name.
         let child_expr = plan
@@ -1162,8 +1252,8 @@ impl<'p> SyntaxPlan<'p> {
             }
             _ => panic!("Expected binary expression node"),
         };
-        let right_sn_id = self.pop_from_stack(right_plan_id);
-        let left_sn_id = self.pop_from_stack(left_plan_id);
+        let right_sn_id = self.pop_from_stack(right_plan_id, id);
+        let left_sn_id = self.pop_from_stack(left_plan_id, id);
         let children = vec![op_sn_id, right_sn_id];
         let sn = SyntaxNode::new_pointer(id, Some(left_sn_id), children);
         self.nodes.push_sn_plan(sn);
@@ -1186,17 +1276,17 @@ impl<'p> SyntaxPlan<'p> {
         let mut right_vec = Vec::with_capacity(1 + when_blocks.len() * 4 + 1);
         right_vec.push(self.nodes.push_sn_non_plan(SyntaxNode::new_end()));
         if let Some(else_expr_id) = else_expr {
-            right_vec.push(self.pop_from_stack(else_expr_id));
+            right_vec.push(self.pop_from_stack(else_expr_id, id));
             right_vec.push(self.nodes.push_sn_non_plan(SyntaxNode::new_else()));
         }
         for (cond_expr_id, res_expr_id) in when_blocks.iter().rev() {
-            right_vec.push(self.pop_from_stack(*res_expr_id));
+            right_vec.push(self.pop_from_stack(*res_expr_id, id));
             right_vec.push(self.nodes.push_sn_non_plan(SyntaxNode::new_then()));
-            right_vec.push(self.pop_from_stack(*cond_expr_id));
+            right_vec.push(self.pop_from_stack(*cond_expr_id, id));
             right_vec.push(self.nodes.push_sn_non_plan(SyntaxNode::new_when()));
         }
         if let Some(search_expr_id) = search_expr {
-            right_vec.push(self.pop_from_stack(search_expr_id));
+            right_vec.push(self.pop_from_stack(search_expr_id, id));
         }
         right_vec.reverse();
         let sn = SyntaxNode::new_pointer(
@@ -1214,7 +1304,7 @@ impl<'p> SyntaxPlan<'p> {
         };
         let to_alias = SmolStr::from(to);
         let child_plan_id = *child;
-        let child_sn_id = self.pop_from_stack(child_plan_id);
+        let child_sn_id = self.pop_from_stack(child_plan_id, id);
         let arena = &mut self.nodes;
         let children = vec![
             arena.push_sn_non_plan(SyntaxNode::new_open()),
@@ -1233,8 +1323,8 @@ impl<'p> SyntaxPlan<'p> {
             panic!("Expected CONCAT node");
         };
         let (left, right) = (*left, *right);
-        let right_sn_id = self.pop_from_stack(right);
-        let left_sn_id = self.pop_from_stack(left);
+        let right_sn_id = self.pop_from_stack(right, id);
+        let left_sn_id = self.pop_from_stack(left, id);
         let children = vec![
             self.nodes.push_sn_non_plan(SyntaxNode::new_concat()),
             right_sn_id,
@@ -1248,7 +1338,7 @@ impl<'p> SyntaxPlan<'p> {
         let Expression::ExprInParentheses(ExprInParentheses { child }) = expr else {
             panic!("Expected expression in parentheses node");
         };
-        let child_sn_id = self.pop_from_stack(*child);
+        let child_sn_id = self.pop_from_stack(*child, id);
         let arena = &mut self.nodes;
         let children = vec![child_sn_id, arena.push_sn_non_plan(SyntaxNode::new_close())];
         let sn = SyntaxNode::new_pointer(
@@ -1304,7 +1394,7 @@ impl<'p> SyntaxPlan<'p> {
             if needs_replacement {
                 // Remove columns from the stack.
                 for child_id in list.iter().rev() {
-                    let _ = self.pop_from_stack(*child_id);
+                    let _ = self.pop_from_stack(*child_id, id);
 
                     // Remove the referred motion from the stack (if any).
                     let expr = plan
@@ -1314,7 +1404,7 @@ impl<'p> SyntaxPlan<'p> {
                         let referred_id = *plan
                             .get_relational_from_reference_node(*child_id)
                             .expect("referred id");
-                        self.pop_from_stack(referred_id);
+                        self.pop_from_stack(referred_id, id);
                     }
                 }
 
@@ -1345,7 +1435,7 @@ impl<'p> SyntaxPlan<'p> {
 
                 // Remove columns from the stack.
                 for child_id in list.iter().rev() {
-                    let _ = self.pop_from_stack(*child_id);
+                    let _ = self.pop_from_stack(*child_id, id);
 
                     // Remove the referred sub-query from the stack (if any).
                     let expr = plan
@@ -1355,7 +1445,7 @@ impl<'p> SyntaxPlan<'p> {
                         let referred_id = *plan
                             .get_relational_from_reference_node(*child_id)
                             .expect("referred id");
-                        sq_sn_id = Some(self.pop_from_stack(referred_id));
+                        sq_sn_id = Some(self.pop_from_stack(referred_id, id));
                     }
                 }
 
@@ -1371,10 +1461,10 @@ impl<'p> SyntaxPlan<'p> {
         children.push(self.nodes.push_sn_non_plan(SyntaxNode::new_close()));
         if let Some((first, others)) = list.split_first() {
             for child_id in others.iter().rev() {
-                children.push(self.pop_from_stack(*child_id));
+                children.push(self.pop_from_stack(*child_id, id));
                 children.push(self.nodes.push_sn_non_plan(SyntaxNode::new_comma()));
             }
-            children.push(self.pop_from_stack(*first));
+            children.push(self.pop_from_stack(*first, id));
         }
         children.push(self.nodes.push_sn_non_plan(SyntaxNode::new_open()));
         // Need to reverse the order of the children back.
@@ -1401,10 +1491,10 @@ impl<'p> SyntaxPlan<'p> {
         nodes.push(self.nodes.push_sn_non_plan(SyntaxNode::new_close()));
         if let Some((first, others)) = args.split_first() {
             for child_id in others.iter().rev() {
-                nodes.push(self.pop_from_stack(*child_id));
+                nodes.push(self.pop_from_stack(*child_id, id));
                 nodes.push(self.nodes.push_sn_non_plan(SyntaxNode::new_comma()));
             }
-            nodes.push(self.pop_from_stack(*first));
+            nodes.push(self.pop_from_stack(*first, id));
         }
         if let Some(FunctionFeature::Distinct) = feature {
             nodes.push(self.nodes.push_sn_non_plan(SyntaxNode::new_distinct()));
@@ -1430,10 +1520,10 @@ impl<'p> SyntaxPlan<'p> {
         let mut need_from = false;
 
         // Syntax nodes on the stack are in the reverse order.
-        let target_sn_id = self.pop_from_stack(target);
+        let target_sn_id = self.pop_from_stack(target, id);
         let mut pattern_sn_id = None;
         if let Some(pattern) = pattern {
-            pattern_sn_id = Some(self.pop_from_stack(pattern));
+            pattern_sn_id = Some(self.pop_from_stack(pattern, id));
             need_from = true;
         }
 
@@ -1475,7 +1565,7 @@ impl<'p> SyntaxPlan<'p> {
         let operator_node_id = self
             .nodes
             .push_sn_non_plan(SyntaxNode::new_operator(&format!("{op}")));
-        let child_sn_id = self.pop_from_stack(child);
+        let child_sn_id = self.pop_from_stack(child, id);
         // Bool::Or operator already covers itself with parentheses, that's why we
         // don't have to cover it here.
         let sn = if op == Unary::Not && is_and {
diff --git a/sbroad-core/src/frontend/sql/ir.rs b/sbroad-core/src/frontend/sql/ir.rs
index 66ac17fb6..feb53e417 100644
--- a/sbroad-core/src/frontend/sql/ir.rs
+++ b/sbroad-core/src/frontend/sql/ir.rs
@@ -656,7 +656,6 @@ impl SubtreeCloner {
                 policy: _,
                 program,
                 output: _,
-                need_parentheses: _,
             }) => {
                 for op in &mut program.0 {
                     match op {
diff --git a/sbroad-core/src/ir/operator.rs b/sbroad-core/src/ir/operator.rs
index 657bc189e..6afb09444 100644
--- a/sbroad-core/src/ir/operator.rs
+++ b/sbroad-core/src/ir/operator.rs
@@ -910,7 +910,6 @@ impl Plan {
         child_id: NodeId,
         policy: &MotionPolicy,
         program: Program,
-        need_parentheses: bool,
     ) -> Result<NodeId, SbroadError> {
         let child_rel_node = self.get_relation_node(child_id)?;
         let alias = match child_rel_node {
@@ -952,7 +951,6 @@ impl Plan {
             policy: policy.clone(),
             program,
             output,
-            need_parentheses,
         };
         let motion_id = self.add_relational(motion.into())?;
         self.replace_parent_in_subtree(output, None, Some(motion_id))?;
diff --git a/sbroad-core/src/ir/transformation/redistribution.rs b/sbroad-core/src/ir/transformation/redistribution.rs
index 441043833..0085a99be 100644
--- a/sbroad-core/src/ir/transformation/redistribution.rs
+++ b/sbroad-core/src/ir/transformation/redistribution.rs
@@ -610,21 +610,7 @@ impl Plan {
                 if let MotionPolicy::None = policy {
                     children_with_motions.push(child);
                 } else {
-                    // Notes on NOT covering VTable read with parentheses:
-                    // * CTE scan will cover vtable scan itself
-                    // * Union [All] and Except don't require them
-                    let parent_rel_node = self.get_relation_node(parent_id)?;
-                    let need_parentheses = !matches!(parent_rel_node, Relational::Union { .. })
-                        && !matches!(parent_rel_node, Relational::ScanCte { .. })
-                        && !matches!(parent_rel_node, Relational::Except { .. })
-                        && !matches!(parent_rel_node, Relational::UnionAll { .. });
-
-                    children_with_motions.push(self.add_motion(
-                        child,
-                        policy,
-                        program,
-                        need_parentheses,
-                    )?);
+                    children_with_motions.push(self.add_motion(child, policy, program)?);
                 }
             } else {
                 children_with_motions.push(child);
@@ -2323,10 +2309,6 @@ impl Plan {
                         id,
                         &MotionPolicy::Full,
                         Program::new(vec![MotionOpcode::RemoveDuplicates]),
-                        // Query like
-                        // `... union (...)`
-                        // in not allowed.
-                        false,
                     )?;
                     old_new.insert(id, new_top_id);
                 }
diff --git a/sbroad-core/src/ir/transformation/redistribution/left_join.rs b/sbroad-core/src/ir/transformation/redistribution/left_join.rs
index b5c7d6f46..84de645b2 100644
--- a/sbroad-core/src/ir/transformation/redistribution/left_join.rs
+++ b/sbroad-core/src/ir/transformation/redistribution/left_join.rs
@@ -70,7 +70,7 @@ impl Plan {
 
             if motion_child_id.is_none() {
                 let motion_id =
-                    self.add_motion(outer_id, &MotionPolicy::Full, Program::default(), true)?;
+                    self.add_motion(outer_id, &MotionPolicy::Full, Program::default())?;
                 self.change_child(join_id, outer_id, motion_id)?;
                 motion_child_id = Some(motion_id);
             }
diff --git a/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_less_for_sub_query.yaml b/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_less_for_sub_query.yaml
index f86fa4e24..9a634f6fc 100644
--- a/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_less_for_sub_query.yaml
+++ b/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_less_for_sub_query.yaml
@@ -185,7 +185,6 @@ nodes:
           policy: Full
           program: [ReshardIfNeeded]
           output: 29
-          need_parentheses: true
 relations:
   tables:
     t2:
diff --git a/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_non_segment_outer_for_sub_query.yaml b/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_non_segment_outer_for_sub_query.yaml
index 9e80c2413..bb6e5dade 100644
--- a/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_non_segment_outer_for_sub_query.yaml
+++ b/sbroad-core/tests/artifactory/ir/transformation/redistribution/full_motion_non_segment_outer_for_sub_query.yaml
@@ -205,7 +205,6 @@ nodes:
           policy: Full
           program: [ReshardIfNeeded]
           output: 31
-          need_parentheses: true
 relations:
   tables:
     t1:
diff --git a/sbroad-core/tests/artifactory/ir/transformation/redistribution/multiple_sub_queries.yaml b/sbroad-core/tests/artifactory/ir/transformation/redistribution/multiple_sub_queries.yaml
index 0440136ec..50a246fa8 100644
--- a/sbroad-core/tests/artifactory/ir/transformation/redistribution/multiple_sub_queries.yaml
+++ b/sbroad-core/tests/artifactory/ir/transformation/redistribution/multiple_sub_queries.yaml
@@ -314,7 +314,6 @@ nodes:
           policy: Full
           program: [ReshardIfNeeded]
           output: 49
-          need_parentheses: true
     - Expression:
         Reference:
           targets:
@@ -346,7 +345,6 @@ nodes:
                     0
           program: [ReshardIfNeeded]
           output: 53
-          need_parentheses: true
 relations:
   tables:
     t1:
diff --git a/sbroad-core/tests/artifactory/ir/transformation/redistribution/segment_motion_for_sub_query.yaml b/sbroad-core/tests/artifactory/ir/transformation/redistribution/segment_motion_for_sub_query.yaml
index c13a99ada..d28ab0cc9 100644
--- a/sbroad-core/tests/artifactory/ir/transformation/redistribution/segment_motion_for_sub_query.yaml
+++ b/sbroad-core/tests/artifactory/ir/transformation/redistribution/segment_motion_for_sub_query.yaml
@@ -196,7 +196,6 @@ nodes:
                     0
           program: [ReshardIfNeeded]
           output: 29
-          need_parentheses: true
 relations:
   tables:
     t1:
-- 
GitLab