From 189a73f46b1933d6a5b60f0c51cf91ec2f2d16fe Mon Sep 17 00:00:00 2001
From: Emir Vildanov <e.vildanov@picodata.io>
Date: Fri, 22 Sep 2023 06:19:47 +0000
Subject: [PATCH] feat: support DROP USER

---
 doc/sql/query.ebnf                            |  4 +-
 sbroad-cartridge/src/api/exec_query.rs        |  3 +
 .../test_app/test/integration/acl_test.lua    | 31 +++++++
 sbroad-core/src/backend/sql/ir.rs             |  6 ++
 sbroad-core/src/backend/sql/tree.rs           |  6 ++
 sbroad-core/src/executor.rs                   | 12 ++-
 sbroad-core/src/executor/ir.rs                |  4 +
 sbroad-core/src/frontend/sql.rs               | 30 +++++++
 sbroad-core/src/frontend/sql/ast.rs           |  6 ++
 sbroad-core/src/frontend/sql/query.pest       |  8 +-
 sbroad-core/src/ir.rs                         | 41 +++++++---
 sbroad-core/src/ir/acl.rs                     | 80 +++++++++++++++++++
 sbroad-core/src/ir/api/parameter.rs           |  4 +-
 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 +-
 18 files changed, 228 insertions(+), 23 deletions(-)
 create mode 100644 sbroad-cartridge/test_app/test/integration/acl_test.lua
 create mode 100644 sbroad-core/src/ir/acl.rs

diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf
index 3c0e9d5bcd..a0907cee04 100644
--- a/doc/sql/query.ebnf
+++ b/doc/sql/query.ebnf
@@ -1,13 +1,15 @@
-STATEMENT   ::= EXPLAIN | DDL | DML | DQL
+STATEMENT   ::= EXPLAIN | DDL | DML | DQL | ACL
 EXPLAIN     ::= 'EXPLAIN' ( DML | DQL )
 DQL         ::= (SELECT | SELECT UNION ALL SELECT | SELECT (EXCEPT 'DISTINCT'? ) SELECT ) Options?
 DML         ::= (DELETE | INSERT) Options?
 DDL         ::= CreateTable | DropTable
+ACL         ::= DropUser
 CreateTable ::= 'create table' table '(' Column (',' Column)* ',' PrimaryKey ')' ('using' ('memtx' | 'vinyl'))? Distribution ('option' '(' ('timeout' '=' DOUBLE)')')?
 Column      ::= name ('Bool' | 'Decimal' | 'Double' | 'Int' | 'Number' | 'Scalar' | 'String' | 'Text' | 'Unsigned' | 'Varchar') ('null' | 'not null')?
 PrimaryKey  ::= 'primary key' '(' name (',' name)* ')'
 Distribution ::= 'global' | ('distributed by' '(' name (',' name)*  ')')
 DropTable   ::= 'drop table' table ('option' '(' ('timeout' '=' DOUBLE)')')?
+DropUser    ::= 'drop user' user ('option' '(' ('timeout' '=' DOUBLE)')')?
 Options     ::= 'option' '(' ('vtable_max_rows' '=' UNSIGNED)? (',' 'sql_vdbe_max_steps' '=' UNSIGNED)? ')'
 SELECT      ::= 'SELECT' ((column (',' column)*) ) 'FROM' (table ('AS' name)? | '(' (SELECT | VALUES) ')' ('AS' name)?) ( ('INNER')? 'JOIN' (table ('AS' name)? | ('(' (SELECT | VALUES) ')' ('AS' name)?))  'ON' expression )? ( 'WHERE' expression )? ( 'GROUP BY' expression(',' expression)* )?
 VALUES      ::= 'VALUES' '(' row (',' row)* ')'
diff --git a/sbroad-cartridge/src/api/exec_query.rs b/sbroad-cartridge/src/api/exec_query.rs
index 332d15edce..c0e4d8f145 100644
--- a/sbroad-cartridge/src/api/exec_query.rs
+++ b/sbroad-cartridge/src/api/exec_query.rs
@@ -60,6 +60,9 @@ pub extern "C" fn dispatch_query(f_ctx: FunctionCtx, args: FunctionArgs) -> c_in
                 if let Ok(true) = query.is_ddl() {
                     return tarantool_error("DDL queries are not supported");
                 }
