Skip to content

Commit 7c91833

Browse files
committed
Add PostgreSQL CREATE USER and ALTER USER support
Currently, the library only supports `CREATE ROLE` and `ALTER ROLE` for PostgreSQL. `CREATE USER` and `ALTER USER` fail to parse with errors like `"Expected: an object type after CREATE, found: USER"` But in PostgreSQL reference: - `CREATE USER` is equivalent to `CREATE ROLE`, except that `LOGIN` is assumed by default - `ALTER USER` is an alias to `ALTER ROLE` - Both should support the same options as their ROLE counterparts This commit extends the existing `CreateRole` and `AlterRole` structures to distinct which keyword has been used: `USER` or `ROLE`. It allows these expressions to be parsed and displayed back.
1 parent 779dcf9 commit 7c91833

File tree

6 files changed

+211
-5
lines changed

6 files changed

+211
-5
lines changed

src/ast/dcl.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ use sqlparser_derive::{Visit, VisitMut};
3131
use super::{display_comma_separated, Expr, Ident, Password};
3232
use crate::ast::{display_separated, ObjectName};
3333

34+
/// Represents whether ROLE or USER keyword was used in CREATE/ALTER statements.
35+
/// In PostgreSQL, CREATE USER and ALTER USER are equivalent to CREATE ROLE and ALTER ROLE,
36+
/// with CREATE USER having LOGIN enabled by default.
37+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
38+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
40+
pub enum RoleKeyword {
41+
Role,
42+
User,
43+
}
44+
45+
impl fmt::Display for RoleKeyword {
46+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47+
match self {
48+
RoleKeyword::Role => write!(f, "ROLE"),
49+
RoleKeyword::User => write!(f, "USER"),
50+
}
51+
}
52+
}
53+
3454
/// An option in `ROLE` statement.
3555
///
3656
/// <https://www.postgresql.org/docs/current/sql-createrole.html>

