From 6b972a01ec914f8e3994333a544bb4dc13785ea8 Mon Sep 17 00:00:00 2001 From: Kaitmazian Maksim <m.kaitmazian@picodata.io> Date: Fri, 2 Feb 2024 19:09:21 +0300 Subject: [PATCH] feat: implement SQL support for drop procedure --- doc/sql/query.ebnf | 3 +- sbroad-core/src/frontend/sql.rs | 137 +++++++++++++++++++----- sbroad-core/src/frontend/sql/ast.rs | 6 ++ sbroad-core/src/frontend/sql/query.pest | 6 +- sbroad-core/src/ir/ddl.rs | 8 +- 5 files changed, 128 insertions(+), 32 deletions(-) diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index 7456d29ff6..56122ea580 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -2,7 +2,7 @@ statement ::= explain | ddl | dml | dql | acl explain ::= 'EXPLAIN' ( dml | dql ) dql ::= (select | select union all select | select (except 'DISTINCT'? ) select ) options? dml ::= (delete | insert | update) options? -ddl ::= create_table | drop_table | create_procedure +ddl ::= create_table | drop_table | create_procedure | drop_procedure acl ::= drop_role | drop_user | create_role | create_user | alter_user | grant_privilege | revoke_privilege revoke_privilege ::= 'REVOKE' privilege 'FROM' (role | user) ('OPTION' '(' ('TIMEOUT' '=' double)')')? grant_privilege ::= 'GRANT' privilege 'TO' (role | user) ('OPTION' '(' ('TIMEOUT' '=' double)')')? @@ -23,6 +23,7 @@ alter_user ::= 'ALTER USER' user 'WITH'? ('LOGIN' | 'NOLOGIN' | 'PASSWORD' "'" column ::= name ('BOOL' | 'DECIMAL' | 'DOUBLE' | 'INT' | 'NUMBER' | 'SCALAR' | 'STRING' | 'TEXT' | 'UNSIGNED' | 'VARCHAR') (('NOT'?) 'NULL')? primary_key ::= 'PRIMARY KEY' '(' name (',' name)* ')' distribution ::= 'GLOBAL' | ('DISTRIBUTED BY' '(' name (',' name)* ')') +drop_procedure ::= 'DROP PROCEDURE' procedure ('(' type (',' type)* ')')? ('OPTION' '(' ('TIMEOUT' '=' double)')')? drop_table ::= 'DROP TABLE' table ('OPTION' '(' ('TIMEOUT' '=' double)')')? drop_role ::= 'DROP ROLE' role ('OPTION' '(' ('TIMEOUT' '=' double)')')? drop_user ::= 'DROP USER' user ('OPTION' '(' ('TIMEOUT' '=' double)')')? diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index aa59409195..99c5de8dd3 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -117,6 +117,47 @@ fn parse_string_value_node(ast: &AbstractSyntaxTree, node_id: usize) -> Result<& Ok(string_value.as_str()) } +fn parse_proc_params( + ast: &AbstractSyntaxTree, + params_node: &ParseNode, +) -> Result<Vec<ParamDef>, SbroadError> { + if params_node.rule != Type::ProcParams { + return Err(SbroadError::Invalid( + Entity::Type, + Some("proc params".into()), + )); + } + + let mut params = Vec::with_capacity(params_node.children.len()); + for param_id in ¶ms_node.children { + let param_def_node = ast.nodes.get_node(*param_id)?; + match param_def_node.rule { + Type::ProcParamDef => { + let type_id = *param_def_node + .children + .first() + .expect("ProcParamDef node must contain exactly one child (type node)."); + assert!(param_def_node.children.get(1).is_none()); + let type_node = ast.nodes.get_node(type_id)?; + let data_type = match type_node.rule { + Type::TypeBool => RelationType::Boolean, + Type::TypeDecimal => RelationType::Decimal, + Type::TypeDouble => RelationType::Double, + Type::TypeInt => RelationType::Integer, + Type::TypeNumber => RelationType::Number, + Type::TypeScalar => RelationType::Scalar, + Type::TypeString | Type::TypeText | Type::TypeVarchar => RelationType::String, + Type::TypeUnsigned => RelationType::Unsigned, + _ => panic!("Unexpected node: {type_node:?}"), + }; + params.push(ParamDef { data_type }); + } + _ => panic!("Unexpected node: {param_def_node:?}"), + } + } + Ok(params) +} + fn parse_create_proc(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, SbroadError> { if node.rule != Type::CreateProc { return Err(SbroadError::Invalid( @@ -136,35 +177,7 @@ fn parse_create_proc(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, proc_name = normalize_name_for_space_api(parse_string_value_node(ast, *child_id)?); } Type::ProcParams => { - let params_node = ast.nodes.get_node(*child_id)?; - params.reserve(params_node.children.len()); - for param_id in ¶ms_node.children { - let param_def_node = ast.nodes.get_node(*param_id)?; - match param_def_node.rule { - Type::ProcParamDef => { - let type_id = *param_def_node.children.first().expect( - "ProcParamDef node must contain exactly one child (type node).", - ); - assert!(param_def_node.children.get(1).is_none()); - let type_node = ast.nodes.get_node(type_id)?; - let data_type = match type_node.rule { - Type::TypeBool => RelationType::Boolean, - Type::TypeDecimal => RelationType::Decimal, - Type::TypeDouble => RelationType::Double, - Type::TypeInt => RelationType::Integer, - Type::TypeNumber => RelationType::Number, - Type::TypeScalar => RelationType::Scalar, - Type::TypeString | Type::TypeText | Type::TypeVarchar => { - RelationType::String - } - Type::TypeUnsigned => RelationType::Unsigned, - _ => panic!("Unexpected node: {type_node:?}"), - }; - params.push(ParamDef { data_type }); - } - _ => panic!("Unexpected node: {param_def_node:?}"), - } - } + params = parse_proc_params(ast, child_node)?; } Type::ProcLanguage => { // We don't need to parse language node, because we support only SQL. @@ -192,6 +205,65 @@ fn parse_create_proc(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, Ok(create_proc) } +fn parse_proc_with_optional_params( + ast: &AbstractSyntaxTree, + node: &ParseNode, +) -> Result<(String, Vec<ParamDef>), SbroadError> { + if node.rule != Type::ProcWithOptionalParams { + return Err(SbroadError::Invalid( + Entity::Type, + Some("proc with optional params".into()), + )); + } + + let mut name = String::new(); + let mut params = Vec::new(); + for child_id in &node.children { + let child_node = ast.nodes.get_node(*child_id)?; + match child_node.rule { + Type::ProcName => { + name = normalize_name_for_space_api(parse_string_value_node(ast, *child_id)?); + } + Type::ProcParams => { + params = parse_proc_params(ast, child_node)?; + } + _ => panic!("Unexpected node: {child_node:?}"), + } + } + + Ok((name, params)) +} + +fn parse_drop_proc(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, SbroadError> { + if node.rule != Type::DropProc { + return Err(SbroadError::Invalid( + Entity::Type, + Some("drop procedure".into()), + )); + } + + let mut name: String = String::new(); + let mut params: Vec<ParamDef> = Vec::new(); + let mut timeout = get_default_timeout(); + for child_id in &node.children { + let child_node = ast.nodes.get_node(*child_id)?; + match child_node.rule { + Type::ProcWithOptionalParams => { + (name, params) = parse_proc_with_optional_params(ast, child_node)?; + } + Type::Timeout => { + timeout = get_timeout(ast, *child_id)?; + } + _ => panic!("Unexpected node: {child_node:?}"), + } + } + Ok(Ddl::DropProc { + name, + params, + timeout, + }) +} + #[allow(clippy::too_many_lines)] #[allow(clippy::uninlined_format_args)] fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, SbroadError> { @@ -2152,6 +2224,11 @@ impl Ast for AbstractSyntaxTree { let plan_id = plan.nodes.push(Node::Ddl(drop_table)); map.add(id, plan_id); } + Type::DropProc => { + let drop_proc = parse_drop_proc(self, node)?; + let plan_id = plan.nodes.push(Node::Ddl(drop_proc)); + map.add(id, plan_id); + } Type::AlterUser => { let user_name_node_id = node .children @@ -2373,6 +2450,8 @@ impl Ast for AbstractSyntaxTree { | Type::ProcParamDef | Type::ProcParams | Type::ProcLanguage + | Type::ProcName + | Type::ProcWithOptionalParams | Type::ScanName | Type::Select | Type::Sharding diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs index bc29122de5..8d108a8b53 100644 --- a/sbroad-core/src/frontend/sql/ast.rs +++ b/sbroad-core/src/frontend/sql/ast.rs @@ -65,6 +65,7 @@ pub enum Type { Distribution, Divide, Double, + DropProc, DropRole, DropTable, DropUser, @@ -137,8 +138,10 @@ pub enum Type { PrivilegeWrite, ProcBody, ProcLanguage, + ProcName, ProcParams, ProcParamDef, + ProcWithOptionalParams, Projection, Reference, RevokePrivilege, @@ -226,6 +229,7 @@ impl Type { Rule::DeletedTable => Ok(Type::DeletedTable), Rule::Divide => Ok(Type::Divide), Rule::Double => Ok(Type::Double), + Rule::DropProc => Ok(Type::DropProc), Rule::DropRole => Ok(Type::DropRole), Rule::DropTable => Ok(Type::DropTable), Rule::DropUser => Ok(Type::DropUser), @@ -300,8 +304,10 @@ impl Type { Rule::PrivilegeWrite => Ok(Type::PrivilegeWrite), Rule::ProcBody => Ok(Type::ProcBody), Rule::ProcLanguage => Ok(Type::ProcLanguage), + Rule::ProcName => Ok(Type::ProcName), Rule::ProcParams => Ok(Type::ProcParams), Rule::ProcParamDef => Ok(Type::ProcParamDef), + Rule::ProcWithOptionalParams => Ok(Type::ProcWithOptionalParams), Rule::Projection => Ok(Type::Projection), Rule::Reference => Ok(Type::Reference), Rule::RevokePrivilege => Ok(Type::RevokePrivilege), diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index 955cfbff72..be7a257cdd 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -47,7 +47,7 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil PrivilegeUsage = { ^"usage" } PrivilegeWrite = { ^"write" } -DDL = _{ CreateTable | DropTable | CreateProc } +DDL = _{ CreateTable | DropTable | CreateProc | DropProc } CreateTable = { ^"create" ~ ^"table" ~ NewTable ~ "(" ~ Columns ~ "," ~ PrimaryKey ~ ")" ~ @@ -89,6 +89,10 @@ DDL = _{ CreateTable | DropTable | CreateProc } SQL = { ^"sql" } ProcBody = { (Insert | Update | Delete) } + DropProc = { ^"drop" ~ ^"procedure" ~ ProcWithOptionalParams ~ Option? } + ProcWithOptionalParams = { ProcName ~ ("(" ~ ProcParams ~ ")")? } + ProcName = @{ Name } + ExplainQuery = _{ Explain } Explain = { ^"explain" ~ Query } diff --git a/sbroad-core/src/ir/ddl.rs b/sbroad-core/src/ir/ddl.rs index a41ef4ce45..18081fd16f 100644 --- a/sbroad-core/src/ir/ddl.rs +++ b/sbroad-core/src/ir/ddl.rs @@ -57,6 +57,11 @@ pub enum Ddl { language: Language, timeout: Decimal, }, + DropProc { + name: String, + params: Vec<ParamDef>, + timeout: Decimal, + }, } impl Ddl { @@ -68,7 +73,8 @@ impl Ddl { match self { Ddl::CreateTable { ref timeout, .. } | Ddl::DropTable { ref timeout, .. } - | Ddl::CreateProc { ref timeout, .. } => timeout, + | Ddl::CreateProc { ref timeout, .. } + | Ddl::DropProc { ref timeout, .. } => timeout, } .to_string() .parse() -- GitLab