+                if let Ok(true) = query.is_acl() {
+                    return tarantool_error("ACL queries are not supported");
+                }
 
                 match query.dispatch() {
                     Ok(result) => child_span("\"tarantool.tuple.return\"", || {
diff --git a/sbroad-cartridge/test_app/test/integration/acl_test.lua b/sbroad-cartridge/test_app/test/integration/acl_test.lua
new file mode 100644
index 0000000000..d9256d69f8
--- /dev/null
+++ b/sbroad-cartridge/test_app/test/integration/acl_test.lua
@@ -0,0 +1,31 @@
+local t = require('luatest')
+local g = t.group('sbroad_with_acl')
+
+local helper = require('test.helper.cluster_no_replication')
+local cluster = nil
+
+g.before_all(
+        function()
+            helper.start_test_cluster(helper.cluster_config)
+            cluster = helper.cluster
+        end
+)
+
+g.after_all(
+        function()
+            helper.stop_test_cluster()
+        end
+)
+
+g.test_drop_user = function()
+    local api = cluster:server("api-1").net_box
+
+    local _, err = api:call(
+            "sbroad.execute",
+            { [[ DROP USER user ]], {} }
+    )
+    t.assert_equals(
+            string.format("%s", err),
+            [[Sbroad Error: ACL queries are not supported]]
+    )
+end
\ No newline at end of file
diff --git a/sbroad-core/src/backend/sql/ir.rs b/sbroad-core/src/backend/sql/ir.rs
index ed80342e25..2cc2959859 100644
--- a/sbroad-core/src/backend/sql/ir.rs
+++ b/sbroad-core/src/backend/sql/ir.rs
@@ -293,6 +293,12 @@ impl ExecutionPlan {
                                     Some("DDL nodes are not supported in the generated SQL".into()),
                                 ));
                             }
+                            Node::Acl(_) => {
+                                return Err(SbroadError::Unsupported(
+                                    Entity::Node,
+                                    Some("ACL 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 428f3da23a..62ba584ac2 100644
--- a/sbroad-core/src/backend/sql/tree.rs
+++ b/sbroad-core/src/backend/sql/tree.rs
@@ -649,6 +649,12 @@ impl<'p> SyntaxPlan<'p> {
                     "DDL node {node:?} is not supported in the syntax plan"
                 )),
             )),
+            Node::Acl(..) => Err(SbroadError::Invalid(
+                Entity::SyntaxPlan,
+                Some(format!(
+                    "ACL 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/executor.rs b/sbroad-core/src/executor.rs
index 4b2843ed15..342c950609 100644
--- a/sbroad-core/src/executor.rs
+++ b/sbroad-core/src/executor.rs
@@ -123,11 +123,11 @@ where
                 }
                 plan.version_map = table_version_map;
             }
-            if !plan.is_ddl()? {
+            if !plan.is_ddl()? && !plan.is_acl()? {
                 cache.put(key, plan.clone())?;
             }
         }
-        if !plan.is_ddl()? {
+        if !plan.is_ddl()? && !plan.is_acl()? {
             plan.bind_params(params)?;
             plan.apply_options()?;
             plan.optimize()?;
@@ -269,6 +269,14 @@ where
     pub fn is_ddl(&self) -> Result<bool, SbroadError> {
         self.exec_plan.get_ir_plan().is_ddl()
     }
+
+    /// Checks that query is ACL.
+    ///
+    /// # Errors
+    /// - Plan is invalid
+    pub fn is_acl(&self) -> Result<bool, SbroadError> {
+        self.exec_plan.get_ir_plan().is_acl()
+    }
 }
 
 #[cfg(test)]
diff --git a/sbroad-core/src/executor/ir.rs b/sbroad-core/src/executor/ir.rs
index d59e4f2bc3..d646319e98 100644
--- a/sbroad-core/src/executor/ir.rs
+++ b/sbroad-core/src/executor/ir.rs
@@ -565,6 +565,10 @@ impl ExecutionPlan {
                     Entity::SubTree,
                     Some("DDL node".to_string()),
                 ))?,
+                Node::Acl { .. } => Err(SbroadError::Invalid(
+                    Entity::SubTree,
+                    Some("ACL node".to_string()),
+                ))?,
             }
             new_plan.nodes.push(node);
             translation.insert(node_id, next_id);
diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs
index 546e09d41e..a43b63a429 100644
--- a/sbroad-core/src/frontend/sql.rs
+++ b/sbroad-core/src/frontend/sql.rs
@@ -30,6 +30,7 @@ use crate::ir::{Node, OptionKind, OptionSpec, Plan};
 use crate::otm::child_span;
 
 use crate::errors::Entity::AST;
+use crate::ir::acl::Acl;
 use crate::ir::aggregates::AggregateKind;
 use crate::ir::helpers::RepeatableState;
 use crate::ir::transformation::redistribution::ColumnPosition;
@@ -1831,6 +1832,34 @@ impl Ast for AbstractSyntaxTree {
                     let plan_id = plan.nodes.push(Node::Ddl(drop_table));
                     map.add(id, plan_id);
                 }
+                Type::DropUser => {
+                    let user_name_id = node.children.first().ok_or_else(|| {
+                        SbroadError::Invalid(
+                            Entity::ParseNode,
+                            Some(String::from("UserName expected under DropUser node")),
+                        )
+                    })?;
+                    let user_name_node = self.nodes.get_node(*user_name_id)?;
+                    let user_name = normalize_name_for_space_api(
+                        user_name_node.value.as_ref().ok_or_else(|| {
+                            SbroadError::NotFound(
+                                Entity::Node,
+                                "user name in the drop user AST".into(),
+                            )
+                        })?,
+                    );
+
+                    let mut timeout = default_timeout;
+                    if let Some(timeout_child_id) = node.children.get(1) {
+                        timeout = get_timeout(self, *timeout_child_id)?;
+                    }
+                    let drop_user = Acl::DropUser {
+                        name: user_name,
+                        timeout,
+                    };
+                    let plan_id = plan.nodes.push(Node::Acl(drop_user));
+                    map.add(id, plan_id);
+                }
                 Type::Add
                 | Type::AliasName
                 | Type::Columns
@@ -1889,6 +1918,7 @@ impl Ast for AbstractSyntaxTree {
                 | Type::TypeVarchar
                 | Type::UpdateList
                 | Type::UpdateItem
+                | Type::UserName
                 | Type::Vinyl => {}
                 rule => {
                     return Err(SbroadError::NotImplemented(
diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs
index b4323840f2..30ae5d52cd 100644
--- a/sbroad-core/src/frontend/sql/ast.rs
+++ b/sbroad-core/src/frontend/sql/ast.rs
@@ -58,6 +58,7 @@ pub enum Type {
     Divide,
     Double,
     DropTable,
+    DropUser,
     Duration,
     Engine,
     Eq,
@@ -138,6 +139,7 @@ pub enum Type {
     UpdateList,
     UpdateItem,
     Unsigned,
+    UserName,
     Value,
     Values,
     ValuesRow,
@@ -180,6 +182,7 @@ impl Type {
             Rule::Divide => Ok(Type::Divide),
             Rule::Double => Ok(Type::Double),
             Rule::DropTable => Ok(Type::DropTable),
+            Rule::DropUser => Ok(Type::DropUser),
             Rule::Duration => Ok(Type::Duration),
             Rule::DoReplace => Ok(Type::DoReplace),
             Rule::DoNothing => Ok(Type::DoNothing),
@@ -262,6 +265,7 @@ impl Type {
             Rule::UpdateList => Ok(Type::UpdateList),
             Rule::UpdateItem => Ok(Type::UpdateItem),
             Rule::Unsigned => Ok(Type::Unsigned),
+            Rule::UserName => Ok(Type::UserName),
             Rule::Value => Ok(Type::Value),
             Rule::Values => Ok(Type::Values),
             Rule::ValuesRow => Ok(Type::ValuesRow),
@@ -311,6 +315,7 @@ impl fmt::Display for Type {
             Type::Divide => "Divide".to_string(),
             Type::Double => "Double".to_string(),
             Type::DropTable => "DropTable".to_string(),
+            Type::DropUser => "DropUser".to_string(),
             Type::Duration => "Duration".to_string(),
             Type::DoReplace => "DoUpdateStrategy".to_string(),
             Type::DoNothing => "DoNothingStrategy".to_string(),
@@ -388,6 +393,7 @@ impl fmt::Display for Type {
             Type::Update => "Update".to_string(),
             Type::UpdateItem => "UpdateItem".to_string(),
             Type::UpdateList => "UpdateList".to_string(),
+            Type::UserName => "UserName".to_string(),
             Type::Unsigned => "Unsigned".to_string(),
             Type::Value => "Value".to_string(),
             Type::Values => "Values".to_string(),
diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest
index 6bff739cd3..315c6f8e17 100644
--- a/sbroad-core/src/frontend/sql/query.pest
+++ b/sbroad-core/src/frontend/sql/query.pest
@@ -1,4 +1,8 @@
-Command = _{ SOI ~ (ExplainQuery | Query | DDL) ~ EOF }
+Command = _{ SOI ~ (ExplainQuery | Query | DDL | ACL) ~ EOF }
+
+ACL = _{ DropUser }
+    DropUser = { ^"drop" ~ ^"user" ~ UserName ~ Option? }
+        UserName = @{ Name }
 
 DDL = _{ CreateTable | DropTable }
     CreateTable = {
@@ -82,7 +86,7 @@ Query = { (Except | UnionAll | Select | Values | Insert | Update | Delete) ~ Opt
         ValuesRow = { Row }
     Option = _{ ^"option" ~ "(" ~ OptionParam ~ ("," ~ OptionParam)* ~ ")" }
     OptionParam = _{ Timeout | SqlVdbeMaxSteps | VTableMaxRows }
-    Timeout = { ^"timeout" ~ "=" ~ Duration}
+    Timeout = { ^"timeout" ~ "=" ~ Duration }
        Duration = @{ Unsigned ~ ("." ~ Unsigned)? }
     SqlVdbeMaxSteps = { ^"sql_vdbe_max_steps" ~ "=" ~ (Unsigned | Parameter) }
     VTableMaxRows = { ^"vtable_max_rows" ~ "=" ~ (Unsigned | Parameter) }
diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs
index 98b53b46c2..528ec874fb 100644
--- a/sbroad-core/src/ir.rs
+++ b/sbroad-core/src/ir.rs
@@ -11,6 +11,7 @@ use std::fmt::{Display, Formatter};
 use std::slice::Iter;
 use tarantool::tlua;
 
+use acl::Acl;
 use ddl::Ddl;
 use expression::Expression;
 use operator::{Arithmetic, Relational};
@@ -32,6 +33,7 @@ use crate::{collection, error, warn};
 use self::parameters::Parameters;
 use self::relation::Relations;
 
+pub mod acl;
 pub mod aggregates;
 pub mod ddl;
 pub mod distribution;
@@ -63,6 +65,7 @@ const DEFAULT_VDBE_MAX_STEPS: u64 = 45000;
 /// dispatching and its performance penalties.
 #[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
 pub enum Node {
+    Acl(Acl),
     Ddl(Ddl),
     Expression(Expression),
     Relational(Relational),
@@ -751,6 +754,15 @@ impl Plan {
         Ok(matches!(self.get_node(top_id)?, Node::Ddl(..)))
     }
 
+    /// Checks that plan is ACL query.
+    ///
+    /// # Errors
+    /// - top node doesn't exist in the plan or is invalid.
+    pub fn is_acl(&self) -> Result<bool, SbroadError> {
+        let top_id = self.get_top()?;
+        Ok(matches!(self.get_node(top_id)?, Node::Acl(..)))
+    }
+
     /// Set top node of plan
     /// # Errors
     /// - top node doesn't exist in the plan.
@@ -769,10 +781,12 @@ impl Plan {
         let node = self.get_node(node_id)?;
         match node {
             Node::Relational(rel) => Ok(rel),
-            Node::Expression(_) | Node::Parameter | Node::Ddl(..) => Err(SbroadError::Invalid(
-                Entity::Node,
-                Some(format!("node is not Relational type: {node:?}")),
-            )),
+            Node::Expression(_) | Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {
+                Err(SbroadError::Invalid(
+                    Entity::Node,
+                    Some(format!("node is not Relational type: {node:?}")),
+                ))
+            }
         }
     }
 
@@ -787,10 +801,9 @@ impl Plan {
     ) -> Result<&mut Relational, SbroadError> {
         match self.get_mut_node(node_id)? {
             Node::Relational(rel) => Ok(rel),
-            Node::Expression(_) | Node::Parameter | Node::Ddl(..) => Err(SbroadError::Invalid(
-                Entity::Node,
-                Some("Node is not relational".into()),
-            )),
+            Node::Expression(_) | Node::Parameter | Node::Ddl(..) | Node::Acl(..) => Err(
+                SbroadError::Invalid(Entity::Node, Some("Node is not relational".into())),
+            ),
         }
     }
 
@@ -813,7 +826,7 @@ impl Plan {
                     ))
                 }
             }
-            Node::Relational(_) | Node::Ddl(..) => Err(SbroadError::Invalid(
+            Node::Relational(_) | Node::Ddl(..) | Node::Acl(..) => Err(SbroadError::Invalid(
                 Entity::Node,
                 Some("node is not Expression type".into()),
             )),
@@ -832,10 +845,12 @@ impl Plan {
         let node = self.get_mut_node(node_id)?;
         match node {
             Node::Expression(exp) => Ok(exp),
-            Node::Relational(_) | Node::Parameter | Node::Ddl(..) => Err(SbroadError::Invalid(
-                Entity::Node,
-                Some(format!("node ({node_id}) is not expression type: {node:?}")),
-            )),
+            Node::Relational(_) | Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {
+                Err(SbroadError::Invalid(
+                    Entity::Node,
+                    Some(format!("node ({node_id}) is not expression type: {node:?}")),
+                ))
+            }
         }
     }
 
diff --git a/sbroad-core/src/ir/acl.rs b/sbroad-core/src/ir/acl.rs
new file mode 100644
index 0000000000..53a702b62b
--- /dev/null
+++ b/sbroad-core/src/ir/acl.rs
@@ -0,0 +1,80 @@
+use crate::ir::{Entity, Node, Plan, SbroadError};
+use serde::{Deserialize, Serialize};
+use tarantool::decimal::Decimal;
+
+#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
+pub enum Acl {
+    DropUser { name: String, timeout: Decimal },
+}
+
+impl Acl {
+    /// Return ACL node timeout.
+    ///
+    /// # Errors
+    /// - timeout parsing error
+    pub fn timeout(&self) -> Result<f64, SbroadError> {
+        match self {
+            Acl::DropUser { ref timeout, .. } => timeout,
+        }
+        .to_string()
+        .parse()
+        .map_err(|e| {
+            SbroadError::Invalid(
+                Entity::SpaceMetadata,
+                Some(format!("timeout parsing error {e:?}")),
+            )
+        })
+    }
+}
+
+impl Plan {
+    /// Get ACL node from the plan arena.
+    ///
+    /// # Errors
+    /// - the node index is absent in arena
+    /// - current node is not of ACL type
+    pub fn get_acl_node(&self, node_id: usize) -> Result<&Acl, SbroadError> {
+        let node = self.get_node(node_id)?;
+        match node {
+            Node::Acl(acl) => Ok(acl),
+            _ => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node is not ACL type: {node:?}")),
+            )),
+        }
+    }
+
+    /// Get a mutable ACL node from the plan arena.
+    ///
+    /// # Errors
+    /// - the node index is absent in arena
+    /// - current node is not of ACL type
+    pub fn get_mut_acl_node(&mut self, node_id: usize) -> Result<&mut Acl, SbroadError> {
+        let node = self.get_mut_node(node_id)?;
+        match node {
+            Node::Acl(acl) => Ok(acl),
+            _ => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node is not ACL type: {node:?}")),
+            )),
+        }
+    }
+
+    /// Take ACL node from the plan arena and replace it with parameter node.
+    ///
+    /// # Errors
+    /// - current node is not of ACL type
+    pub fn take_acl_node(&mut self, node_id: usize) -> Result<Acl, SbroadError> {
+        // Check that node is ACL type (before making any distructive operations).
+        let _ = self.get_acl_node(node_id)?;
+        // Replace ACL with parameter node.
+        let node = std::mem::replace(self.get_mut_node(node_id)?, Node::Parameter);
+        match node {
+            Node::Acl(acl) => Ok(acl),
+            _ => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some(format!("node is not ACL type: {node:?}")),
+            )),
+        }
+    }
+}
diff --git a/sbroad-core/src/ir/api/parameter.rs b/sbroad-core/src/ir/api/parameter.rs
index a57212a629..9180092d77 100644
--- a/sbroad-core/src/ir/api/parameter.rs
+++ b/sbroad-core/src/ir/api/parameter.rs
@@ -174,7 +174,7 @@ impl Plan {
                     }
                     Expression::Constant { .. } | Expression::CountAsterisk => {}
                 },
-                Node::Parameter | Node::Ddl(..) => {}
+                Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {}
             }
         }
 
@@ -281,7 +281,7 @@ impl Plan {
                     }
                     Expression::Constant { .. } | Expression::CountAsterisk => {}
                 },
-                Node::Parameter | Node::Ddl(..) => {}
+                Node::Parameter | Node::Ddl(..) | Node::Acl(..) => {}
             }
         }
 
diff --git a/sbroad-core/src/ir/distribution.rs b/sbroad-core/src/ir/distribution.rs
index 43dcb5282c..b60278171b 100644
--- a/sbroad-core/src/ir/distribution.rs
+++ b/sbroad-core/src/ir/distribution.rs
@@ -650,6 +650,10 @@ impl Plan {
                 Entity::Distribution,
                 Some("Failed to get distribution for a DDL node.".to_string()),
             )),
+            Node::Acl(_) => Err(SbroadError::Invalid(
+                Entity::Distribution,
+                Some("Failed to get distribution for a ACL node.".to_string()),
+            )),
         }
     }
 
