diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 22762168f..330af3d33 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1117,6 +1117,7 @@ pub enum Statement { if_not_exists: bool, }, /// CREATE ROLE + /// See [postgres](https://www.postgresql.org/docs/current/sql-createrole.html) CreateRole { names: Vec, if_not_exists: bool, @@ -1132,7 +1133,9 @@ pub enum Statement { connection_limit: Option, valid_until: Option, in_role: Vec, + in_group: Vec, role: Vec, + user: Vec, admin: Vec, // MSSQL authorization_owner: Option, @@ -2005,7 +2008,9 @@ impl fmt::Display for Statement { connection_limit, valid_until, in_role, + in_group, role, + user, admin, authorization_owner, } => { @@ -2064,9 +2069,15 @@ impl fmt::Display for Statement { if !in_role.is_empty() { write!(f, " IN ROLE {}", display_comma_separated(in_role))?; } + if !in_group.is_empty() { + write!(f, " IN GROUP {}", display_comma_separated(in_group))?; + } if !role.is_empty() { write!(f, " ROLE {}", display_comma_separated(role))?; } + if !user.is_empty() { + write!(f, " USER {}", display_comma_separated(user))?; + } if !admin.is_empty() { write!(f, " ADMIN {}", display_comma_separated(admin))?; } diff --git a/src/parser.rs b/src/parser.rs index 503ef07db..e1f0c87a1 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2109,7 +2109,9 @@ impl<'a> Parser<'a> { let mut connection_limit = None; let mut valid_until = None; let mut in_role = vec![]; - let mut roles = vec![]; + let mut in_group = vec![]; + let mut role = vec![]; + let mut user = vec![]; let mut admin = vec![]; while let Some(keyword) = self.parse_one_of_keywords(&optional_keywords) { @@ -2209,22 +2211,37 @@ impl<'a> Parser<'a> { } } Keyword::IN => { - if self.parse_keyword(Keyword::ROLE) || self.parse_keyword(Keyword::GROUP) { + if self.parse_keyword(Keyword::ROLE) { if !in_role.is_empty() { - parser_err!("Found multiple IN ROLE or IN GROUP") + parser_err!("Found multiple IN ROLE") } else { in_role = self.parse_comma_separated(Parser::parse_identifier)?; Ok(()) } + } else if self.parse_keyword(Keyword::GROUP) { + if !in_group.is_empty() { + parser_err!("Found multiple IN GROUP") + } else { + in_group = self.parse_comma_separated(Parser::parse_identifier)?; + Ok(()) + } } else { self.expected("ROLE or GROUP after IN", self.peek_token()) } } - Keyword::ROLE | Keyword::USER => { - if !roles.is_empty() { - parser_err!("Found multiple ROLE or USER") + Keyword::ROLE => { + if !role.is_empty() { + parser_err!("Found multiple ROLE") + } else { + role = self.parse_comma_separated(Parser::parse_identifier)?; + Ok(()) + } + } + Keyword::USER => { + if !user.is_empty() { + parser_err!("Found multiple USER") } else { - roles = self.parse_comma_separated(Parser::parse_identifier)?; + user = self.parse_comma_separated(Parser::parse_identifier)?; Ok(()) } } @@ -2254,7 +2271,9 @@ impl<'a> Parser<'a> { connection_limit, valid_until, in_role, - role: roles, + in_group, + role, + user, admin, authorization_owner, }) diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 07e87770c..171624c0b 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1807,7 +1807,9 @@ fn parse_create_role() { connection_limit, valid_until, in_role, + in_group, role, + user, admin, authorization_owner, }], @@ -1833,13 +1835,29 @@ fn parse_create_role() { Some(Expr::Value(Value::SingleQuotedString("2025-01-01".into()))) ); assert_eq_vec(&["role1", "role2"], in_role); + assert!(in_group.is_empty()); assert_eq_vec(&["role3"], role); + assert!(user.is_empty()); assert_eq_vec(&["role4", "role5"], admin); assert_eq!(*authorization_owner, None); } err => panic!("Failed to parse CREATE ROLE test case: {:?}", err), } + let sql = "CREATE ROLE abc WITH USER foo, bar ROLE baz "; + match pg().parse_sql_statements(sql).as_deref() { + Ok( + [Statement::CreateRole { + names, user, role, .. + }], + ) => { + assert_eq_vec(&["abc"], names); + assert_eq_vec(&["foo", "bar"], user); + assert_eq_vec(&["baz"], role); + } + err => panic!("Failed to parse CREATE ROLE test case: {:?}", err), + } + let negatables = vec![ "BYPASSRLS", "CREATEDB",