From 1f24b51bfd705bd0e06bc5e165942c462d67ca77 Mon Sep 17 00:00:00 2001
From: Denis Smirnov <sd@picodata.io>
Date: Mon, 5 Feb 2024 16:47:52 +0700
Subject: [PATCH] feat: introduce a new node type - code block

---
 sbroad-core/src/backend/sql/ir.rs      |  9 ++++
 sbroad-core/src/backend/sql/tree.rs    |  6 +++
 sbroad-core/src/errors.rs              |  3 ++
 sbroad-core/src/executor.rs            | 12 ++++-
 sbroad-core/src/executor/ir.rs         |  4 ++
 sbroad-core/src/ir.rs                  | 58 +++++++++++++++--------
 sbroad-core/src/ir/api/parameter.rs    |  4 +-
 sbroad-core/src/ir/block.rs            | 64 ++++++++++++++++++++++++++
 sbroad-core/src/ir/distribution.rs     |  4 ++
 sbroad-core/src/ir/expression/types.rs |  4 ++
 sbroad-core/src/ir/tree/expression.rs  |  3 +-
 sbroad-core/src/ir/tree/relation.rs    |  3 +-
 sbroad-core/src/ir/tree/subtree.rs     |  2 +-
 13 files changed, 150 insertions(+), 26 deletions(-)
 create mode 100644 sbroad-core/src/ir/block.rs

diff --git a/sbroad-core/src/backend/sql/ir.rs b/sbroad-core/src/backend/sql/ir.rs
index 022374d1ca..aa8c94c4c0 100644
--- a/sbroad-core/src/backend/sql/ir.rs
+++ b/sbroad-core/src/backend/sql/ir.rs
@@ -297,6 +297,15 @@ impl ExecutionPlan {
                                     Some("ACL nodes are not supported in the generated SQL".into()),
                                 ));
                             }
+                            Node::Block(_) => {
+                                return Err(SbroadError::Unsupported(
+                                    Entity::Node,
+                                    Some(
+                                        "Code block nodes are not supported in the generated SQL"
+                                            .into(),
+                                    ),
+                                ));
+                            }
                             Node::Parameter => {
                                 return Err(SbroadError::Unsupported(
                                     Entity::Node,
diff --git a/sbroad-core/src/backend/sql/tree.rs b/sbroad-core/src/backend/sql/tree.rs
index e1c609029d..dc67aba054 100644
--- a/sbroad-core/src/backend/sql/tree.rs
+++ b/sbroad-core/src/backend/sql/tree.rs
@@ -665,6 +665,12 @@ impl<'p> SyntaxPlan<'p> {
                     "ACL node {node:?} is not supported in the syntax plan"
                 )),
             )),