diff --git a/sbroad-core/src/ir/expression/types.rs b/sbroad-core/src/ir/expression/types.rs
index 72f1357f25..c850880af7 100644
--- a/sbroad-core/src/ir/expression/types.rs
+++ b/sbroad-core/src/ir/expression/types.rs
@@ -18,6 +18,10 @@ impl Plan {
                 Entity::Node,
                 Some("DDL node has no type".to_string()),
             )),
+            Node::Acl(_) => Err(SbroadError::Invalid(
+                Entity::Node,
+                Some("ACL 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 6f612fdf8e..d31efa4446 100644
--- a/sbroad-core/src/ir/tree/expression.rs
+++ b/sbroad-core/src/ir/tree/expression.rs
@@ -180,7 +180,8 @@ fn expression_next<'nodes>(
             )
             | Node::Relational(_)
             | Node::Parameter
-            | Node::Ddl(_),
+            | Node::Ddl(_)
+            | Node::Acl(_),
         )
         | None => None,
     }
diff --git a/sbroad-core/src/ir/tree/relation.rs b/sbroad-core/src/ir/tree/relation.rs
index e7296406e5..06441067f1 100644
--- a/sbroad-core/src/ir/tree/relation.rs
+++ b/sbroad-core/src/ir/tree/relation.rs
@@ -89,7 +89,8 @@ fn relational_next<'nodes>(
             Node::Relational(Relational::ScanRelation { .. })
             | Node::Expression(_)
             | Node::Parameter
-            | Node::Ddl(_),
+            | Node::Ddl(_)
+            | Node::Acl(_),
         )
         | None => None,
     }
diff --git a/sbroad-core/src/ir/tree/subtree.rs b/sbroad-core/src/ir/tree/subtree.rs
index a9434a6330..ad2a43002c 100644
--- a/sbroad-core/src/ir/tree/subtree.rs
+++ b/sbroad-core/src/ir/tree/subtree.rs
@@ -193,7 +193,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(..) => None,
+            Node::Parameter | Node::Ddl(..) | Node::Acl(..) => None,
             Node::Expression(exp) => match exp {
                 Expression::Alias { child, .. }
                 | Expression::Cast { child, .. }
-- 
GitLab