diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index 1c6cd9990e011a19a84b0cc193488544afafcf15..a048b1bdb30d8c49d4376c1ae9d17c8f2ccb7ef9 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -108,8 +108,15 @@ grant ::= 'GRANT' ( ) 'TO' (role | user) ddl ::= (alter_procedure | create_index | create_procedure | create_table - | drop_index | drop_procedure | drop_table) + | drop_index | drop_procedure | drop_table | alter_system) ('OPTION' '(' ('TIMEOUT' '=' double)')')? +alter_system ::= ('ALTER' 'SYSTEM' + ( + 'RESET' ('ALL' | param_name) | + 'SET' param_name ('=' | 'TO') ('DEFAULT' | param_value) + ) + ('FOR' ('ALL' 'TIERS' | 'TIER' tier))? + ) alter_procedure ::= 'ALTER' 'PROCEDURE' procedure ('(' type (',' type)* ')')? 'RENAME' 'TO' procedure create_index ::= 'CREATE' 'UNIQUE'? 'INDEX' index 'ON' table diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index c68b706204d7876b9df4739b667f4b0116d5600e..c259463df0e48b2174692509e5f1539044e71d61 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, SetParamScopeType, SetParamValue}; +use crate::ir::ddl::{AlterSystemType, ColumnDef, Ddl, SetParamScopeType, SetParamValue}; use crate::ir::ddl::{Language, ParamDef}; use crate::ir::expression::cast::Type as CastType; use crate::ir::expression::{ @@ -301,6 +301,95 @@ fn parse_create_proc(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, Ok(create_proc) } +fn parse_alter_system<M: Metadata>( + ast: &AbstractSyntaxTree, + node: &ParseNode, + pairs_map: &mut ParsingPairsMap, + worker: &mut ExpressionsWorker<M>, + plan: &mut Plan, +) -> Result<Ddl, SbroadError> { + let alter_system_type_node_id = node + .children + .first() + .expect("Alter system type node expected."); + let alter_system_type_node = ast.nodes.get_node(*alter_system_type_node_id)?; + + let ty = match alter_system_type_node.rule { + Rule::AlterSystemReset => { + let param_name = + if let Some(identifier_node_id) = alter_system_type_node.children.first() { + Some(parse_identifier(ast, *identifier_node_id)?) + } else { + None + }; + AlterSystemType::AlterSystemReset { param_name } + } + Rule::AlterSystemSet => { + let param_name_node_id = alter_system_type_node + .children + .first() + .expect("Param name node expected under Alter system."); + let param_name = parse_identifier(ast, *param_name_node_id)?; + + if let Some(param_value_node_id) = alter_system_type_node.children.get(1) { + let expr_pair = pairs_map.remove_pair(*param_value_node_id); + let expr_plan_node_id = parse_expr(Pairs::single(expr_pair), &[], worker, plan)?; + let value_node = plan.get_node(expr_plan_node_id)?; + if let Node::Expression(Expression::Constant { value }) = value_node { + AlterSystemType::AlterSystemSet { + param_name, + param_value: value.clone(), + } + } else { + // TODO: Should be fixed as + // https://git.picodata.io/picodata/picodata/sbroad/-/issues/763 + return Err(SbroadError::Invalid( + Entity::Expression, + Some(SmolStr::from( + "ALTER SYSTEM currently supports only literals as values.", + )), + )); + } + } else { + // In case of `set <PARAM_NAME> to default` we send `Reset` opcode + // instead of `Set`. + AlterSystemType::AlterSystemReset { + param_name: Some(param_name), + } + } + } + _ => unreachable!("Unexpected rule: {:?}", alter_system_type_node.rule), + }; + + let tier_name = if let Some(tier_node_id) = node.children.get(1) { + let tier_node = ast.nodes.get_node(*tier_node_id)?; + let tier_node_child_id = tier_node + .children + .first() + .expect("Expected mandatory child node under AlterSystemTier."); + let tier_node_child = ast.nodes.get_node(*tier_node_child_id)?; + match tier_node_child.rule { + Rule::AlterSystemTiersAll => None, + Rule::AlterSystemTierSingle => { + let node_child_id = tier_node_child + .children + .first() + .expect("Child node expected under AlterSystemTierSingle."); + Some(parse_identifier(ast, *node_child_id)?) + } + _ => panic!("Unexpected rule met under AlterSystemTier."), + } + } else { + None + }; + + Ok(Ddl::AlterSystem { + ty, + tier_name, + timeout: get_default_timeout(), + }) +} + fn parse_proc_with_optional_params( ast: &AbstractSyntaxTree, node: &ParseNode, @@ -2597,8 +2686,8 @@ impl AbstractSyntaxTree { // * `Expr`s are parsed using Pratt parser with a separate `parse_expr` // function call on the stage of `resolve_metadata`. // * `Row`s are added to support parsing Row expressions under `Values` nodes. - // * `Literal`s are added to support procedure calls which should not contain - // all possible `Expr`s. + // * `Literal`s are added to support procedure calls and + // ALTER SYSTEM which should not contain all possible `Expr`s. pairs_map.insert(arena_node_id, stack_node.pair.clone()); } Rule::Projection | Rule::OrderBy => { @@ -3410,6 +3499,12 @@ impl AbstractSyntaxTree { let plan_id = plan.nodes.push(Node::Ddl(create_index)); map.add(id, plan_id); } + Rule::AlterSystem => { + let alter_system = + parse_alter_system(self, node, pairs_map, &mut worker, &mut plan)?; + let plan_id = plan.nodes.push(Node::Ddl(alter_system)); + map.add(id, plan_id); + } Rule::CreateProc => { let create_proc = parse_create_proc(self, node)?; let plan_id = plan.nodes.push(Node::Ddl(create_proc)); diff --git a/sbroad-core/src/frontend/sql/ir/tests.rs b/sbroad-core/src/frontend/sql/ir/tests.rs index d859d866f8aa4a2c5520a2f6773c6fdb1a2733ff..b844473f54600a442c3c8c43c4e1c3ef44ce0817 100644 --- a/sbroad-core/src/frontend/sql/ir/tests.rs +++ b/sbroad-core/src/frontend/sql/ir/tests.rs @@ -3755,6 +3755,44 @@ fn front_mock_set_param_transaction() { } } +#[test] +fn front_alter_system_check_parses_ok() { + let queries_to_check_ok = vec![ + r#"alter system set param_name = 1"#, + r#"alter system set "param_name" = 1"#, + r#"alter system set param_name = 'value'"#, + r#"alter system set param_name = true"#, + r#"alter system set param_name to 1"#, + r#"alter system set param_name to 2.3"#, + r#"alter system set param_name to default"#, + r#"alter system set param_name to 'value'"#, + r#"alter system set param_name to null"#, + r#"alter system reset all"#, + r#"alter system reset param_name"#, + r#"alter system reset "param_name""#, + r#"alter system reset "param_name" for all tiers"#, + r#"alter system reset "param_name" for tier tier_name"#, + r#"alter system reset "param_name" for tier "tier_name""#, + ]; + let metadata = &RouterConfigurationMock::new(); + for query in queries_to_check_ok { + let plan = AbstractSyntaxTree::transform_into_plan(query, metadata); + assert!(plan.is_ok()) + } + + let queries_to_check_all_expressions_not_supported = vec![ + r#"alter system set param_name = ?"#, + r#"alter system set param_name = 1 + 1"#, + ]; + let metadata = &RouterConfigurationMock::new(); + for query in queries_to_check_all_expressions_not_supported { + let err = AbstractSyntaxTree::transform_into_plan(query, metadata).unwrap_err(); + assert!(err + .to_string() + .contains("ALTER SYSTEM currently supports only literals as values.")) + } +} + #[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 d360eb74511cc04c8496a55ecd246a5e5d21a3ea..88947079987d7322be2974cb16a1aa4f0d0f2757 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -56,7 +56,7 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil PrivilegeWrite = { ^"write" } DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex - | CreateProc | DropProc | RenameProc | SetParam | SetTransaction } + | CreateProc | DropProc | RenameProc | SetParam | SetTransaction | AlterSystem } CreateTable = { ^"create" ~ ^"table" ~ NewTable ~ "(" ~ Columns ~ ("," ~ PrimaryKey)? ~ ")" ~ @@ -149,6 +149,13 @@ DDL = _{ CreateTable | DropTable | CreateIndex | DropIndex Deferrable = { NotFlag? ~ ^"deferrable" } TransactionSnapshot = { ^"snapshot" ~ SingleQuotedString } + AlterSystem = { ^"alter" ~ ^"system" ~ (AlterSystemSet | AlterSystemReset) ~ AlterSystemTier? } + AlterSystemReset = {^"reset" ~ (^"all" | Identifier) } + AlterSystemSet = {^"set" ~ Identifier ~ ("=" | ^"to") ~ (^"default" | Expr) } + AlterSystemTier = { ^"for" ~ (AlterSystemTiersAll | AlterSystemTierSingle) } + AlterSystemTiersAll = { ^"all" ~ ^"tiers" } + AlterSystemTierSingle = { ^"tier" ~ Identifier } + 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 811d02ffa3da5c65107fc6aba6bb65892b450271..55d173c172fa79afb5a874f900a6cf316d248f1e 100644 --- a/sbroad-core/src/ir/ddl.rs +++ b/sbroad-core/src/ir/ddl.rs @@ -1,3 +1,4 @@ +use crate::ir::value::Value; use crate::{ errors::{Entity, SbroadError}, ir::{relation::Type as RelationType, Node, Plan}, @@ -61,6 +62,18 @@ impl SetParamValue { } } +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub enum AlterSystemType { + AlterSystemSet { + param_name: SmolStr, + param_value: Value, + }, + AlterSystemReset { + /// In case of None, all params are supposed to be reset. + param_name: Option<SmolStr>, + }, +} + #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum Ddl { CreateTable { @@ -83,6 +96,13 @@ pub enum Ddl { name: SmolStr, timeout: Decimal, }, + AlterSystem { + ty: AlterSystemType, + /// In case of None, ALTER is supposed + /// to be executed on all tiers. + tier_name: Option<SmolStr>, + timeout: Decimal, + }, CreateProc { name: SmolStr, params: Vec<ParamDef>, @@ -143,6 +163,7 @@ impl Ddl { | Ddl::DropTable { ref timeout, .. } | Ddl::CreateIndex { ref timeout, .. } | Ddl::DropIndex { ref timeout, .. } + | Ddl::AlterSystem { ref timeout, .. } | Ddl::SetParam { ref timeout, .. } | Ddl::SetTransaction { ref timeout, .. } | Ddl::CreateProc { ref timeout, .. }