From ac22b606bd3453b9ed22aa60c05d68382a943b02 Mon Sep 17 00:00:00 2001
From: EmirVildanov <reddog201030@gmail.com>
Date: Thu, 29 Aug 2024 10:51:22 +0300
Subject: [PATCH] feat: replace SubQueries queue logic with { pest_pair ->
 ast_id } mapping

---
 sbroad-core/src/frontend/sql.rs | 65 +++++++++++++++++++++++++--------
 1 file changed, 50 insertions(+), 15 deletions(-)

diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs
index 47d568d40b..b6bb57cf54 100644
--- a/sbroad-core/src/frontend/sql.rs
+++ b/sbroad-core/src/frontend/sql.rs
@@ -1526,7 +1526,13 @@ struct ExpressionsWorker<'worker, M>
 where
     M: Metadata,
 {
-    subquery_ids_queue: VecDeque<NodeId>,
+    /// Helper map of { sq_pair -> ast_id } used for identifying SQ nodes
+    /// during both general and Pratt parsing.
+    sq_pair_to_ast_ids: &'worker PairToAstIdTranslation<'worker>,
+    /// Map of { sq_ast_id -> sq_plan_id }.
+    /// Used instead of reference to general `map` using during
+    /// parsing in order no to take an immutable reference on it.
+    sq_ast_to_plan_id: Translation,
     /// Vec of BETWEEN expressions met during parsing.
     /// Used later to fix them as soon as we need to resolve double-linking problem
     /// of left expression.
@@ -1568,9 +1574,13 @@ impl<'worker, M> ExpressionsWorker<'worker, M>
 where
     M: Metadata,
 {
-    fn new<'plan: 'worker, 'meta: 'worker>(metadata: &'meta M) -> Self {
+    fn new<'plan: 'worker, 'meta: 'worker>(
+        metadata: &'meta M,
+        sq_pair_to_ast_ids: &'worker PairToAstIdTranslation,
+    ) -> Self {
         Self {
-            subquery_ids_queue: VecDeque::new(),
+            sq_pair_to_ast_ids,
+            sq_ast_to_plan_id: Translation::with_capacity(sq_pair_to_ast_ids.len()),
             subquery_replaces: AHashMap::new(),
             sub_queries_to_fix_queue: VecDeque::new(),
             metadata,
@@ -2433,10 +2443,13 @@ where
                     ParseExpression::PlanId { plan_id: ref_id }
                 }
                 Rule::SubQuery => {
-                    let subquery_plan_id = worker.subquery_ids_queue
-                        .pop_front()
-                        .expect("Corresponding expression subquery is not found");
-                    ParseExpression::SubQueryPlanId { plan_id: subquery_plan_id }
+                    let sq_ast_id = worker
+                        .sq_pair_to_ast_ids
+                        .get(&primary).expect("SQ ast_id must exist for pest pair");
+                    let plan_id = worker
+                        .sq_ast_to_plan_id
+                        .get(*sq_ast_id)?;
+                    ParseExpression::SubQueryPlanId { plan_id }
                 }
                 Rule::Row => {
                     let mut children = Vec::new();
@@ -3036,6 +3049,11 @@ impl<'pairs_map> ParsingPairsMap<'pairs_map> {
     }
 }
 
+/// Hash map of { pair -> ast_id }, where
+/// * pair (key) -- inner pest structure for AST node
+/// * ast_id (value) -- id of our AST node.
+type PairToAstIdTranslation<'i> = HashMap<Pair<'i, Rule>, usize>;
+
 impl AbstractSyntaxTree {
     /// Build an empty AST.
     fn empty() -> Self {
@@ -3062,6 +3080,7 @@ impl AbstractSyntaxTree {
         query: &'query str,
         pairs_map: &mut ParsingPairsMap<'query>,
         pos_to_ast_id: &mut SelectChildPairTranslation,
+        pairs_to_ast_id: &mut PairToAstIdTranslation<'query>,
     ) -> Result<(), SbroadError> {
         let mut command_pair = match ParseTree::parse(Rule::Command, query) {
             Ok(p) => p,
@@ -3130,6 +3149,9 @@ impl AbstractSyntaxTree {
                 Rule::Projection | Rule::OrderBy => {
                     pos_to_ast_id.insert(stack_node.pair.line_col(), arena_node_id);
                 }
+                Rule::SubQuery => {
+                    pairs_to_ast_id.insert(stack_node.pair.clone(), arena_node_id);
+                }
                 _ => {}
             }
 
@@ -3269,6 +3291,7 @@ impl AbstractSyntaxTree {
         metadata: &M,
         pairs_map: &mut ParsingPairsMap,
         pos_to_ast_id: &mut SelectChildPairTranslation,
+        sq_pair_to_ast_ids: &PairToAstIdTranslation,
     ) -> Result<Plan, SbroadError>
     where
         M: Metadata,
@@ -3285,7 +3308,7 @@ impl AbstractSyntaxTree {
         // Counter for `Expression::ValuesRow` output column name aliases ("COLUMN_<`col_idx`>").
         // Is it global for every `ValuesRow` met in the AST.
         let mut col_idx: usize = 0;
-        let mut worker = ExpressionsWorker::new(metadata);
+        let mut worker = ExpressionsWorker::new(metadata, sq_pair_to_ast_ids);
         let mut ctes = CTEs::new();
 
         for level_node in dft_post.iter(top) {
@@ -3299,10 +3322,6 @@ impl AbstractSyntaxTree {
                         .expect("could not find first child id in scan node");
                     let rel_child_id_plan = map.get(*rel_child_id_ast)?;
                     let rel_child_node = plan.get_relation_node(rel_child_id_plan)?;
-                    if let Relational::ScanSubQuery(_) = rel_child_node {
-                        // We want `SubQuery` ids to be used only during expressions parsing.
-                        worker.subquery_ids_queue.pop_front();
-                    }
 
                     map.add(id, rel_child_id_plan);
                     if let Some(ast_alias_id) = node.children.get(1) {
@@ -3349,7 +3368,7 @@ impl AbstractSyntaxTree {
                         .expect("child node id is not found among sub-query children.");
                     let plan_child_id = map.get(*ast_child_id)?;
                     let plan_sq_id = plan.add_sub_query(plan_child_id, None)?;
-                    worker.subquery_ids_queue.push_back(plan_sq_id);
+                    worker.sq_ast_to_plan_id.add(id, plan_sq_id);
                     map.add(id, plan_sq_id);
                 }
                 Rule::VTableMaxRows => {
@@ -4305,15 +4324,31 @@ impl Ast for AbstractSyntaxTree {
         // * Add expressions `ParseNode`s into `arena`
         // * Save copy of them into map of { expr_arena_id -> corresponding pair copy }.
         let mut ast_id_to_pairs_map = ParsingPairsMap::new();
+
+        // Helper variables holding mappings useful for parsing.
+        // Move out of `AbstractSyntaxTree` so as not to add a lifetime template arguments.
         let mut pos_to_ast_id: SelectChildPairTranslation = HashMap::new();
+        let mut sq_pair_to_ast_ids: PairToAstIdTranslation = HashMap::new();
+
         let mut ast = AbstractSyntaxTree::empty();
-        ast.fill(query, &mut ast_id_to_pairs_map, &mut pos_to_ast_id)?;
+        ast.fill(
+            query,
+            &mut ast_id_to_pairs_map,
+            &mut pos_to_ast_id,
+            &mut sq_pair_to_ast_ids,
+        )?;
+
         // Empty query.
         if ast.is_empty() {
             return Ok(Plan::empty());
         }
 
-        ast.resolve_metadata(metadata, &mut ast_id_to_pairs_map, &mut pos_to_ast_id)
+        ast.resolve_metadata(
+            metadata,
+            &mut ast_id_to_pairs_map,
+            &mut pos_to_ast_id,
+            &sq_pair_to_ast_ids,
+        )
     }
 }
 
-- 
GitLab