+            Node::Block(..) => Err(SbroadError::Invalid(
+                Entity::SyntaxPlan,
+                Some(format!(
+                    "Block node {node:?} is not supported in the syntax plan"
+                )),
+            )),
             Node::Parameter => {
                 let sn = SyntaxNode::new_parameter(id);
                 Ok(self.nodes.push_syntax_node(sn))
diff --git a/sbroad-core/src/errors.rs b/sbroad-core/src/errors.rs
index da6b1472af..6161b5f996 100644
--- a/sbroad-core/src/errors.rs
+++ b/sbroad-core/src/errors.rs
@@ -88,6 +88,8 @@ pub enum Entity {
     OptionSpec,
     /// corresponds to struct ReferredNodes
     ReferredNodes,
+    /// Routine
+    Routine,
     /// parser rule
     Rule,
     /// corresponds to struct RouterRuntime
@@ -182,6 +184,7 @@ impl fmt::Display for Entity {
             Entity::Relational => "relational".to_string(),
             Entity::RequiredData => "required data".to_string(),
             Entity::ReferredNodes => "referred nodes".to_string(),
+            Entity::Routine => "routine".to_string(),
             Entity::Rule => "rule".to_string(),
             Entity::Runtime => "runtime".to_string(),
             Entity::Option => "option".to_string(),
diff --git a/sbroad-core/src/executor.rs b/sbroad-core/src/executor.rs
index 7e27a6c5af..cb9ccca14e 100644
--- a/sbroad-core/src/executor.rs
+++ b/sbroad-core/src/executor.rs
@@ -141,11 +141,11 @@ where
                 }
                 plan.version_map = table_version_map;
             }
-            if !plan.is_ddl()? && !plan.is_acl()? {
+            if !plan.is_ddl()? && !plan.is_acl()? && !plan.is_block()? {
                 cache.put(key, plan.clone())?;
             }
         }
-        if !plan.is_ddl()? && !plan.is_acl()? {
+        if !plan.is_ddl()? && !plan.is_acl()? && !plan.is_block()? {
             plan.bind_params(params)?;
             plan.apply_options()?;
             plan.optimize()?;
@@ -265,6 +265,14 @@ where
         self.is_explain
     }
 
+    /// Checks that query is a statement block.
+    ///
+    /// # Errors
+    /// - plan is invalid
+    pub fn is_block(&self) -> Result<bool, SbroadError> {
+        self.exec_plan.get_ir_plan().is_block()
+    }
+
     /// Checks that query is DDL.
     ///
     /// # Errors
diff --git a/sbroad-core/src/executor/ir.rs b/sbroad-core/src/executor/ir.rs
index 86c30a86d7..7f2eb82656 100644
--- a/sbroad-core/src/executor/ir.rs
+++ b/sbroad-core/src/executor/ir.rs
@@ -616,6 +616,10 @@ impl ExecutionPlan {
                     Entity::SubTree,
                     Some("ACL node".to_string()),
                 ))?,
+                Node::Block { .. } => Err(SbroadError::Invalid(
+                    Entity::SubTree,
+                    Some("code block node".to_string()),
+                ))?,
             }
             new_plan.nodes.push(node);
             translation.insert(node_id, next_id);
diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs
index 0ded073e7c..1996287173 100644
--- a/sbroad-core/src/ir.rs
+++ b/sbroad-core/src/ir.rs
@@ -12,6 +12,7 @@ use std::slice::Iter;
 use tarantool::tlua;
 
 use acl::Acl;
+use block::Block;
 use ddl::Ddl;
 use expression::Expression;
 use operator::{Arithmetic, Relational};
@@ -38,6 +39,7 @@ use self::relation::Relations;
 #[allow(elided_lifetimes_in_associated_constant)]
 pub mod acl;
 pub mod aggregates;
+pub mod block;
 pub mod ddl;
 pub mod distribution;
 pub mod expression;
@@ -69,6 +71,7 @@ const DEFAULT_VDBE_MAX_STEPS: u64 = 45000;
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub enum Node {
     Acl(Acl),
+    Block(Block),
     Ddl(Ddl),
     Expression(Expression),
     Relational(Relational),
@@ -804,6 +807,15 @@ impl Plan {
         self.is_explain
     }
 
+    /// Checks that plan is a block of queries.
+    ///
+    /// # Errors
+    /// - top node doesn't exist in the plan or is invalid.
+    pub fn is_block(&self) -> Result<bool, SbroadError> {
+        let top_id = self.get_top()?;
+        Ok(matches!(self.get_node(top_id)?, Node::Block(..)))
+    }
+
     /// Checks that plan is DDL query
     ///
     /// # Errors
@@ -840,12 +852,14 @@ impl Plan {
         let node = self.get_node(node_id)?;
         match node {
             Node::Relational(rel) => Ok(rel),
-            Node::Expression(_) | Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {
-                Err(SbroadError::Invalid(
-                    Entity::Node,
-                    Some(format!("node is not Relational type: {node:?}")),
-                ))
-            }
+            Node::Expression(_)
+            | Node::Parameter
+            | Node::Ddl(..)
+            | Node::Acl(..)
+            | Node::Block(..) => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node is not Relational type: {node:?}")),
+            )),
         }
     }
 
