From cc73a24d7afee5bddcdca189ab15a5e608a040f5 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Mon, 17 Mar 2025 23:05:02 +0200 Subject: [PATCH 1/4] mysql GLOBAL scope/context in SET stmts --- src/ast/mod.rs | 33 ++++++++++++++++++++++++++------- src/parser/mod.rs | 20 ++++++++++---------- tests/sqlparser_common.rs | 12 ++++++------ tests/sqlparser_hive.rs | 9 +++++---- tests/sqlparser_mssql.rs | 2 +- tests/sqlparser_mysql.rs | 2 +- tests/sqlparser_postgres.rs | 18 ++++++++---------- 7 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 8c4079213..4e6b63589 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::tokenizer::Span; +use crate::{keywords::Keyword, tokenizer::Span}; pub use self::data_type::{ ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember, @@ -2578,7 +2578,7 @@ pub enum Set { /// SQL Standard-style /// SET a = 1; SingleAssignment { - local: bool, + scope: ContextModifier, hivevar: bool, variable: ObjectName, values: Vec, @@ -2660,7 +2660,7 @@ impl Display for Set { role_name, } => { let role_name = role_name.clone().unwrap_or_else(|| Ident::new("NONE")); - write!(f, "SET{context_modifier} ROLE {role_name}") + write!(f, "SET {context_modifier}ROLE {role_name}") } Self::SetSessionParam(kind) => write!(f, "SET {kind}"), Self::SetTransaction { @@ -2707,7 +2707,7 @@ impl Display for Set { Ok(()) } Set::SingleAssignment { - local, + scope, hivevar, variable, values, @@ -2715,7 +2715,7 @@ impl Display for Set { write!( f, "SET {}{}{} = {}", - if *local { "LOCAL " } else { "" }, + scope, if *hivevar { "HIVEVAR:" } else { "" }, variable, display_comma_separated(values) @@ -7910,6 +7910,8 @@ pub enum ContextModifier { Local, /// `SESSION` identifier Session, + /// `GLOBAL` identifier + Global, } impl fmt::Display for ContextModifier { @@ -7919,11 +7921,28 @@ impl fmt::Display for ContextModifier { write!(f, "") } Self::Local => { - write!(f, " LOCAL") + write!(f, "LOCAL ") } Self::Session => { - write!(f, " SESSION") + write!(f, "SESSION ") } + Self::Global => { + write!(f, "GLOBAL ") + } + } + } +} + +impl From> for ContextModifier { + fn from(kw: Option) -> Self { + match kw { + Some(kw) => match kw { + Keyword::LOCAL => Self::Local, + Keyword::SESSION => Self::Session, + Keyword::GLOBAL => Self::Global, + _ => Self::None, + }, + None => Self::None, } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e4c170edd..0753380d6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -11100,11 +11100,7 @@ impl<'a> Parser<'a> { /// Parse a `SET ROLE` statement. Expects SET to be consumed already. fn parse_set_role(&mut self, modifier: Option) -> Result { self.expect_keyword_is(Keyword::ROLE)?; - let context_modifier = match modifier { - Some(Keyword::LOCAL) => ContextModifier::Local, - Some(Keyword::SESSION) => ContextModifier::Session, - _ => ContextModifier::None, - }; + let context_modifier = ContextModifier::from(modifier); let role_name = if self.parse_keyword(Keyword::NONE) { None @@ -11176,8 +11172,12 @@ impl<'a> Parser<'a> { } fn parse_set(&mut self) -> Result { - let modifier = - self.parse_one_of_keywords(&[Keyword::SESSION, Keyword::LOCAL, Keyword::HIVEVAR]); + let modifier = self.parse_one_of_keywords(&[ + Keyword::SESSION, + Keyword::LOCAL, + Keyword::HIVEVAR, + Keyword::GLOBAL, + ]); if let Some(Keyword::HIVEVAR) = modifier { self.expect_token(&Token::Colon)?; @@ -11193,7 +11193,7 @@ impl<'a> Parser<'a> { { if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { return Ok(Set::SingleAssignment { - local: modifier == Some(Keyword::LOCAL), + scope: ContextModifier::from(modifier), hivevar: modifier == Some(Keyword::HIVEVAR), variable: ObjectName::from(vec!["TIMEZONE".into()]), values: self.parse_set_values(false)?, @@ -11283,7 +11283,7 @@ impl<'a> Parser<'a> { }?; Ok(Set::SingleAssignment { - local: modifier == Some(Keyword::LOCAL), + scope: ContextModifier::from(modifier), hivevar: modifier == Some(Keyword::HIVEVAR), variable, values, @@ -11311,7 +11311,7 @@ impl<'a> Parser<'a> { if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { let stmt = match variables { OneOrManyWithParens::One(var) => Set::SingleAssignment { - local: modifier == Some(Keyword::LOCAL), + scope: ContextModifier::from(modifier), hivevar: modifier == Some(Keyword::HIVEVAR), variable: var, values: self.parse_set_values(false)?, diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index c65cc6b53..ab64c155f 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -8627,12 +8627,12 @@ fn parse_set_transaction() { fn parse_set_variable() { match verified_stmt("SET SOMETHING = '1'") { Statement::Set(Set::SingleAssignment { - local, + scope, hivevar, variable, values, }) => { - assert!(!local); + assert_eq!(scope, ContextModifier::None); assert!(!hivevar); assert_eq!(variable, ObjectName::from(vec!["SOMETHING".into()])); assert_eq!( @@ -8719,12 +8719,12 @@ fn parse_set_variable() { fn parse_set_role_as_variable() { match verified_stmt("SET role = 'foobar'") { Statement::Set(Set::SingleAssignment { - local, + scope, hivevar, variable, values, }) => { - assert!(!local); + assert_eq!(scope, ContextModifier::None); assert!(!hivevar); assert_eq!(variable, ObjectName::from(vec!["role".into()])); assert_eq!( @@ -8766,12 +8766,12 @@ fn parse_double_colon_cast_at_timezone() { fn parse_set_time_zone() { match verified_stmt("SET TIMEZONE = 'UTC'") { Statement::Set(Set::SingleAssignment { - local, + scope, hivevar, variable, values, }) => { - assert!(!local); + assert_eq!(scope, ContextModifier::None); assert!(!hivevar); assert_eq!(variable, ObjectName::from(vec!["TIMEZONE".into()])); assert_eq!( diff --git a/tests/sqlparser_hive.rs b/tests/sqlparser_hive.rs index 56fe22a0d..a9549cb60 100644 --- a/tests/sqlparser_hive.rs +++ b/tests/sqlparser_hive.rs @@ -21,9 +21,10 @@ //! is also tested (on the inputs it can handle). use sqlparser::ast::{ - ClusteredBy, CommentDef, CreateFunction, CreateFunctionBody, CreateFunctionUsing, CreateTable, - Expr, Function, FunctionArgumentList, FunctionArguments, Ident, ObjectName, OrderByExpr, - OrderByOptions, SelectItem, Set, Statement, TableFactor, UnaryOperator, Use, Value, + ClusteredBy, CommentDef, ContextModifier, CreateFunction, CreateFunctionBody, + CreateFunctionUsing, CreateTable, Expr, Function, FunctionArgumentList, FunctionArguments, + Ident, ObjectName, OrderByExpr, OrderByOptions, SelectItem, Set, Statement, TableFactor, + UnaryOperator, Use, Value, }; use sqlparser::dialect::{GenericDialect, HiveDialect, MsSqlDialect}; use sqlparser::parser::ParserError; @@ -369,7 +370,7 @@ fn set_statement_with_minus() { assert_eq!( hive().verified_stmt("SET hive.tez.java.opts = -Xmx4g"), Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![ Ident::new("hive"), diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index af71d2523..d4e5fa719 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1251,7 +1251,7 @@ fn parse_mssql_declare() { }] }, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![Ident::new("@bar")]), values: vec![Expr::Value( diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index a56335934..2c5d72b72 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -618,7 +618,7 @@ fn parse_set_variables() { assert_eq!( mysql_and_generic().verified_stmt("SET LOCAL autocommit = 1"), Statement::Set(Set::SingleAssignment { - local: true, + scope: ContextModifier::Local, hivevar: false, variable: ObjectName::from(vec!["autocommit".into()]), values: vec![Expr::value(number("1"))], diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index e62f23597..cf66af74e 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -988,8 +988,7 @@ fn parse_create_schema_if_not_exists() { Statement::CreateSchema { if_not_exists: true, schema_name, - options: _, - default_collate_spec: _, + .. } => assert_eq!("schema_name", schema_name.to_string()), _ => unreachable!(), } @@ -1433,7 +1432,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![Ident::new("a")]), values: vec![Expr::Identifier(Ident { @@ -1448,7 +1447,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![Ident::new("a")]), values: vec![Expr::Value( @@ -1461,7 +1460,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![Ident::new("a")]), values: vec![Expr::value(number("0"))], @@ -1472,7 +1471,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![Ident::new("a")]), values: vec![Expr::Identifier(Ident::new("DEFAULT"))], @@ -1483,7 +1482,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: true, + scope: ContextModifier::Local, hivevar: false, variable: ObjectName::from(vec![Ident::new("a")]), values: vec![Expr::Identifier("b".into())], @@ -1494,7 +1493,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![Ident::new("a"), Ident::new("b"), Ident::new("c")]), values: vec![Expr::Identifier(Ident { @@ -1512,7 +1511,7 @@ fn parse_set() { assert_eq!( stmt, Statement::Set(Set::SingleAssignment { - local: false, + scope: ContextModifier::None, hivevar: false, variable: ObjectName::from(vec![ Ident::new("hive"), @@ -1526,7 +1525,6 @@ fn parse_set() { ); pg_and_generic().one_statement_parses_to("SET a TO b", "SET a = b"); - pg_and_generic().one_statement_parses_to("SET SESSION a = b", "SET a = b"); assert_eq!( pg_and_generic().parse_sql_statements("SET"), From 39c9b2723dd2ebed00830e05467b7b74e346aa86 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Mon, 17 Mar 2025 23:09:46 +0200 Subject: [PATCH 2/4] add test case --- tests/sqlparser_common.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index ab64c155f..bdac412bf 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -8645,6 +8645,26 @@ fn parse_set_variable() { _ => unreachable!(), } + match verified_stmt("SET GLOBAL VARIABLE = 'Value'") { + Statement::Set(Set::SingleAssignment { + scope, + hivevar, + variable, + values, + }) => { + assert_eq!(scope, ContextModifier::Global); + assert!(!hivevar); + assert_eq!(variable, ObjectName::from(vec!["VARIABLE".into()])); + assert_eq!( + values, + vec![Expr::Value( + (Value::SingleQuotedString("Value".into())).with_empty_span() + )] + ); + } + _ => unreachable!(), + } + let multi_variable_dialects = all_dialects_where(|d| d.supports_parenthesized_set_variables()); let sql = r#"SET (a, b, c) = (1, 2, 3)"#; match multi_variable_dialects.verified_stmt(sql) { From 70aba82752a39a17fd45ba3d6b54042211b79b68 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Tue, 18 Mar 2025 21:53:37 +0200 Subject: [PATCH 3/4] add Global to ContextModifier doc --- src/ast/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4e6b63589..9a7b30cfa 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -7899,7 +7899,7 @@ impl fmt::Display for FlushLocation { } } -/// Optional context modifier for statements that can be or `LOCAL`, or `SESSION`. +/// Optional context modifier for statements that can be or `LOCAL`, 'GLOBAL', or `SESSION`. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] From 4260dcd85f0a6d5d34779ec1431007cd74e008a3 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Wed, 19 Mar 2025 20:47:56 +0200 Subject: [PATCH 4/4] replace impl with a fn --- src/ast/mod.rs | 18 ++---------------- src/parser/mod.rs | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 9a7b30cfa..ea3031d3f 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "visitor")] use sqlparser_derive::{Visit, VisitMut}; -use crate::{keywords::Keyword, tokenizer::Span}; +use crate::tokenizer::Span; pub use self::data_type::{ ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember, @@ -7899,7 +7899,7 @@ impl fmt::Display for FlushLocation { } } -/// Optional context modifier for statements that can be or `LOCAL`, 'GLOBAL', or `SESSION`. +/// Optional context modifier for statements that can be or `LOCAL`, `GLOBAL`, or `SESSION`. #[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] @@ -7933,20 +7933,6 @@ impl fmt::Display for ContextModifier { } } -impl From> for ContextModifier { - fn from(kw: Option) -> Self { - match kw { - Some(kw) => match kw { - Keyword::LOCAL => Self::Local, - Keyword::SESSION => Self::Session, - Keyword::GLOBAL => Self::Global, - _ => Self::None, - }, - None => Self::None, - } - } -} - /// Function describe in DROP FUNCTION. #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0753380d6..a74f735f9 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -1799,6 +1799,15 @@ impl<'a> Parser<'a> { }) } + fn keyword_to_modifier(k: Option) -> ContextModifier { + match k { + Some(Keyword::LOCAL) => ContextModifier::Local, + Some(Keyword::GLOBAL) => ContextModifier::Global, + Some(Keyword::SESSION) => ContextModifier::Session, + _ => ContextModifier::None, + } + } + /// Check if the root is an identifier and all fields are identifiers. fn is_all_ident(root: &Expr, fields: &[AccessExpr]) -> bool { if !matches!(root, Expr::Identifier(_)) { @@ -11100,7 +11109,7 @@ impl<'a> Parser<'a> { /// Parse a `SET ROLE` statement. Expects SET to be consumed already. fn parse_set_role(&mut self, modifier: Option) -> Result { self.expect_keyword_is(Keyword::ROLE)?; - let context_modifier = ContextModifier::from(modifier); + let context_modifier = Self::keyword_to_modifier(modifier); let role_name = if self.parse_keyword(Keyword::NONE) { None @@ -11193,7 +11202,7 @@ impl<'a> Parser<'a> { { if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { return Ok(Set::SingleAssignment { - scope: ContextModifier::from(modifier), + scope: Self::keyword_to_modifier(modifier), hivevar: modifier == Some(Keyword::HIVEVAR), variable: ObjectName::from(vec!["TIMEZONE".into()]), values: self.parse_set_values(false)?, @@ -11283,7 +11292,7 @@ impl<'a> Parser<'a> { }?; Ok(Set::SingleAssignment { - scope: ContextModifier::from(modifier), + scope: Self::keyword_to_modifier(modifier), hivevar: modifier == Some(Keyword::HIVEVAR), variable, values, @@ -11311,7 +11320,7 @@ impl<'a> Parser<'a> { if self.consume_token(&Token::Eq) || self.parse_keyword(Keyword::TO) { let stmt = match variables { OneOrManyWithParens::One(var) => Set::SingleAssignment { - scope: ContextModifier::from(modifier), + scope: Self::keyword_to_modifier(modifier), hivevar: modifier == Some(Keyword::HIVEVAR), variable: var, values: self.parse_set_values(false)?,