Skip to content

Commit 4eadf2f

Browse files
Parse ALTER USER as a synonym for ALTER ROLE
1 parent b376022 commit 4eadf2f

5 files changed

Lines changed: 116 additions & 44 deletions

File tree

src/dialect/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,18 @@ pub trait Dialect: Debug + Any {
366366
false
367367
}
368368

369+
/// Returns true if the dialect treats `ALTER USER` as a synonym for `ALTER ROLE`.
370+
///
371+
/// In PostgreSQL, `ALTER USER` and `ALTER ROLE` are synonyms that accept the same
372+
/// option syntax, so `ALTER USER` is parsed into a [`Statement::AlterRole`].
373+
///
374+
/// <https://www.postgresql.org/docs/current/sql-alteruser.html>
375+
///
376+
/// [`Statement::AlterRole`]: crate::ast::Statement::AlterRole
377+
fn supports_alter_user_as_alter_role(&self) -> bool {
378+
false
379+
}
380+
369381
/// Returns true if the dialects supports `group sets, roll up, or cube` expressions.
370382
fn supports_group_by_expr(&self) -> bool {
371383
false

src/dialect/postgresql.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,10 @@ impl Dialect for PostgreSqlDialect {
161161
true
162162
}
163163

164+
fn supports_alter_user_as_alter_role(&self) -> bool {
165+
true
166+
}
167+
164168
fn prec_value(&self, prec: Precedence) -> u8 {
165169
match prec {
166170
Precedence::Period => PERIOD_PREC,

src/parser/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10955,6 +10955,9 @@ impl<'a> Parser<'a> {
1095510955
Keyword::ROLE => self.parse_alter_role(),
1095610956
Keyword::POLICY => self.parse_alter_policy().map(Into::into),
1095710957
Keyword::CONNECTOR => self.parse_alter_connector(),
10958+
Keyword::USER if self.dialect.supports_alter_user_as_alter_role() => {
10959+
self.parse_alter_role()
10960+
}
1095810961
Keyword::USER => self.parse_alter_user().map(Into::into),
1095910962
// unreachable because expect_one_of_keywords used above
1096010963
unexpected_keyword => Err(ParserError::ParserError(

tests/sqlparser_common.rs

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18479,9 +18479,10 @@ fn parse_create_index_different_using_positions() {
1847918479

1848018480
#[test]
1848118481
fn test_parse_alter_user() {
18482-
verified_stmt("ALTER USER u1");
18483-
verified_stmt("ALTER USER IF EXISTS u1");
18484-
let stmt = verified_stmt("ALTER USER IF EXISTS u1 RENAME TO u2");
18482+
let dialects = all_dialects_except(|d| d.supports_alter_user_as_alter_role());
18483+
dialects.verified_stmt("ALTER USER u1");
18484+
dialects.verified_stmt("ALTER USER IF EXISTS u1");
18485+
let stmt = dialects.verified_stmt("ALTER USER IF EXISTS u1 RENAME TO u2");
1848518486
match stmt {
1848618487
Statement::AlterUser(alter) => {
1848718488
assert!(alter.if_exists);
@@ -18490,35 +18491,35 @@ fn test_parse_alter_user() {
1849018491
}
1849118492
_ => unreachable!(),
1849218493
}
18493-
verified_stmt("ALTER USER IF EXISTS u1 RESET PASSWORD");
18494-
verified_stmt("ALTER USER IF EXISTS u1 ABORT ALL QUERIES");
18495-
verified_stmt(
18494+
dialects.verified_stmt("ALTER USER IF EXISTS u1 RESET PASSWORD");
18495+
dialects.verified_stmt("ALTER USER IF EXISTS u1 ABORT ALL QUERIES");
18496+
dialects.verified_stmt(
1849618497
"ALTER USER IF EXISTS u1 ADD DELEGATED AUTHORIZATION OF ROLE r1 TO SECURITY INTEGRATION i1",
1849718498
);
18498-
verified_stmt("ALTER USER IF EXISTS u1 REMOVE DELEGATED AUTHORIZATION OF ROLE r1 FROM SECURITY INTEGRATION i1");
18499-
verified_stmt(
18499+
dialects.verified_stmt("ALTER USER IF EXISTS u1 REMOVE DELEGATED AUTHORIZATION OF ROLE r1 FROM SECURITY INTEGRATION i1");
18500+
dialects.verified_stmt(
1850018501
"ALTER USER IF EXISTS u1 REMOVE DELEGATED AUTHORIZATIONS FROM SECURITY INTEGRATION i1",
1850118502
);
18502-
verified_stmt("ALTER USER IF EXISTS u1 ENROLL MFA");
18503-
let stmt = verified_stmt("ALTER USER u1 SET DEFAULT_MFA_METHOD PASSKEY");
18503+
dialects.verified_stmt("ALTER USER IF EXISTS u1 ENROLL MFA");
18504+
let stmt = dialects.verified_stmt("ALTER USER u1 SET DEFAULT_MFA_METHOD PASSKEY");
1850418505
match stmt {
1850518506
Statement::AlterUser(alter) => {
1850618507
assert_eq!(alter.set_default_mfa_method, Some(MfaMethodKind::PassKey))
1850718508
}
1850818509
_ => unreachable!(),
1850918510
}
18510-
verified_stmt("ALTER USER u1 SET DEFAULT_MFA_METHOD TOTP");
18511-
verified_stmt("ALTER USER u1 SET DEFAULT_MFA_METHOD DUO");
18512-
let stmt = verified_stmt("ALTER USER u1 REMOVE MFA METHOD PASSKEY");
18511+
dialects.verified_stmt("ALTER USER u1 SET DEFAULT_MFA_METHOD TOTP");
18512+
dialects.verified_stmt("ALTER USER u1 SET DEFAULT_MFA_METHOD DUO");
18513+
let stmt = dialects.verified_stmt("ALTER USER u1 REMOVE MFA METHOD PASSKEY");
1851318514
match stmt {
1851418515
Statement::AlterUser(alter) => {
1851518516
assert_eq!(alter.remove_mfa_method, Some(MfaMethodKind::PassKey))
1851618517
}
1851718518
_ => unreachable!(),
1851818519
}
18519-
verified_stmt("ALTER USER u1 REMOVE MFA METHOD TOTP");
18520-
verified_stmt("ALTER USER u1 REMOVE MFA METHOD DUO");
18521-
let stmt = verified_stmt("ALTER USER u1 MODIFY MFA METHOD PASSKEY SET COMMENT 'abc'");
18520+
dialects.verified_stmt("ALTER USER u1 REMOVE MFA METHOD TOTP");
18521+
dialects.verified_stmt("ALTER USER u1 REMOVE MFA METHOD DUO");
18522+
let stmt = dialects.verified_stmt("ALTER USER u1 MODIFY MFA METHOD PASSKEY SET COMMENT 'abc'");
1852218523
match stmt {
1852318524
Statement::AlterUser(alter) => {
1852418525
assert_eq!(
@@ -18531,10 +18532,10 @@ fn test_parse_alter_user() {
1853118532
}
1853218533
_ => unreachable!(),
1853318534
}
18534-
verified_stmt("ALTER USER u1 ADD MFA METHOD OTP");
18535-
verified_stmt("ALTER USER u1 ADD MFA METHOD OTP COUNT = 8");
18535+
dialects.verified_stmt("ALTER USER u1 ADD MFA METHOD OTP");
18536+
dialects.verified_stmt("ALTER USER u1 ADD MFA METHOD OTP COUNT = 8");
1853618537

18537-
let stmt = verified_stmt("ALTER USER u1 SET AUTHENTICATION POLICY p1");
18538+
let stmt = dialects.verified_stmt("ALTER USER u1 SET AUTHENTICATION POLICY p1");
1853818539
match stmt {
1853918540
Statement::AlterUser(alter) => {
1854018541
assert_eq!(
@@ -18547,19 +18548,19 @@ fn test_parse_alter_user() {
1854718548
}
1854818549
_ => unreachable!(),
1854918550
}
18550-
verified_stmt("ALTER USER u1 SET PASSWORD POLICY p1");
18551-
verified_stmt("ALTER USER u1 SET SESSION POLICY p1");
18552-
let stmt = verified_stmt("ALTER USER u1 UNSET AUTHENTICATION POLICY");
18551+
dialects.verified_stmt("ALTER USER u1 SET PASSWORD POLICY p1");
18552+
dialects.verified_stmt("ALTER USER u1 SET SESSION POLICY p1");
18553+
let stmt = dialects.verified_stmt("ALTER USER u1 UNSET AUTHENTICATION POLICY");
1855318554
match stmt {
1855418555
Statement::AlterUser(alter) => {
1855518556
assert_eq!(alter.unset_policy, Some(UserPolicyKind::Authentication));
1855618557
}
1855718558
_ => unreachable!(),
1855818559
}
18559-
verified_stmt("ALTER USER u1 UNSET PASSWORD POLICY");
18560-
verified_stmt("ALTER USER u1 UNSET SESSION POLICY");
18560+
dialects.verified_stmt("ALTER USER u1 UNSET PASSWORD POLICY");
18561+
dialects.verified_stmt("ALTER USER u1 UNSET SESSION POLICY");
1856118562

18562-
let stmt = verified_stmt("ALTER USER u1 SET TAG k1='v1'");
18563+
let stmt = dialects.verified_stmt("ALTER USER u1 SET TAG k1='v1'");
1856318564
match stmt {
1856418565
Statement::AlterUser(alter) => {
1856518566
assert_eq!(
@@ -18574,23 +18575,25 @@ fn test_parse_alter_user() {
1857418575
}
1857518576
_ => unreachable!(),
1857618577
}
18577-
verified_stmt("ALTER USER u1 SET TAG k1='v1', k2='v2'");
18578-
let stmt = verified_stmt("ALTER USER u1 UNSET TAG k1");
18578+
dialects.verified_stmt("ALTER USER u1 SET TAG k1='v1', k2='v2'");
18579+
let stmt = dialects.verified_stmt("ALTER USER u1 UNSET TAG k1");
1857918580
match stmt {
1858018581
Statement::AlterUser(alter) => {
1858118582
assert_eq!(alter.unset_tag, vec!["k1".to_string()]);
1858218583
}
1858318584
_ => unreachable!(),
1858418585
}
18585-
verified_stmt("ALTER USER u1 UNSET TAG k1, k2, k3");
18586+
dialects.verified_stmt("ALTER USER u1 UNSET TAG k1, k2, k3");
1858618587

18587-
let dialects = all_dialects_where(|d| d.supports_boolean_literals());
18588-
dialects.one_statement_parses_to(
18588+
let bool_dialects = all_dialects_where(|d| {
18589+
d.supports_boolean_literals() && !d.supports_alter_user_as_alter_role()
18590+
});
18591+
bool_dialects.one_statement_parses_to(
1858918592
"ALTER USER u1 SET PASSWORD='secret', MUST_CHANGE_PASSWORD=TRUE, MINS_TO_UNLOCK=10",
1859018593
"ALTER USER u1 SET PASSWORD='secret', MUST_CHANGE_PASSWORD=true, MINS_TO_UNLOCK=10",
1859118594
);
1859218595

18593-
let stmt = dialects.verified_stmt(
18596+
let stmt = bool_dialects.verified_stmt(
1859418597
"ALTER USER u1 SET PASSWORD='secret', MUST_CHANGE_PASSWORD=true, MINS_TO_UNLOCK=10",
1859518598
);
1859618599
match stmt {
@@ -18625,16 +18628,16 @@ fn test_parse_alter_user() {
1862518628
_ => unreachable!(),
1862618629
}
1862718630

18628-
let stmt = verified_stmt("ALTER USER u1 UNSET PASSWORD");
18631+
let stmt = dialects.verified_stmt("ALTER USER u1 UNSET PASSWORD");
1862918632
match stmt {
1863018633
Statement::AlterUser(alter) => {
1863118634
assert_eq!(alter.unset_props, vec!["PASSWORD".to_string()]);
1863218635
}
1863318636
_ => unreachable!(),
1863418637
}
18635-
verified_stmt("ALTER USER u1 UNSET PASSWORD, MUST_CHANGE_PASSWORD, MINS_TO_UNLOCK");
18638+
dialects.verified_stmt("ALTER USER u1 UNSET PASSWORD, MUST_CHANGE_PASSWORD, MINS_TO_UNLOCK");
1863618639

18637-
let stmt = verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL')");
18640+
let stmt = dialects.verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL')");
1863818641
match stmt {
1863918642
Statement::AlterUser(alter) => {
1864018643
assert_eq!(
@@ -18650,11 +18653,11 @@ fn test_parse_alter_user() {
1865018653
}
1865118654
_ => unreachable!(),
1865218655
}
18653-
verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=()");
18654-
verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('R1', 'R2', 'R3')");
18655-
verified_stmt("ALTER USER u1 SET PASSWORD='secret', DEFAULT_SECONDARY_ROLES=('ALL')");
18656-
verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL'), PASSWORD='secret'");
18657-
let stmt = verified_stmt(
18656+
dialects.verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=()");
18657+
dialects.verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('R1', 'R2', 'R3')");
18658+
dialects.verified_stmt("ALTER USER u1 SET PASSWORD='secret', DEFAULT_SECONDARY_ROLES=('ALL')");
18659+
dialects.verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL'), PASSWORD='secret'");
18660+
let stmt = dialects.verified_stmt(
1865818661
"ALTER USER u1 SET WORKLOAD_IDENTITY=(TYPE=AWS, ARN='arn:aws:iam::123456789:r1/')",
1865918662
);
1866018663
match stmt {
@@ -18688,13 +18691,13 @@ fn test_parse_alter_user() {
1868818691
}
1868918692
_ => unreachable!(),
1869018693
}
18691-
verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL'), PASSWORD='secret', WORKLOAD_IDENTITY=(TYPE=AWS, ARN='arn:aws:iam::123456789:r1/')");
18694+
dialects.verified_stmt("ALTER USER u1 SET DEFAULT_SECONDARY_ROLES=('ALL'), PASSWORD='secret', WORKLOAD_IDENTITY=(TYPE=AWS, ARN='arn:aws:iam::123456789:r1/')");
1869218695

18693-
verified_stmt("ALTER USER u1 PASSWORD 'AAA'");
18694-
verified_stmt("ALTER USER u1 ENCRYPTED PASSWORD 'AAA'");
18695-
verified_stmt("ALTER USER u1 PASSWORD NULL");
18696+
dialects.verified_stmt("ALTER USER u1 PASSWORD 'AAA'");
18697+
dialects.verified_stmt("ALTER USER u1 ENCRYPTED PASSWORD 'AAA'");
18698+
dialects.verified_stmt("ALTER USER u1 PASSWORD NULL");
1869618699

18697-
one_statement_parses_to(
18700+
dialects.one_statement_parses_to(
1869818701
"ALTER USER u1 WITH PASSWORD 'AAA'",
1869918702
"ALTER USER u1 PASSWORD 'AAA'",
1870018703
);

tests/sqlparser_postgres.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4546,6 +4546,56 @@ fn parse_alter_role() {
45464546
);
45474547
}
45484548

4549+
#[test]
4550+
fn parse_alter_user() {
4551+
// `ALTER USER` is a PostgreSQL synonym for `ALTER ROLE`, so it round-trips to `ALTER ROLE`.
4552+
let canonical = "ALTER ROLE old_name RENAME TO new_name";
4553+
assert_eq!(
4554+
pg().one_statement_parses_to("ALTER USER old_name RENAME TO new_name", canonical),
4555+
Statement::AlterRole {
4556+
name: Ident::new("old_name"),
4557+
operation: AlterRoleOperation::RenameRole {
4558+
role_name: Ident::new("new_name"),
4559+
},
4560+
}
4561+
);
4562+
4563+
let canonical = "ALTER ROLE bob WITH SUPERUSER PASSWORD 'x' CONNECTION LIMIT 5";
4564+
assert_eq!(
4565+
pg().one_statement_parses_to(
4566+
"ALTER USER bob WITH SUPERUSER PASSWORD 'x' CONNECTION LIMIT 5",
4567+
canonical
4568+
),
4569+
Statement::AlterRole {
4570+
name: Ident::new("bob"),
4571+
operation: AlterRoleOperation::WithOptions {
4572+
options: vec![
4573+
RoleOption::SuperUser(true),
4574+
RoleOption::Password(Password::Password(Expr::Value(
4575+
Value::SingleQuotedString("x".into()).with_empty_span()
4576+
))),
4577+
RoleOption::ConnectionLimit(Expr::value(number("5"))),
4578+
]
4579+
},
4580+
}
4581+
);
4582+
4583+
assert_eq!(
4584+
pg().one_statement_parses_to(
4585+
"ALTER USER bob SET search_path TO public",
4586+
"ALTER ROLE bob SET search_path TO public"
4587+
),
4588+
Statement::AlterRole {
4589+
name: Ident::new("bob"),
4590+
operation: AlterRoleOperation::Set {
4591+
config_name: ObjectName::from(vec![Ident::new("search_path")]),
4592+
config_value: SetConfigValue::Value(Expr::Identifier(Ident::new("public"))),
4593+
in_database: None,
4594+
},
4595+
}
4596+
);
4597+
}
4598+
45494599
#[test]
45504600
fn parse_delimited_identifiers() {
45514601
// check that quoted identifiers in any position remain quoted after serialization

0 commit comments

Comments
 (0)