From 79d1d796d0d94e688c4c2c04b2d89c4a285e06f8 Mon Sep 17 00:00:00 2001 From: Kaitmazian Maksim <m.kaitmazian@picodata.io> Date: Thu, 12 Sep 2024 13:00:35 +0300 Subject: [PATCH] feat: introduce IF EXISTS and IF NOT EXISTS options --- doc/sql/query.ebnf | 20 +- sbroad-core/src/frontend/sql.rs | 287 +++++++++++++++--------- sbroad-core/src/frontend/sql/query.pest | 25 ++- sbroad-core/src/ir.rs | 42 ++-- sbroad-core/src/ir/node.rs | 40 ++-- sbroad-core/src/ir/node/plugin.rs | 10 +- sbroad-core/src/ir/node/tests.rs | 4 +- sbroad-core/src/ir/operator.rs | 2 +- 8 files changed, 253 insertions(+), 177 deletions(-) diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index 55b6f08019..771825a30c 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -129,7 +129,7 @@ alter_system ::= 'ALTER' 'SYSTEM' ('OPTION' '(' ('TIMEOUT' '=' double)')')? alter_procedure ::= 'ALTER' 'PROCEDURE' procedure ('(' type (',' type)* ')')? 'RENAME' 'TO' procedure ('OPTION' '(' ('TIMEOUT' '=' double)')')? -create_index ::= 'CREATE' 'UNIQUE'? 'INDEX' index 'ON' table +create_index ::= 'CREATE' 'UNIQUE'? 'INDEX' ('IF' 'NOT' 'EXISTS')? index 'ON' table ('USING' ('TREE' | 'HASH' | 'RTREE' | 'BITSET'))? '(' column (',' column)* ')' ('WITH' '(' ( @@ -156,13 +156,13 @@ create_index ::= 'CREATE' 'UNIQUE'? 'INDEX' index 'ON' table ) )* ')')? ('OPTION' '(' ('TIMEOUT' '=' double)')')? -create_procedure ::= 'CREATE' 'PROCEDURE' procedure '(' type (',' type)* ')' +create_procedure ::= 'CREATE' 'PROCEDURE' ('IF' 'NOT' 'EXISTS')? procedure '(' type (',' type)* ')' ('LANGUAGE' 'SQL')? ( ('AS' '$$' (insert | update | delete) '$$') | ('BEGIN' 'ATOMIC' (insert | update | delete) 'END') ) ('OPTION' '(' ('TIMEOUT' '=' double)')')? -create_role ::= 'CREATE' 'ROLE' role -create_table ::= 'CREATE' 'TABLE' table +create_role ::= 'CREATE' 'ROLE' ('IF' 'NOT' 'EXISTS')? role +create_table ::= 'CREATE' 'TABLE' ('IF' 'NOT' 'EXISTS')? table '(' column type ('NOT'? 'NULL')? ('PRIMARY' 'KEY')? (',' column type ('NOT'? 'NULL')? ('PRIMARY' 'KEY')?)* (',' 'PRIMARY' 'KEY' '(' column (',' column)* ')')? @@ -170,7 +170,7 @@ create_table ::= 'CREATE' 'TABLE' table ('USING' ('MEMTX' | 'VINYL'))? (('DISTRIBUTED' (('BY' '(' column (',' column)* ')' ('IN' 'TIER' tier)?) | 'GLOBALLY'))?)? ('OPTION' '(' ('TIMEOUT' '=' double)')')? -create_user ::= 'CREATE' 'USER' user 'WITH'? 'PASSWORD' "'" password "'" +create_user ::= 'CREATE' 'USER' ('IF' 'NOT' 'EXISTS')? user 'WITH'? 'PASSWORD' "'" password "'" ('USING' ('CHAP-SHA1' | 'LDAP' | 'MD5'))? alter_user ::= 'ALTER' 'USER' user 'WITH'? ( @@ -179,11 +179,11 @@ alter_user ::= 'ALTER' 'USER' user | 'PASSWORD' "'" password "'" ('USING' ('CHAP-SHA1' | 'LDAP' | 'MD5'))? | 'RENAME' 'TO' user ) -drop_index ::= 'DROP' 'INDEX' index ('OPTION' '(' ('TIMEOUT' '=' double)')')? -drop_procedure ::= 'DROP' 'PROCEDURE' procedure ('(' type (',' type)* ')')? ('OPTION' '(' ('TIMEOUT' '=' double)')')? -drop_table ::= 'DROP' 'TABLE' table ('OPTION' '(' ('TIMEOUT' '=' double)')')? -drop_role ::= 'DROP' 'ROLE' role -drop_user ::= 'DROP' 'USER' user +drop_index ::= 'DROP' 'INDEX' ('IF' 'EXISTS')? index ('OPTION' '(' ('TIMEOUT' '=' double)')')? +drop_procedure ::= 'DROP' 'PROCEDURE' ('IF' 'EXISTS')? procedure ('(' type (',' type)* ')')? ('OPTION' '(' ('TIMEOUT' '=' double)')')? +drop_table ::= 'DROP' 'TABLE' ('IF' 'EXISTS')? table ('OPTION' '(' ('TIMEOUT' '=' double)')')? +drop_role ::= 'DROP' 'ROLE' ('IF' 'EXISTS')? role +drop_user ::= 'DROP' 'USER' ('IF' 'EXISTS')? user create_plugin ::= 'CREATE' 'PLUGIN' ('IF' 'NOT' 'EXISTS')? plugin version ('OPTION' '(' ('TIMEOUT' '=' double)')')? drop_plugin ::= 'DROP' 'PLUGIN' ('IF' 'EXISTS')? plugin version ('WITH' 'DATA')? diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index 15d6ccd9ee..051a0ad3e5 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -72,6 +72,9 @@ use tarantool::space::SpaceEngineType; const DEFAULT_TIMEOUT_F64: f64 = 24.0 * 60.0 * 60.0; const DEFAULT_AUTH_METHOD: &str = "md5"; +const DEFAULT_IF_EXISTS: bool = false; +const DEFAULT_IF_NOT_EXISTS: bool = false; + fn get_default_timeout() -> Decimal { Decimal::from_str(&format!("{DEFAULT_TIMEOUT_F64}")).expect("default timeout casting failed") } @@ -259,19 +262,20 @@ fn parse_create_proc( ast: &AbstractSyntaxTree, node: &ParseNode, ) -> Result<CreateProc, SbroadError> { - let proc_name_id = node.children.first().expect("Expected to get Proc name"); - let proc_name = parse_identifier(ast, *proc_name_id)?; - - let proc_params_id = node.children.get(1).expect("Expedcted to get Proc params"); - let proc_params = ast.nodes.get_node(*proc_params_id)?; - let params = parse_proc_params(ast, proc_params)?; - + let mut name = None; + let mut params = None; let language = Language::SQL; let mut body = SmolStr::default(); + let mut if_not_exists = DEFAULT_IF_NOT_EXISTS; let mut timeout = get_default_timeout(); - for child_id in node.children.iter().skip(2) { + for child_id in &node.children { let child_node = ast.nodes.get_node(*child_id)?; match child_node.rule { + Rule::Identifier => name = Some(parse_identifier(ast, *child_id)?), + Rule::ProcParams => { + let proc_params = ast.nodes.get_node(*child_id)?; + params = Some(parse_proc_params(ast, proc_params)?); + } Rule::ProcLanguage => { // We don't need to parse language node, because we support only SQL. } @@ -282,17 +286,21 @@ fn parse_create_proc( .expect("procedure body must not be empty") .clone(); } + Rule::IfNotExists => if_not_exists = true, Rule::Timeout => { timeout = get_timeout(ast, *child_id)?; } _ => unreachable!("Unexpected node: {child_node:?}"), } } + let name = name.expect("name expected as a child"); + let params = params.expect("params expected as a child"); let create_proc = CreateProc { - name: proc_name, + name, params, language, body, + if_not_exists, timeout, }; Ok(create_proc) @@ -405,22 +413,29 @@ fn parse_proc_with_optional_params( } fn parse_drop_proc(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<DropProc, SbroadError> { - let proc_with_optional_params_id = node - .children - .first() - .expect("Expected to see ProcWithOptionalParams"); - let proc_with_optional_params = ast.nodes.get_node(*proc_with_optional_params_id)?; - let (name, params) = parse_proc_with_optional_params(ast, proc_with_optional_params)?; - - let timeout = if let Some(timeout_id) = node.children.get(1) { - get_timeout(ast, *timeout_id)? - } else { - get_default_timeout() - }; + let mut name = None; + let mut params = None; + let mut timeout = get_default_timeout(); + let mut if_exists = DEFAULT_IF_EXISTS; + for child_id in &node.children { + let child_node = ast.nodes.get_node(*child_id)?; + match child_node.rule { + Rule::ProcWithOptionalParams => { + let (proc_name, proc_params) = parse_proc_with_optional_params(ast, child_node)?; + name = Some(proc_name); + params = proc_params; + } + Rule::TimeoutOption => timeout = get_timeout(ast, *child_id)?, + Rule::IfExists => if_exists = true, + child_node => panic!("unexpected node {child_node:?}"), + } + } + let name = name.expect("drop proc wihtout proc name"); Ok(DropProc { name, params, + if_exists, timeout, }) } @@ -444,6 +459,7 @@ fn parse_create_index( let mut dimension = None; let mut distance = None; let mut hint = None; + let mut if_not_exists = DEFAULT_IF_NOT_EXISTS; let mut timeout = get_default_timeout(); let first_child = |node: &ParseNode| -> &ParseNode { @@ -483,6 +499,7 @@ fn parse_create_index( let child_node = ast.nodes.get_node(*child_id)?; match child_node.rule { Rule::Unique => unique = true, + Rule::IfNotExists => if_not_exists = true, Rule::Identifier => name = parse_identifier(ast, *child_id)?, Rule::Table => table_name = parse_identifier(ast, *child_id)?, Rule::IndexType => { @@ -550,6 +567,7 @@ fn parse_create_index( table_name, columns, unique, + if_not_exists, index_type, bloom_fpr, page_size, @@ -568,15 +586,21 @@ fn parse_drop_index(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<DropIn assert_eq!(node.rule, Rule::DropIndex); let mut name = SmolStr::default(); let mut timeout = get_default_timeout(); + let mut if_exists = DEFAULT_IF_EXISTS; for child_id in &node.children { let child_node = ast.nodes.get_node(*child_id)?; match child_node.rule { Rule::Identifier => name = parse_identifier(ast, *child_id)?, Rule::Timeout => timeout = get_timeout(ast, *child_id)?, + Rule::IfExists => if_exists = true, _ => panic!("Unexpected drop index node: {child_node:?}"), } } - Ok(DropIndex { name, timeout }) + Ok(DropIndex { + name, + if_exists, + timeout, + }) } #[allow(clippy::too_many_lines)] @@ -600,6 +624,7 @@ fn parse_create_table( let mut timeout = get_default_timeout(); let mut tier = None; let mut is_global = false; + let mut if_not_exists = DEFAULT_IF_NOT_EXISTS; let nullable_primary_key_column_error = Err(SbroadError::Invalid( Entity::Column, @@ -615,6 +640,7 @@ fn parse_create_table( for child_id in &node.children { let child_node = ast.nodes.get_node(*child_id)?; match child_node.rule { + Rule::IfNotExists => if_not_exists = true, Rule::NewTable => { table_name = parse_identifier(ast, *child_id)?; } @@ -858,34 +884,63 @@ fn parse_create_table( if shard_key.is_empty() && !is_global { shard_key = pk_keys.clone(); } - let create_sharded_table = if shard_key.is_empty() { + + let sharding_key = if !shard_key.is_empty() { + Some(shard_key) + } else { if engine_type != SpaceEngineType::Memtx { return Err(SbroadError::Unsupported( Entity::Query, Some("global spaces can use only memtx engine".into()), )); - } - CreateTable { - name: table_name, - format: columns, - primary_key: pk_keys, - sharding_key: None, - engine_type, - timeout, - tier, - } - } else { - CreateTable { - name: table_name, - format: columns, - primary_key: pk_keys, - sharding_key: Some(shard_key), - engine_type, - timeout, - tier, - } + }; + None }; - Ok(create_sharded_table) + + Ok(CreateTable { + name: table_name, + format: columns, + primary_key: pk_keys, + sharding_key, + engine_type, + if_not_exists, + timeout, + tier, + }) +} + +fn parse_drop_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<DropTable, SbroadError> { + let mut table_name = SmolStr::default(); + let mut timeout = get_default_timeout(); + let mut if_exists = DEFAULT_IF_EXISTS; + for child_id in &node.children { + let child_node = ast.nodes.get_node(*child_id)?; + match child_node.rule { + Rule::Table => { + table_name = parse_identifier(ast, *child_id)?; + } + Rule::IfExists => { + if_exists = true; + } + Rule::Timeout => { + timeout = get_timeout(ast, *child_id)?; + } + _ => { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format_smolstr!( + "AST drop table node {:?} contains unexpected children", + child_node, + )), + )); + } + } + } + Ok(DropTable { + name: table_name, + if_exists, + timeout, + }) } fn parse_set_param(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<SetParam, SbroadError> { @@ -3567,6 +3622,10 @@ impl AbstractSyntaxTree { let mut col_idx: usize = 0; let mut worker = ExpressionsWorker::new(metadata, sq_pair_to_ast_ids); let mut ctes = CTEs::new(); + // This flag disables resolving of table names for DROP TABLE queries, + // as it can be used with tables that are not presented in metadata. + // Unresolved table names are handled in picodata depending in IF EXISTS options. + let resolve_table_names = self.nodes.get_node(top)?.rule != Rule::DropTable; for level_node in dft_post.iter(top) { let id = level_node.1; @@ -3611,7 +3670,7 @@ impl AbstractSyntaxTree { Rule::Cte => { parse_cte(self, id, &mut map, &mut ctes, &mut plan)?; } - Rule::Table => { + Rule::Table if resolve_table_names => { // The thing is we don't want to normalize name. // Should we fix `parse_identifier` or `table` logic? let table_name = parse_identifier(self, id)?; @@ -4194,50 +4253,37 @@ impl AbstractSyntaxTree { map.add(id, plan_id); } Rule::DropRole => { - let role_name_id = node - .children - .first() - .expect("RoleName expected under DropRole node"); - let role_name = parse_identifier(self, *role_name_id)?; - - let mut timeout = get_default_timeout(); - if let Some(timeout_child_id) = node.children.get(1) { - timeout = get_timeout(self, *timeout_child_id)?; - } - let drop_role = DropRole { - name: role_name, - timeout, - }; - let plan_id = plan.nodes.push(drop_role.into()); - map.add(id, plan_id); - } - Rule::DropTable => { - let mut table_name = SmolStr::default(); + let mut name = None; let mut timeout = get_default_timeout(); + let mut if_exists = DEFAULT_IF_EXISTS; for child_id in &node.children { let child_node = self.nodes.get_node(*child_id)?; match child_node.rule { - Rule::Table => { - table_name = parse_identifier(self, *child_id)?; - } - Rule::Timeout => { - timeout = get_timeout(self, *child_id)?; - } + Rule::Identifier => name = Some(parse_identifier(self, *child_id)?), + Rule::IfExists => if_exists = true, + Rule::Timeout => timeout = get_timeout(self, *child_id)?, _ => { return Err(SbroadError::Invalid( Entity::Node, Some(format_smolstr!( - "AST drop table node {:?} contains unexpected children", - child_node, + "ACL node contains unexpected child: {child_node:?}", )), )); } } } - let drop_table = DropTable { - name: table_name, + let name = name.expect("RoleName expected under DropRole node"); + let drop_role = DropRole { + name, + if_exists, timeout, }; + + let plan_id = plan.nodes.push(drop_role.into()); + map.add(id, plan_id); + } + Rule::DropTable => { + let drop_table = parse_drop_table(self, node)?; let plan_id = plan.nodes.push(drop_table.into()); map.add(id, plan_id); } @@ -4323,28 +4369,19 @@ impl AbstractSyntaxTree { map.add(id, plan_id); } Rule::CreateUser => { - let mut iter = node.children.iter(); - let user_name_node_id = iter.next().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(SmolStr::from("RoleName expected as a first child")), - ) - })?; - let user_name = parse_identifier(self, *user_name_node_id)?; - - let pwd_node_id = iter.next().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(SmolStr::from("Password expected as a second child")), - ) - })?; - let password = parse_string_literal(self, *pwd_node_id)?; - + let mut name = None; + let mut password = None; + let mut if_not_exists = DEFAULT_IF_NOT_EXISTS; let mut timeout = get_default_timeout(); let mut auth_method = get_default_auth_method(); - for child_id in iter { + for child_id in &node.children { let child_node = self.nodes.get_node(*child_id)?; match child_node.rule { + Rule::Identifier => name = Some(parse_identifier(self, *child_id)?), + Rule::SingleQuotedString => { + password = Some(parse_string_literal(self, *child_id)?); + } + Rule::IfNotExists => if_not_exists = true, Rule::Timeout => { timeout = get_timeout(self, *child_id)?; } @@ -4369,48 +4406,76 @@ impl AbstractSyntaxTree { } } + let name = name.expect("username expected as a child"); + let password = password.expect("password expected as a child"); let create_user = CreateUser { - name: user_name, + name, password, + if_not_exists, auth_method, timeout, }; + let plan_id = plan.nodes.push(create_user.into()); map.add(id, plan_id); } Rule::DropUser => { - let user_name_id = node - .children - .first() - .expect("RoleName expected under DropUser node"); - let user_name = parse_identifier(self, *user_name_id)?; - + let mut name = None; let mut timeout = get_default_timeout(); - if let Some(timeout_child_id) = node.children.get(1) { - timeout = get_timeout(self, *timeout_child_id)?; + let mut if_exists = DEFAULT_IF_EXISTS; + for child_id in &node.children { + let child_node = self.nodes.get_node(*child_id)?; + match child_node.rule { + Rule::Identifier => name = Some(parse_identifier(self, *child_id)?), + Rule::IfExists => if_exists = true, + Rule::Timeout => timeout = get_timeout(self, *child_id)?, + _ => { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format_smolstr!( + "ACL node contains unexpected child: {child_node:?}", + )), + )); + } + } } + let name = name.expect("RoleName expected under DropUser node"); let drop_user = DropUser { - name: user_name, + name, + if_exists, timeout, }; let plan_id = plan.nodes.push(drop_user.into()); map.add(id, plan_id); } Rule::CreateRole => { - let role_name_id = node - .children - .first() - .expect("RoleName expected under CreateRole node"); - let role_name = parse_identifier(self, *role_name_id)?; - + let mut name = None; + let mut if_not_exists = DEFAULT_IF_NOT_EXISTS; let mut timeout = get_default_timeout(); - if let Some(timeout_child_id) = node.children.get(1) { - timeout = get_timeout(self, *timeout_child_id)?; + for child_id in &node.children { + let child_node = self.nodes.get_node(*child_id)?; + match child_node.rule { + Rule::Identifier => name = Some(parse_identifier(self, *child_id)?), + Rule::IfNotExists => if_not_exists = true, + Rule::Timeout => timeout = get_timeout(self, *child_id)?, + _ => { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format_smolstr!( + "ACL node contains unexpected child: {child_node:?}", + )), + )); + } + } } + + let name = name.expect("RoleName expected under CreateRole node"); let create_role = CreateRole { - name: role_name, + name, + if_not_exists, timeout, }; + let plan_id = plan.nodes.push(create_role.into()); map.add(id, plan_id); } diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index 5d69964fe6..d4883bdd68 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -8,12 +8,13 @@ Table = @{ Identifier } ScanTable = { Table } ScanCteOrTable = @{ Table } +IfExists = { ^"if" ~ ^"exists" } +IfNotExists = { ^"if" ~ ^"not" ~ ^"exists" } + Plugin = _{ CreatePlugin | DropPlugin | AlterPlugin } CreatePlugin = { ^"create" ~ ^"plugin" ~ IfNotExists? ~ Identifier ~ PluginVersion ~ TimeoutOption? } PluginVersion = @{ Unsigned ~ "." ~ Unsigned ~ "." ~ Unsigned } - IfNotExists = { ^"if" ~ ^"not" ~ ^"exists" } DropPlugin = { ^"drop" ~ ^"plugin" ~ IfExists? ~ Identifier ~ PluginVersion ~ WithData? ~ TimeoutOption? } - IfExists = { ^"if" ~ ^"exists" } WithData = { ^"with" ~ ^"data" } AlterPlugin = { ^"alter" ~ ^"plugin" ~ Identifier ~ AlterVariant } AlterVariant = _{ EnablePlugin | DisablePlugin | MigrateTo | AddServiceToTier | RemoveServiceFromTier | ChangeConfig } @@ -30,7 +31,7 @@ Plugin = _{ CreatePlugin | DropPlugin | AlterPlugin } ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivilege | RevokePrivilege } CreateUser = { - ^"create" ~ ^"user" ~ Identifier ~ (^"with")? ~ ^"password" ~ SingleQuotedString ~ + ^"create" ~ ^"user" ~ IfNotExists? ~ Identifier ~ (^"with")? ~ ^"password" ~ SingleQuotedString ~ AuthMethod? ~ TimeoutOption? } AlterUser = { @@ -45,9 +46,9 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil ChapSha1 = { ^"chap-sha1" } Md5 = { ^"md5" } Ldap = { ^"ldap" } - DropUser = { ^"drop" ~ ^"user" ~ Identifier ~ TimeoutOption? } - CreateRole = { ^"create" ~ ^"role" ~ Identifier ~ TimeoutOption? } - DropRole = { ^"drop" ~ ^"role" ~ Identifier ~ TimeoutOption? } + DropUser = { ^"drop" ~ ^"user" ~ IfExists? ~ Identifier ~ TimeoutOption? } + CreateRole = { ^"create" ~ ^"role" ~ IfNotExists? ~ Identifier ~ TimeoutOption? } + DropRole = { ^"drop" ~ ^"role" ~ IfExists? ~ Identifier ~ TimeoutOption? } GrantPrivilege = { ^"grant" ~ PrivBlock ~ ^"to" ~ Identifier ~ TimeoutOption? } RevokePrivilege = { ^"revoke" ~ PrivBlock ~ ^"from" ~ Identifier ~ TimeoutOption? } PrivBlock = _{ PrivBlockPrivilege | PrivBlockRolePass } @@ -78,7 +79,7 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex | CreateProc | DropProc | RenameProc | SetParam | SetTransaction | AlterSystem } CreateTable = { - ^"create" ~ ^"table" ~ NewTable ~ + ^"create" ~ ^"table" ~ IfNotExists? ~ NewTable ~ "(" ~ Columns ~ ("," ~ PrimaryKey)? ~ ")" ~ Engine? ~ Distribution? ~ TimeoutOption? } @@ -95,10 +96,10 @@ DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex Global = { ^"globally" } Sharding = { ^"by" ~ "(" ~ Identifier ~ ("," ~ Identifier)* ~ ")" ~ Tier? } Tier = { ^"in" ~ ^"tier" ~ Identifier } - DropTable = { ^"drop" ~ ^"table" ~ Table ~ TimeoutOption? } + DropTable = { ^"drop" ~ ^"table" ~ IfExists? ~ Table ~ TimeoutOption? } CreateProc = { - ^"create" ~ ^"procedure" ~ Identifier + ^"create" ~ ^"procedure" ~ IfNotExists? ~ Identifier ~ ProcParams ~ (^"language" ~ ProcLanguage)? ~ ((^"as" ~ "$$" ~ ProcBody ~ "$$") | (^"begin" ~ "atomic" ~ ProcBody ~ "end")) ~ TimeoutOption? @@ -108,7 +109,7 @@ DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex SQL = { ^"sql" } ProcBody = { (Insert | Update | Delete) } - DropProc = { ^"drop" ~ ^"procedure" ~ ProcWithOptionalParams ~ TimeoutOption? } + DropProc = { ^"drop" ~ ^"procedure" ~ IfExists? ~ ProcWithOptionalParams ~ TimeoutOption? } ProcWithOptionalParams = { Identifier ~ ProcParams? } RenameProc = { ^"alter" ~ ^"procedure" ~ OldProc ~ ProcParams? ~ ^"rename" ~ ^"to" ~ NewProc ~ TimeoutOption? } @@ -116,7 +117,7 @@ DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex NewProc = @{ Identifier } CreateIndex = { - ^"create" ~ Unique? ~ ^"index" ~ Identifier ~ ^"on" ~ Table + ^"create" ~ Unique? ~ ^"index" ~ IfNotExists? ~ Identifier ~ ^"on" ~ Table ~ IndexType? ~ "(" ~ Parts ~ ")" ~ IndexOptions? ~ TimeoutOption? } Unique = { ^"unique" } @@ -140,7 +141,7 @@ DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex Manhattan = { ^"manhattan" } Hint = { ^"hint" ~ "=" ~ (True | False) } - DropIndex = { ^"drop" ~ ^"index" ~ Identifier ~ TimeoutOption? } + DropIndex = { ^"drop" ~ ^"index" ~ IfExists? ~ Identifier ~ TimeoutOption? } SetParam = { ^"set" ~ SetScope? ~ ConfParam } SetScope = { ScopeSession | ScopeLocal } diff --git a/sbroad-core/src/ir.rs b/sbroad-core/src/ir.rs index ea7e9454f7..f03c9fa718 100644 --- a/sbroad-core/src/ir.rs +++ b/sbroad-core/src/ir.rs @@ -33,7 +33,7 @@ use crate::ir::helpers::RepeatableState; use crate::ir::node::plugin::{MutPlugin, Plugin}; use crate::ir::node::{ Alias, ArenaType, ArithmeticExpr, BoolExpr, Case, Cast, Concat, Constant, ExprInParentheses, - GroupBy, Having, Insert, Limit, Motion, MutNode, Node, Node136, Node224, Node32, Node64, + GroupBy, Having, Insert, Limit, Motion, MutNode, Node, Node136, Node232, Node32, Node64, Node96, NodeId, NodeOwned, OrderBy, Projection, Reference, Row, ScanRelation, Selection, StableFunction, Trim, UnaryExpr, Values, }; @@ -79,7 +79,7 @@ pub struct Nodes { arena64: Vec<Node64>, arena96: Vec<Node96>, arena136: Vec<Node136>, - arena224: Vec<Node224>, + arena224: Vec<Node232>, } impl Nodes { @@ -179,17 +179,17 @@ impl Nodes { Node::Plugin(Plugin::ChangeConfig(change_config)) } }), - ArenaType::Arena224 => self + ArenaType::Arena232 => self .arena224 .get(id.offset as usize) .map(|node| match node { - Node224::CreateIndex(create_index) => Node::Ddl(Ddl::CreateIndex(create_index)), - Node224::CreateTable(create_table) => Node::Ddl(Ddl::CreateTable(create_table)), - Node224::Invalid(inv) => Node::Invalid(inv), - Node224::AppendServiceToTier(append) => { + Node232::CreateIndex(create_index) => Node::Ddl(Ddl::CreateIndex(create_index)), + Node232::CreateTable(create_table) => Node::Ddl(Ddl::CreateTable(create_table)), + Node232::Invalid(inv) => Node::Invalid(inv), + Node232::AppendServiceToTier(append) => { Node::Plugin(Plugin::AppendServiceToTier(append)) } - Node224::RemoveServiceFromTier(remove) => { + Node232::RemoveServiceFromTier(remove) => { Node::Plugin(Plugin::RemoveServiceFromTier(remove)) } }), @@ -341,21 +341,21 @@ impl Nodes { } }) } - ArenaType::Arena224 => { + ArenaType::Arena232 => { self.arena224 .get_mut(id.offset as usize) .map(|node| match node { - Node224::CreateIndex(create_index) => { + Node232::CreateIndex(create_index) => { MutNode::Ddl(MutDdl::CreateIndex(create_index)) } - Node224::Invalid(inv) => MutNode::Invalid(inv), - Node224::CreateTable(create_table) => { + Node232::Invalid(inv) => MutNode::Invalid(inv), + Node232::CreateTable(create_table) => { MutNode::Ddl(MutDdl::CreateTable(create_table)) } - Node224::AppendServiceToTier(append) => { + Node232::AppendServiceToTier(append) => { MutNode::Plugin(MutPlugin::AppendServiceToTier(append)) } - Node224::RemoveServiceFromTier(remove) => { + Node232::RemoveServiceFromTier(remove) => { MutNode::Plugin(MutPlugin::RemoveServiceFromTier(remove)) } }) @@ -384,9 +384,9 @@ impl Nodes { offset: u32::try_from(self.arena136.len()).unwrap(), arena_type: ArenaType::Arena136, }, - ArenaType::Arena224 => NodeId { + ArenaType::Arena232 => NodeId { offset: u32::try_from(self.arena224.len()).unwrap(), - arena_type: ArenaType::Arena224, + arena_type: ArenaType::Arena232, }, } } @@ -425,7 +425,7 @@ impl Nodes { self.arena136.iter() } - pub fn iter224(&self) -> Iter<'_, Node224> { + pub fn iter224(&self) -> Iter<'_, Node232> { self.arena224.iter() } @@ -477,10 +477,10 @@ impl Nodes { new_node_id } - NodeAligned::Node224(node224) => { + NodeAligned::Node232(node224) => { let new_node_id = NodeId { offset: u32::try_from(self.arena224.len()).unwrap(), - arena_type: ArenaType::Arena224, + arena_type: ArenaType::Arena232, }; self.arena224.push(node224); @@ -881,13 +881,13 @@ impl Plan { let node136 = std::mem::replace(node136, stub); node136.into_owned() } - ArenaType::Arena224 => { + ArenaType::Arena232 => { let node224 = self .nodes .arena224 .get_mut(usize::try_from(dst_id.offset).unwrap()) .unwrap(); - let stub = Node224::Invalid(Invalid {}); + let stub = Node232::Invalid(Invalid {}); let node224 = std::mem::replace(node224, stub); node224.into_owned() } diff --git a/sbroad-core/src/ir/node.rs b/sbroad-core/src/ir/node.rs index 9c4f998236..dbd7cb7cf1 100644 --- a/sbroad-core/src/ir/node.rs +++ b/sbroad-core/src/ir/node.rs @@ -45,7 +45,7 @@ pub enum ArenaType { Arena64, Arena96, Arena136, - Arena224, + Arena232, } impl Display for ArenaType { @@ -63,7 +63,7 @@ impl Display for ArenaType { ArenaType::Arena136 => { write!(f, "136") } - ArenaType::Arena224 => { + ArenaType::Arena232 => { write!(f, "224") } } @@ -743,6 +743,7 @@ impl From<ValuesRow> for NodeAligned { #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct DropRole { pub name: SmolStr, + pub if_exists: bool, pub timeout: Decimal, } @@ -755,6 +756,7 @@ impl From<DropRole> for NodeAligned { #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct DropUser { pub name: SmolStr, + pub if_exists: bool, pub timeout: Decimal, } @@ -767,6 +769,7 @@ impl From<DropUser> for NodeAligned { #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct CreateRole { pub name: SmolStr, + pub if_not_exists: bool, pub timeout: Decimal, } @@ -780,6 +783,7 @@ impl From<CreateRole> for NodeAligned { pub struct CreateUser { pub name: SmolStr, pub password: SmolStr, + pub if_not_exists: bool, pub auth_method: SmolStr, pub timeout: Decimal, } @@ -838,6 +842,7 @@ pub struct CreateTable { pub sharding_key: Option<Vec<SmolStr>>, /// Vinyl is supported only for sharded tables. pub engine_type: SpaceEngineType, + pub if_not_exists: bool, pub timeout: Decimal, /// Shows which tier the sharded table belongs to. /// Field has value, only if it was specified in [ON TIER] part of CREATE TABLE statement. @@ -849,13 +854,14 @@ pub struct CreateTable { impl From<CreateTable> for NodeAligned { fn from(value: CreateTable) -> Self { - Self::Node224(Node224::CreateTable(value)) + Self::Node232(Node232::CreateTable(value)) } } #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct DropTable { pub name: SmolStr, + pub if_exists: bool, pub timeout: Decimal, } @@ -870,6 +876,7 @@ pub struct CreateProc { pub name: SmolStr, pub params: Vec<ParamDef>, pub body: SmolStr, + pub if_not_exists: bool, pub language: Language, pub timeout: Decimal, } @@ -884,6 +891,7 @@ impl From<CreateProc> for NodeAligned { pub struct DropProc { pub name: SmolStr, pub params: Option<Vec<ParamDef>>, + pub if_exists: bool, pub timeout: Decimal, } @@ -913,6 +921,7 @@ pub struct CreateIndex { pub table_name: SmolStr, pub columns: Vec<SmolStr>, pub unique: bool, + pub if_not_exists: bool, pub index_type: IndexType, pub bloom_fpr: Option<Decimal>, pub page_size: Option<u32>, @@ -927,13 +936,14 @@ pub struct CreateIndex { impl From<CreateIndex> for NodeAligned { fn from(value: CreateIndex) -> Self { - Self::Node224(Node224::CreateIndex(value)) + Self::Node232(Node232::CreateIndex(value)) } } #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct DropIndex { pub name: SmolStr, + pub if_exists: bool, pub timeout: Decimal, } @@ -1225,7 +1235,7 @@ impl Node136 { #[allow(clippy::module_name_repetitions, clippy::large_enum_variant)] #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] -pub enum Node224 { +pub enum Node232 { Invalid(Invalid), CreateTable(CreateTable), CreateIndex(CreateIndex), @@ -1233,21 +1243,21 @@ pub enum Node224 { RemoveServiceFromTier(RemoveServiceFromTier), } -impl Node224 { +impl Node232 { #[must_use] pub fn into_owned(self) -> NodeOwned { match self { - Node224::CreateTable(create_table) => { + Node232::CreateTable(create_table) => { NodeOwned::Ddl(DdlOwned::CreateTable(create_table)) } - Node224::CreateIndex(create_index) => { + Node232::CreateIndex(create_index) => { NodeOwned::Ddl(DdlOwned::CreateIndex(create_index)) } - Node224::Invalid(inv) => NodeOwned::Invalid(inv), - Node224::AppendServiceToTier(append) => { + Node232::Invalid(inv) => NodeOwned::Invalid(inv), + Node232::AppendServiceToTier(append) => { NodeOwned::Plugin(PluginOwned::AppendServiceToTier(append)) } - Node224::RemoveServiceFromTier(remove) => { + Node232::RemoveServiceFromTier(remove) => { NodeOwned::Plugin(PluginOwned::RemoveServiceFromTier(remove)) } } @@ -1261,7 +1271,7 @@ pub enum NodeAligned { Node64(Node64), Node96(Node96), Node136(Node136), - Node224(Node224), + Node232(Node232), } impl From<Node32> for NodeAligned { @@ -1288,9 +1298,9 @@ impl From<Node136> for NodeAligned { } } -impl From<Node224> for NodeAligned { - fn from(value: Node224) -> Self { - Self::Node224(value) +impl From<Node232> for NodeAligned { + fn from(value: Node232) -> Self { + Self::Node232(value) } } // parameter to avoid multiple enums diff --git a/sbroad-core/src/ir/node/plugin.rs b/sbroad-core/src/ir/node/plugin.rs index 57a103c104..424d90df44 100644 --- a/sbroad-core/src/ir/node/plugin.rs +++ b/sbroad-core/src/ir/node/plugin.rs @@ -1,5 +1,5 @@ use crate::errors::{Entity, SbroadError}; -use crate::ir::node::{Node136, Node224, Node96, NodeAligned}; +use crate::ir::node::{Node136, Node232, Node96, NodeAligned}; use crate::ir::{Node, NodeId, Plan}; use serde::{Deserialize, Serialize}; use smol_str::{format_smolstr, SmolStr}; @@ -116,7 +116,7 @@ pub struct AppendServiceToTier { impl From<AppendServiceToTier> for NodeAligned { fn from(value: AppendServiceToTier) -> Self { - Self::Node224(Node224::AppendServiceToTier(value)) + Self::Node232(Node232::AppendServiceToTier(value)) } } @@ -131,7 +131,7 @@ pub struct RemoveServiceFromTier { impl From<RemoveServiceFromTier> for NodeAligned { fn from(value: RemoveServiceFromTier) -> Self { - Self::Node224(Node224::RemoveServiceFromTier(value)) + Self::Node232(Node232::RemoveServiceFromTier(value)) } } @@ -419,7 +419,7 @@ mod test { }, TestCase { sql: r#"ALTER PLUGIN "abc" 0.1.0 ADD SERVICE "svc1" TO TIER "tier1" option(timeout=1)"#, - arena_type: ArenaType::Arena224, + arena_type: ArenaType::Arena232, expected: PluginOwned::AppendServiceToTier(AppendServiceToTier { service_name: SmolStr::from("svc1"), plugin_name: SmolStr::from("abc"), @@ -430,7 +430,7 @@ mod test { }, TestCase { sql: r#"ALTER PLUGIN "abc" 0.1.0 REMOVE SERVICE "svc1" FROM TIER "tier1" option(timeout=11)"#, - arena_type: ArenaType::Arena224, + arena_type: ArenaType::Arena232, expected: PluginOwned::RemoveServiceFromTier(RemoveServiceFromTier { service_name: SmolStr::from("svc1"), plugin_name: SmolStr::from("abc"), diff --git a/sbroad-core/src/ir/node/tests.rs b/sbroad-core/src/ir/node/tests.rs index d5453a0a98..e541233a21 100644 --- a/sbroad-core/src/ir/node/tests.rs +++ b/sbroad-core/src/ir/node/tests.rs @@ -1,4 +1,4 @@ -use crate::ir::node::{Node136, Node224, Node32, Node64, Node96}; +use crate::ir::node::{Node136, Node232, Node32, Node64, Node96}; #[test] fn test_node_size() { @@ -6,5 +6,5 @@ fn test_node_size() { assert!(std::mem::size_of::<Node64>() == 72); assert!(std::mem::size_of::<Node96>() == 96); assert!(std::mem::size_of::<Node136>() == 136); - assert!(std::mem::size_of::<Node224>() == 224); + assert!(std::mem::size_of::<Node232>() == 232); } diff --git a/sbroad-core/src/ir/operator.rs b/sbroad-core/src/ir/operator.rs index 9b31ad124e..8669a42497 100644 --- a/sbroad-core/src/ir/operator.rs +++ b/sbroad-core/src/ir/operator.rs @@ -1635,7 +1635,7 @@ impl Plan { for (id, _) in self.nodes.arena224.iter().enumerate() { let parent_id = NodeId { offset: u32::try_from(id).unwrap(), - arena_type: ArenaType::Arena224, + arena_type: ArenaType::Arena232, }; if !matches!(self.get_node(parent_id)?, Node::Relational(_)) { -- GitLab