@@ -860,9 +874,14 @@ impl Plan {
     ) -> Result<&mut Relational, SbroadError> {
         match self.get_mut_node(node_id)? {
             Node::Relational(rel) => Ok(rel),
-            Node::Expression(_) | Node::Parameter | Node::Ddl(..) | Node::Acl(..) => Err(
-                SbroadError::Invalid(Entity::Node, Some("Node is not relational".into())),
-            ),
+            Node::Expression(_)
+            | Node::Parameter
+            | Node::Ddl(..)
+            | Node::Acl(..)
+            | Node::Block(..) => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some("Node is not relational".into()),
+            )),
         }
     }
 
@@ -885,10 +904,9 @@ impl Plan {
                     ))
                 }
             }
-            Node::Relational(_) | Node::Ddl(..) | Node::Acl(..) => Err(SbroadError::Invalid(
-                Entity::Node,
-                Some("node is not Expression type".into()),
-            )),
+            Node::Relational(_) | Node::Ddl(..) | Node::Acl(..) | Node::Block(..) => Err(
+                SbroadError::Invalid(Entity::Node, Some("node is not Expression type".into())),
+            ),
         }
     }
 
@@ -904,12 +922,14 @@ impl Plan {
         let node = self.get_mut_node(node_id)?;
         match node {
             Node::Expression(exp) => Ok(exp),
-            Node::Relational(_) | Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {
-                Err(SbroadError::Invalid(
-                    Entity::Node,
-                    Some(format!("node ({node_id}) is not expression type: {node:?}")),
-                ))
-            }
+            Node::Relational(_)
+            | Node::Parameter
+            | Node::Ddl(..)
+            | Node::Acl(..)
+            | Node::Block(..) => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node ({node_id}) is not expression type: {node:?}")),
+            )),
         }
     }
 
diff --git a/sbroad-core/src/ir/api/parameter.rs b/sbroad-core/src/ir/api/parameter.rs
index a15a34f2d3..0683099344 100644
--- a/sbroad-core/src/ir/api/parameter.rs
+++ b/sbroad-core/src/ir/api/parameter.rs
@@ -247,7 +247,7 @@ impl Plan {
                     | Expression::Constant { .. }
                     | Expression::CountAsterisk => {}
                 },
-                Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {}
+                Node::Parameter | Node::Ddl(..) | Node::Acl(..) | Node::Block(..) => {}
             }
         }
 
@@ -360,7 +360,7 @@ impl Plan {
                     | Expression::Constant { .. }
                     | Expression::CountAsterisk => {}
                 },
-                Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {}
+                Node::Parameter | Node::Ddl(..) | Node::Acl(..) | Node::Block(..) => {}
             }
         }
 
diff --git a/sbroad-core/src/ir/block.rs b/sbroad-core/src/ir/block.rs
new file mode 100644
index 0000000000..be8d4e140b
--- /dev/null
+++ b/sbroad-core/src/ir/block.rs
@@ -0,0 +1,64 @@
+use crate::errors::{Entity, SbroadError};
+use crate::ir::{Node, NodeId, Plan};
+
+use serde::{Deserialize, Serialize};
+
+#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
+pub enum Block {
+    /// Procedure body.
+    Procedure {
+        /// The name of the procedure.
+        name: String,
+        /// Passed values to the procedure.
+        values: Vec<NodeId>,
+    },
+}
+
+impl Default for Block {
+    fn default() -> Self {
+        Block::Procedure {
+            name: String::new(),
+            values: vec![],
+        }
+    }
+}
+
+impl Plan {
+    /// Get a reference to a block node.
+    ///
+    /// # Errors
+    /// - the node is not a block node.
+    pub fn get_block_node(&self, node_id: NodeId) -> Result<&Block, SbroadError> {
+        let node = self.get_node(node_id)?;
+        match node {
+            Node::Block(block) => Ok(block),
+            Node::Expression(_)
+            | Node::Relational(_)
+            | Node::Ddl(..)
+            | Node::Acl(..)
+            | Node::Parameter => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node {node:?} (id {node_id}) is not Block type")),
+            )),
+        }
+    }
+
+    /// Get a mutable reference to a block node.
+    ///
+    /// # Errors
+    /// - the node is not a block node.
+    pub fn get_mut_block_node(&mut self, node_id: NodeId) -> Result<&mut Block, SbroadError> {
+        let node = self.get_mut_node(node_id)?;
+        match node {
+            Node::Block(block) => Ok(block),
+            Node::Expression(_)
+            | Node::Relational(_)
+            | Node::Ddl(..)
+            | Node::Acl(..)
+            | Node::Parameter => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node {node:?} (id {node_id}) is not Block type")),
+            )),
+        }
+    }
+}
diff --git a/sbroad-core/src/ir/distribution.rs b/sbroad-core/src/ir/distribution.rs
index e865b3d4b0..832d16d0d7 100644
--- a/sbroad-core/src/ir/distribution.rs
+++ b/sbroad-core/src/ir/distribution.rs
@@ -798,6 +798,10 @@ impl Plan {
                 Entity::Distribution,
                 Some("Failed to get distribution for a ACL node.".to_string()),
             )),
