From af62abf63537e854e6dc4a2962b81ce1d0ce91d6 Mon Sep 17 00:00:00 2001
From: EmirVildanov <reddog201030@gmail.com>
Date: Tue, 4 Jun 2024 15:23:28 +0300
Subject: [PATCH] feat: mock param and transaction settings

---
 sbroad-core/src/frontend/sql.rs          | 61 +++++++++++++++++++++++-
 sbroad-core/src/frontend/sql/ir/tests.rs | 32 +++++++++++++
 sbroad-core/src/frontend/sql/query.pest  | 30 +++++++++++-
 sbroad-core/src/ir/ddl.rs                | 34 +++++++++++++
 4 files changed, 155 insertions(+), 2 deletions(-)

diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs
index add50594b..642a38b2a 100644
--- a/sbroad-core/src/frontend/sql.rs
+++ b/sbroad-core/src/frontend/sql.rs
@@ -26,7 +26,7 @@ use crate::frontend::sql::ast::{
 };
 use crate::frontend::sql::ir::Translation;
 use crate::frontend::Ast;
-use crate::ir::ddl::{ColumnDef, Ddl};
+use crate::ir::ddl::{ColumnDef, Ddl, SetParamScopeType, SetParamValue};
 use crate::ir::ddl::{Language, ParamDef};
 use crate::ir::expression::cast::Type as CastType;
 use crate::ir::expression::{
@@ -789,6 +789,53 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl,
     Ok(create_sharded_table)
 }
 
+fn parse_set_param(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, SbroadError> {
+    let mut scope_type = SetParamScopeType::Session;
+    let mut param_value = None;
+    for child_id in &node.children {
+        let child_node = ast.nodes.get_node(*child_id)?;
+        match child_node.rule {
+            Rule::SetScope => {
+                let set_scope_child_id = child_node
+                    .children
+                    .first()
+                    .expect("SetScope must have child.");
+                let set_scope_child = ast.nodes.get_node(*set_scope_child_id)?;
+                match set_scope_child.rule {
+                    Rule::ScopeSession => {}
+                    Rule::ScopeLocal => scope_type = SetParamScopeType::Local,
+                    _ => panic!("Unexpected rule met under SetScope."),
+                }
+            }
+            Rule::ConfParam => {
+                let conf_param_child_id = child_node
+                    .children
+                    .first()
+                    .expect("ConfParam must have child.");
+                let conf_param_child = ast.nodes.get_node(*conf_param_child_id)?;
+                match conf_param_child.rule {
+                    Rule::NamedParam => {
+                        let param_name_id = conf_param_child
+                            .children
+                            .first()
+                            .expect("Param name expected under NamedParam.");
+                        let param_name = parse_identifier(ast, *param_name_id)?;
+                        param_value = Some(SetParamValue::NamedParam { name: param_name });
+                    }
+                    Rule::TimeZoneParam => param_value = Some(SetParamValue::TimeZone),
+                    _ => panic!("Unexpected rule met under ConfParam."),
+                }
+            }
+            _ => panic!("Unexpected rule met under SetParam."),
+        }
+    }
+    Ok(Ddl::SetParam {
+        scope_type,
+        param_value: param_value.unwrap(),
+        timeout: get_default_timeout(),
+    })
+}
+
 fn parse_select_full(
     ast: &AbstractSyntaxTree,
     node_id: usize,
@@ -3476,6 +3523,18 @@ impl AbstractSyntaxTree {
                     let plan_id = plan.nodes.push(Node::Acl(create_role));
                     map.add(id, plan_id);
                 }
+                Rule::SetParam => {
+                    let set_param_node = parse_set_param(self, node)?;
+                    let plan_id = plan.nodes.push(Node::Ddl(set_param_node));
+                    map.add(id, plan_id);
+                }
+                Rule::SetTransaction => {
+                    let set_transaction_node = Ddl::SetTransaction {
+                        timeout: get_default_timeout(),
+                    };
+                    let plan_id = plan.nodes.push(Node::Ddl(set_transaction_node));
+                    map.add(id, plan_id);
+                }
                 _ => {}
             }
         }
diff --git a/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad-core/src/frontend/sql/ir/tests.rs
index 95b34689d..e9273244e 100644
--- a/sbroad-core/src/frontend/sql/ir/tests.rs
+++ b/sbroad-core/src/frontend/sql/ir/tests.rs
@@ -3556,6 +3556,38 @@ fn front_count_no_params() {
     );
 }
 
+#[test]
+fn front_mock_set_param_transaction() {
+    let queries_to_check = vec![
+        r#"set session default_param = default"#,
+        r#"set session stringparam = 'value'"#,
+        r#"set session identparam to ident"#,
+        r#"set local intparam to -3"#,
+        r#"set local doubleparam = -42.5"#,
+        r#"set doubleparam = -42.5"#,
+        r#"set local time zone local"#,
+        r#"set time zone -3"#,
+        r#"set time zone 'value'"#,
+        r#"set transaction snapshot 'snapshot-string'"#,
+        r#"set transaction read write"#,
+        r#"set transaction read only"#,
+        r#"set transaction deferrable"#,
+        r#"set transaction not deferrable"#,
+        r#"set transaction isolation level serializable"#,
+        r#"set transaction isolation level repeatable read"#,
+        r#"set transaction isolation level read commited"#,
+        r#"set transaction isolation level read uncommited"#,
+        r#"set session characteristics as transaction read only"#,
+        r#"set session characteristics as transaction isolation level read commited"#,
+    ];
+
+    let metadata = &RouterConfigurationMock::new();
+    for query in queries_to_check {
+        let plan = AbstractSyntaxTree::transform_into_plan(query, metadata);
+        assert!(plan.is_ok())
+    }
+}
+
 #[cfg(test)]
 mod cte;
 #[cfg(test)]
diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest
index 4d4e3d3f0..014ed49ef 100644
--- a/sbroad-core/src/frontend/sql/query.pest
+++ b/sbroad-core/src/frontend/sql/query.pest
@@ -55,7 +55,8 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil
             PrivilegeUsage = { ^"usage" }
             PrivilegeWrite = { ^"write" }
 
-DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex | CreateProc | DropProc | RenameProc }
+DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex
+         | CreateProc | DropProc | RenameProc | SetParam | SetTransaction }
     CreateTable = {
         ^"create" ~ ^"table" ~ NewTable ~
         "(" ~ Columns ~ ("," ~ PrimaryKey)? ~ ")" ~
@@ -121,6 +122,33 @@ DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex | CreateProc | DropPr
 
     DropIndex = { ^"drop" ~ ^"index" ~ Identifier ~ TimeoutOption? }
 
+    SetParam = { ^"set" ~ SetScope? ~ ConfParam  }
+        SetScope = { ScopeSession | ScopeLocal }
+            ScopeSession  = { ^"session" }
+            ScopeLocal    = { ^"local" }
+        ConfParam = { NamedParam | TimeZoneParam }
+            NamedParam    = { Identifier ~ (^"to" | "=") ~ NamedParamValue }
+                NamedParamValue = { ParamValueDefault | SingleQuotedString | Identifier | Double | Decimal | Integer }
+            TimeZoneParam = { ^"time" ~ ^"zone" ~ TimeZoneParamValue }
+                TimeZoneParamValue = { NamedParamValue | ParamValueLocal }
+        ParamValueLocal = { ^"local" }
+        ParamValueDefault = { ^"default" }
+
+    SetTransaction = { ^"set" ~ (SetTransactionSubRule | SetSessionCharacteristics) }
+        SetTransactionSubRule = { ^"transaction" ~ (TransactionMode | TransactionSnapshot) }
+        SetSessionCharacteristics = { ^"session" ~ ^"characteristics" ~ ^"as" ~ ^"transaction" ~ TransactionMode }
+            TransactionMode = { IsolationLevel | ReadWrite | ReadOnly | Deferrable }
+                IsolationLevel = { ^"isolation" ~ ^"level" ~ ConcreteIsolationLevel }
+                    ConcreteIsolationLevel = _{ Serializable | RepeatableRead | ReadCommited | ReadUncommited }
+                        Serializable = { ^"serializable" }
+                        RepeatableRead = { ^"repeatable" ~ ^"read" }
+                        ReadCommited = { ^"read" ~ ^"commited" }
+                        ReadUncommited = { ^"read" ~ ^"uncommited" }
+                ReadWrite  = { ^"read" ~ ^"write" }
+                ReadOnly   = { ^"read" ~ ^"only" }
+                Deferrable = { NotFlag? ~ ^"deferrable" }
+            TransactionSnapshot = { ^"snapshot" ~ SingleQuotedString }
+
 Block = { CallProc ~ DqlOption? }
     CallProc = { ^"call" ~ Identifier ~ "(" ~ ProcValues ~ ")" }
         ProcValues = { ProcValue? ~ ("," ~ ProcValue)* }
diff --git a/sbroad-core/src/ir/ddl.rs b/sbroad-core/src/ir/ddl.rs
index 5eb1635e0..f692c2853 100644
--- a/sbroad-core/src/ir/ddl.rs
+++ b/sbroad-core/src/ir/ddl.rs
@@ -38,6 +38,29 @@ pub enum Language {
     SQL,
 }
 
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
+pub enum SetParamScopeType {
+    Local,
+    Session,
+}
+
+// TODO: Fill with actual values.
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
+pub enum SetParamValue {
+    NamedParam { name: SmolStr },
+    TimeZone,
+}
+
+impl SetParamValue {
+    #[must_use]
+    pub fn param_name(&self) -> SmolStr {
+        match self {
+            SetParamValue::NamedParam { name } => name.clone(),
+            SetParamValue::TimeZone => SmolStr::from("TimeZone"),
+        }
+    }
+}
+
 #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
 pub enum Ddl {
     CreateTable {
@@ -98,6 +121,15 @@ pub enum Ddl {
         name: SmolStr,
         timeout: Decimal,
     },
+    SetParam {
+        scope_type: SetParamScopeType,
+        param_value: SetParamValue,
+        timeout: Decimal,
+    },
+    // TODO: Fill with actual values.
+    SetTransaction {
+        timeout: Decimal,
+    },
 }
 
 impl Ddl {
@@ -111,6 +143,8 @@ impl Ddl {
             | Ddl::DropTable { ref timeout, .. }
             | Ddl::CreateIndex { ref timeout, .. }
             | Ddl::DropIndex { ref timeout, .. }
+            | Ddl::SetParam { ref timeout, .. }
+            | Ddl::SetTransaction { ref timeout, .. }
             | Ddl::CreateProc { ref timeout, .. }
             | Ddl::DropProc { ref timeout, .. }
             | Ddl::RenameRoutine { ref timeout, .. } => timeout,
-- 
GitLab