src/ast/mod.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub use self::data_type::{
5555
ExactNumberInfo, IntervalFields, StructBracketKind, TimezoneInfo,
5656
};
5757
pub use self::dcl::{
58-
AlterRoleOperation, ResetConfig, RoleOption, SecondaryRoles, SetConfigValue, Use,
58+
AlterRoleOperation, ResetConfig, RoleKeyword, RoleOption, SecondaryRoles, SetConfigValue, Use,
5959
};
6060
pub use self::ddl::{
6161
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
@@ -3314,6 +3314,8 @@ pub enum Statement {
33143314
CreateRole {
33153315
names: Vec<ObjectName>,
33163316
if_not_exists: bool,
3317+
/// Whether ROLE or USER keyword was used
3318+
keyword: RoleKeyword,
33173319
// Postgres
33183320
login: Option<bool>,
33193321
inherit: Option<bool>,
@@ -3421,6 +3423,8 @@ pub enum Statement {
34213423
/// ```
34223424
AlterRole {
34233425
name: Ident,
3426+
/// Whether ROLE or USER keyword was used
3427+
keyword: RoleKeyword,
34243428
operation: AlterRoleOperation,
34253429
},
34263430
/// ```sql
@@ -5279,6 +5283,7 @@ impl fmt::Display for Statement {
52795283
Statement::CreateRole {
52805284
names,
52815285
if_not_exists,
5286+
keyword,
52825287
inherit,
52835288
login,
52845289
bypassrls,
@@ -5298,7 +5303,7 @@ impl fmt::Display for Statement {
52985303
} => {
52995304
write!(
53005305
f,
5301-
"CREATE ROLE {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}",
5306+
"CREATE {keyword} {if_not_exists}{names}{superuser}{create_db}{create_role}{inherit}{login}{replication}{bypassrls}",
53025307
if_not_exists = if *if_not_exists { "IF NOT EXISTS " } else { "" },
53035308
names = display_separated(names, ", "),
53045309
superuser = match *superuser {
@@ -5337,6 +5342,7 @@ impl fmt::Display for Statement {
53375342
None => ""
53385343
}
53395344
)?;
5345+
53405346
if let Some(limit) = connection_limit {
53415347
write!(f, " CONNECTION LIMIT {limit}")?;
53425348
}
@@ -5506,8 +5512,12 @@ impl fmt::Display for Statement {
55065512
Statement::AlterType(AlterType { name, operation }) => {
55075513
write!(f, "ALTER TYPE {name} {operation}")
55085514
}
5509-
Statement::AlterRole { name, operation } => {
5510-
write!(f, "ALTER ROLE {name} {operation}")
5515+
Statement::AlterRole {
5516+
name,
5517+
keyword,
5518+
operation,
5519+
} => {
5520+
write!(f, "ALTER {keyword} {name} {operation}")
55115521
}
55125522
Statement::AlterPolicy {
55135523
name,

src/parser/alter.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{Parser, ParserError};
1919
use crate::{
2020
ast::{
2121
AlterConnectorOwner, AlterPolicyOperation, AlterRoleOperation, Expr, Password, ResetConfig,
22-
RoleOption, SetConfigValue, Statement,
22+
RoleKeyword, RoleOption, SetConfigValue, Statement,
2323
},
2424
dialect::{MsSqlDialect, PostgreSqlDialect},
2525
keywords::Keyword,
@@ -39,6 +39,15 @@ impl Parser<'_> {
3939
))
4040
}
4141

42+
pub fn parse_alter_user(&mut self) -> Result<Statement, ParserError> {
43+
if dialect_of!(self is PostgreSqlDialect) {
44+
return self.parse_pg_alter_user();
45+
}
46+
Err(ParserError::ParserError(
47+
"ALTER USER is only supported for PostgreSqlDialect".into(),
48+
))
49+
}
50+
4251
/// Parse ALTER POLICY statement
4352
/// ```sql
4453
/// ALTER POLICY policy_name ON table_name [ RENAME TO new_name ]
@@ -162,11 +171,23 @@ impl Parser<'_> {
162171

163172
Ok(Statement::AlterRole {
164173
name: role_name,
174+
keyword: RoleKeyword::Role,
165175
operation,
166176
})
167177
}
168178

169179
fn parse_pg_alter_role(&mut self) -> Result<Statement, ParserError> {
180+
self.parse_pg_alter_role_or_user(RoleKeyword::Role)
181+
}
182+
183+
fn parse_pg_alter_user(&mut self) -> Result<Statement, ParserError> {
184+
self.parse_pg_alter_role_or_user(RoleKeyword::User)
185+
}
186+
187+
fn parse_pg_alter_role_or_user(
188+
&mut self,
189+
keyword: RoleKeyword,
190+
) -> Result<Statement, ParserError> {
170191
let role_name = self.parse_identifier()?;
171192

172193
// [ IN DATABASE _`database_name`_ ]
@@ -246,6 +267,7 @@ impl Parser<'_> {
246267

247268
Ok(Statement::AlterRole {
248269
name: role_name,
270+
keyword,
249271
operation,
250272
})
251273
}

src/parser/mod.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4788,6 +4788,10 @@ impl<'a> Parser<'a> {
47884788
}
47894789

47904790
fn parse_create_user(&mut self, or_replace: bool) -> Result<Statement, ParserError> {
4791+
if dialect_of!(self is PostgreSqlDialect) {
4792+
return self.parse_pg_create_user();
4793+
}
4794+
47914795
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
47924796
let name = self.parse_identifier()?;
47934797
let options = self.parse_key_value_options(false, &[Keyword::WITH, Keyword::TAG])?;
@@ -5995,6 +5999,17 @@ impl<'a> Parser<'a> {
59955999
}
59966000

59976001
pub fn parse_create_role(&mut self) -> Result<Statement, ParserError> {
6002+
self.parse_pg_create_role_or_user(RoleKeyword::Role)
6003+
}
6004+
6005+
pub fn parse_pg_create_user(&mut self) -> Result<Statement, ParserError> {
6006+
self.parse_pg_create_role_or_user(RoleKeyword::User)
6007+
}
6008+
6009+
fn parse_pg_create_role_or_user(
6010+
&mut self,
6011+
keyword: RoleKeyword,
6012+
) -> Result<Statement, ParserError> {
59986013
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
59996014
let names = self.parse_comma_separated(|p| p.parse_object_name(false))?;
60006015

@@ -6196,9 +6211,15 @@ impl<'a> Parser<'a> {
61966211
}?
61976212
}
61986213

6214+
// In PostgreSQL, CREATE USER defaults to LOGIN=true, CREATE ROLE defaults to LOGIN=false
6215+
if login.is_none() && keyword == RoleKeyword::User {
6216+
login = Some(true);
6217+
}
6218+
61996219
Ok(Statement::CreateRole {
62006220
names,
62016221
if_not_exists,
6222+
keyword,
62026223
login,
62036224
inherit,
62046225
bypassrls,
@@ -9263,6 +9284,7 @@ impl<'a> Parser<'a> {
92639284
Keyword::TABLE,
92649285
Keyword::INDEX,
92659286
Keyword::ROLE,
9287+
Keyword::USER,
92669288
Keyword::POLICY,
92679289
Keyword::CONNECTOR,
92689290
Keyword::ICEBERG,
@@ -9300,6 +9322,7 @@ impl<'a> Parser<'a> {
93009322
})
93019323
}
93029324
Keyword::ROLE => self.parse_alter_role(),
9325+
Keyword::USER => self.parse_alter_user(),
93039326
Keyword::POLICY => self.parse_alter_policy(),
93049327
Keyword::CONNECTOR => self.parse_alter_connector(),
93059328
// unreachable because expect_one_of_keywords used above

tests/sqlparser_mssql.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -802,6 +802,7 @@ fn parse_alter_role() {
802802
quote_style: None,
803803
span: Span::empty(),
804804
},
805+
keyword: RoleKeyword::Role,
805806
operation: AlterRoleOperation::RenameRole {
806807
role_name: Ident {
807808
value: "new_name".into(),
@@ -821,6 +822,7 @@ fn parse_alter_role() {
821822
quote_style: None,
822823
span: Span::empty(),
823824
},
825+
keyword: RoleKeyword::Role,
824826
operation: AlterRoleOperation::AddMember {
825827
member_name: Ident {
826828
value: "new_member".into(),
@@ -840,6 +842,7 @@ fn parse_alter_role() {
840842
quote_style: None,
841843
span: Span::empty(),
842844
},
845+
keyword: RoleKeyword::Role,
843846
operation: AlterRoleOperation::DropMember {
844847
member_name: Ident {
845848
value: "old_member".into(),

0 commit comments

Comments
 (0)