+            Node::Block(_) => Err(SbroadError::Invalid(
+                Entity::Distribution,
+                Some("Failed to get distribution for a code block node.".to_string()),
+            )),
         }
     }
 
diff --git a/sbroad-core/src/ir/expression/types.rs b/sbroad-core/src/ir/expression/types.rs
index 08e231eed2..38bffe7386 100644
--- a/sbroad-core/src/ir/expression/types.rs
+++ b/sbroad-core/src/ir/expression/types.rs
@@ -22,6 +22,10 @@ impl Plan {
                 Entity::Node,
                 Some("ACL node has no type".to_string()),
             )),
+            Node::Block(_) => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some("code block node has no type".to_string()),
+            )),
         }
     }
 }
diff --git a/sbroad-core/src/ir/tree/expression.rs b/sbroad-core/src/ir/tree/expression.rs
index d31efa4446..5337a7fee3 100644
--- a/sbroad-core/src/ir/tree/expression.rs
+++ b/sbroad-core/src/ir/tree/expression.rs
@@ -181,7 +181,8 @@ fn expression_next<'nodes>(
             | Node::Relational(_)
             | Node::Parameter
             | Node::Ddl(_)
-            | Node::Acl(_),
+            | Node::Acl(_)
+            | Node::Block(_),
         )
         | None => None,
     }
diff --git a/sbroad-core/src/ir/tree/relation.rs b/sbroad-core/src/ir/tree/relation.rs
index c5366fb6ac..813874718b 100644
--- a/sbroad-core/src/ir/tree/relation.rs
+++ b/sbroad-core/src/ir/tree/relation.rs
@@ -100,7 +100,8 @@ fn relational_next<'nodes>(
             | Node::Expression(_)
             | Node::Parameter
             | Node::Ddl(_)
-            | Node::Acl(_),
+            | Node::Acl(_)
+            | Node::Block(_),
         )
         | None => None,
     }
diff --git a/sbroad-core/src/ir/tree/subtree.rs b/sbroad-core/src/ir/tree/subtree.rs
index b0938f9632..4fca332c72 100644
--- a/sbroad-core/src/ir/tree/subtree.rs
+++ b/sbroad-core/src/ir/tree/subtree.rs
@@ -195,7 +195,7 @@ fn subtree_next<'plan>(
 ) -> Option<&'plan usize> {
     if let Some(child) = iter.get_nodes().arena.get(iter.get_current()) {
         return match child {
-            Node::Parameter | Node::Ddl(..) | Node::Acl(..) => None,
+            Node::Parameter | Node::Ddl(..) | Node::Acl(..) | Node::Block(..) => None,
             Node::Expression(exp) => match exp {
                 Expression::Alias { child, .. }
                 | Expression::Cast { child, .. }
-- 
GitLab