Skip to content

Commit 1547f09

Browse files
Support PostgreSQL ALTER DEFAULT PRIVILEGES
1 parent f354ee2 commit 1547f09

6 files changed

Lines changed: 288 additions & 9 deletions

File tree

‎src/ast/dcl.rs‎

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,3 +535,149 @@ impl From<Revoke> for crate::ast::Statement {
535535
crate::ast::Statement::Revoke(v)
536536
}
537537
}
538+
539+
/// `ALTER DEFAULT PRIVILEGES`, which applies to objects created later rather
540+
/// than to existing ones.
541+
///
542+
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html)
543+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
544+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
545+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
546+
pub struct AlterDefaultPrivileges {
547+
/// `FOR ROLE <role> [, ...]`, empty when the clause is absent.
548+
pub for_roles: Vec<Ident>,
549+
/// `IN SCHEMA <schema> [, ...]`, empty when the clause is absent.
550+
pub in_schemas: Vec<ObjectName>,
551+
/// The abbreviated `GRANT` or `REVOKE` that follows.
552+
pub operation: AlterDefaultPrivilegesOperation,
553+
}
554+
555+
impl fmt::Display for AlterDefaultPrivileges {
556+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
557+
f.write_str("ALTER DEFAULT PRIVILEGES")?;
558+
if !self.for_roles.is_empty() {
559+
write!(f, " FOR ROLE {}", display_comma_separated(&self.for_roles))?;
560+
}
561+
if !self.in_schemas.is_empty() {
562+
write!(
563+
f,
564+
" IN SCHEMA {}",
565+
display_comma_separated(&self.in_schemas)
566+
)?;
567+
}
568+
write!(f, " {}", self.operation)
569+
}
570+
}
571+
572+
impl From<AlterDefaultPrivileges> for crate::ast::Statement {
573+
fn from(v: AlterDefaultPrivileges) -> Self {
574+
crate::ast::Statement::AlterDefaultPrivileges(v)
575+
}
576+
}
577+
578+
/// The abbreviated `GRANT` or `REVOKE` of an [`AlterDefaultPrivileges`], naming
579+
/// an object type rather than named objects and so unable to reuse [`Grant`].
580+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
581+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
582+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
583+
pub enum AlterDefaultPrivilegesOperation {
584+
/// `GRANT <privileges> ON <object_type> TO <grantees> [WITH GRANT OPTION]`
585+
Grant {
586+
/// The privileges being granted.
587+
privileges: Privileges,
588+
/// The kind of future object the privileges apply to.
589+
object_type: DefaultPrivilegesObjectType,
590+
/// The roles receiving the privileges.
591+
grantees: Vec<Grantee>,
592+
/// Whether `WITH GRANT OPTION` is present.
593+
with_grant_option: bool,
594+
},
595+
/// `REVOKE [GRANT OPTION FOR] <privileges> ON <object_type> FROM <grantees> [CASCADE | RESTRICT]`
596+
Revoke {
597+
/// Whether `GRANT OPTION FOR` is present.
598+
grant_option_for: bool,
599+
/// The privileges being revoked.
600+
privileges: Privileges,
601+
/// The kind of future object the privileges apply to.
602+
object_type: DefaultPrivilegesObjectType,
603+
/// The roles losing the privileges.
604+
grantees: Vec<Grantee>,
605+
/// Optional `CASCADE`/`RESTRICT` behaviour.
606+
cascade: Option<CascadeOption>,
607+
},
608+
}
609+
610+
impl fmt::Display for AlterDefaultPrivilegesOperation {
611+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
612+
match self {
613+
AlterDefaultPrivilegesOperation::Grant {
614+
privileges,
615+
object_type,
616+
grantees,
617+
with_grant_option,
618+
} => {
619+
write!(
620+
f,
621+
"GRANT {privileges} ON {object_type} TO {}",
622+
display_comma_separated(grantees)
623+
)?;
624+
if *with_grant_option {
625+
f.write_str(" WITH GRANT OPTION")?;
626+
}
627+
}
628+
AlterDefaultPrivilegesOperation::Revoke {
629+
grant_option_for,
630+
privileges,
631+
object_type,
632+
grantees,
633+
cascade,
634+
} => {
635+
f.write_str("REVOKE ")?;
636+
if *grant_option_for {
637+
f.write_str("GRANT OPTION FOR ")?;
638+
}
639+
write!(
640+
f,
641+
"{privileges} ON {object_type} FROM {}",
642+
display_comma_separated(grantees)
643+
)?;
644+
if let Some(cascade) = cascade {
645+
write!(f, " {cascade}")?;
646+
}
647+
}
648+
}
649+
Ok(())
650+
}
651+
}
652+
653+
/// The kind of future object an [`AlterDefaultPrivileges`] applies to.
654+
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
655+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
656+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
657+
pub enum DefaultPrivilegesObjectType {
658+
/// `TABLES`, which also covers views and foreign tables.
659+
Tables,
660+
/// `SEQUENCES`
661+
Sequences,
662+
/// `FUNCTIONS`
663+
Functions,
664+
/// `ROUTINES`, a synonym of `FUNCTIONS`.
665+
Routines,
666+
/// `TYPES`
667+
Types,
668+
/// `SCHEMAS`
669+
Schemas,
670+
}
671+
672+
impl fmt::Display for DefaultPrivilegesObjectType {
673+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
674+
f.write_str(match self {
675+
DefaultPrivilegesObjectType::Tables => "TABLES",
676+
DefaultPrivilegesObjectType::Sequences => "SEQUENCES",
677+
DefaultPrivilegesObjectType::Functions => "FUNCTIONS",
678+
DefaultPrivilegesObjectType::Routines => "ROUTINES",
679+
DefaultPrivilegesObjectType::Types => "TYPES",
680+
DefaultPrivilegesObjectType::Schemas => "SCHEMAS",
681+
})
682+
}
683+
}

‎src/ast/mod.rs‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ pub use self::data_type::{
5858
ExactNumberInfo, IntervalFields, MapBracketKind, StructBracketKind, TimezoneInfo,
5959
};
6060
pub use self::dcl::{
61-
AlterRoleOperation, CreateRole, Grant, ResetConfig, Revoke, RoleOption, SecondaryRoles,
61+
AlterDefaultPrivileges, AlterDefaultPrivilegesOperation, AlterRoleOperation, CreateRole,
62+
DefaultPrivilegesObjectType, Grant, ResetConfig, Revoke, RoleOption, SecondaryRoles,
6263
SetConfigValue, Use,
6364
};
6465
pub use self::ddl::{
@@ -4615,6 +4616,10 @@ pub enum Statement {
46154616
/// ```
46164617
Revoke(Revoke),
46174618
/// ```sql
4619+
/// ALTER DEFAULT PRIVILEGES
4620+
/// ```
4621+
AlterDefaultPrivileges(AlterDefaultPrivileges),
4622+
/// ```sql
46184623
/// DEALLOCATE [ PREPARE ] { name | ALL }
46194624
/// ```
46204625
///
@@ -6118,6 +6123,7 @@ impl fmt::Display for Statement {
61186123
Statement::Grant(grant) => write!(f, "{grant}"),
61196124
Statement::Deny(s) => write!(f, "{s}"),
61206125
Statement::Revoke(revoke) => write!(f, "{revoke}"),
6126+
Statement::AlterDefaultPrivileges(stmt) => write!(f, "{stmt}"),
61216127
Statement::Deallocate { name, prepare } => write!(
61226128
f,
61236129
"DEALLOCATE {prepare}{name}",

‎src/ast/spans.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ impl Spanned for Values {
301301
/// - [Statement::Assert]
302302
/// - [Statement::Grant]
303303
/// - [Statement::Revoke]
304+
/// - [Statement::AlterDefaultPrivileges]
304305
/// - [Statement::Deallocate]
305306
/// - [Statement::Execute]
306307
/// - [Statement::Prepare]
@@ -466,6 +467,7 @@ impl Spanned for Statement {
466467
Statement::Grant { .. } => Span::empty(),
467468
Statement::Deny { .. } => Span::empty(),
468469
Statement::Revoke { .. } => Span::empty(),
470+
Statement::AlterDefaultPrivileges { .. } => Span::empty(),
469471
Statement::Deallocate { .. } => Span::empty(),
470472
Statement::Execute { .. } => Span::empty(),
471473
Statement::Prepare { .. } => Span::empty(),

‎src/keywords.rs‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,7 @@ define_keywords!(
909909
ROLLBACK,
910910
ROLLUP,
911911
ROOT,
912+
ROUTINES,
912913
ROW,
913914
ROWGROUPSIZE,
914915
ROWID,
@@ -1096,6 +1097,7 @@ define_keywords!(
10961097
TSVECTOR,
10971098
TUPLE,
10981099
TYPE,
1100+
TYPES,
10991101
TYPMOD_IN,
11001102
TYPMOD_OUT,
11011103
UBIGINT,

‎src/parser/mod.rs‎

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11181,6 +11181,10 @@ impl<'a> Parser<'a> {
1118111181
return self.parse_alter_text_search().map(Into::into);
1118211182
}
1118311183

11184+
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::PRIVILEGES]) {
11185+
return self.parse_alter_default_privileges().map(Into::into);
11186+
}
11187+
1118411188
let object_type = self.expect_one_of_keywords(&[
1118511189
Keyword::VIEW,
1118611190
Keyword::TYPE,
@@ -17838,14 +17842,7 @@ impl<'a> Parser<'a> {
1783817842
pub fn parse_grant_deny_revoke_privileges_objects(
1783917843
&mut self,
1784017844
) -> Result<(Privileges, Option<GrantObjects>), ParserError> {
17841-
let privileges = if self.parse_keyword(Keyword::ALL) {
17842-
Privileges::All {
17843-
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
17844-
}
17845-
} else {
17846-
let actions = self.parse_actions_list()?;
17847-
Privileges::Actions(actions)
17848-
};
17845+
let privileges = self.parse_privileges()?;
1784917846

1785017847
let objects = if self.parse_keyword(Keyword::ON) {
1785117848
if self.parse_keywords(&[Keyword::ALL, Keyword::TABLES, Keyword::IN, Keyword::SCHEMA]) {
@@ -18377,6 +18374,102 @@ impl<'a> Parser<'a> {
1837718374
})
1837818375
}
1837918376

18377+
/// Parse `ALTER DEFAULT PRIVILEGES`, whose keywords are already consumed.
18378+
///
18379+
/// See [PostgreSQL docs](https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html).
18380+
pub fn parse_alter_default_privileges(
18381+
&mut self,
18382+
) -> Result<AlterDefaultPrivileges, ParserError> {
18383+
// `FOR USER` is a synonym of `FOR ROLE` and is normalised to it.
18384+
let for_roles = if self.parse_keyword(Keyword::FOR) {
18385+
self.expect_one_of_keywords(&[Keyword::ROLE, Keyword::USER])?;
18386+
self.parse_comma_separated(|p| p.parse_identifier())?
18387+
} else {
18388+
vec![]
18389+
};
18390+
18391+
let in_schemas = if self.parse_keywords(&[Keyword::IN, Keyword::SCHEMA]) {
18392+
self.parse_comma_separated(|p| p.parse_object_name(false))?
18393+
} else {
18394+
vec![]
18395+
};
18396+
18397+
let is_grant = if self.parse_keyword(Keyword::GRANT) {
18398+
true
18399+
} else if self.parse_keyword(Keyword::REVOKE) {
18400+
false
18401+
} else {
18402+
return self.expected_ref("GRANT or REVOKE", self.peek_token_ref());
18403+
};
18404+
18405+
let grant_option_for =
18406+
!is_grant && self.parse_keywords(&[Keyword::GRANT, Keyword::OPTION, Keyword::FOR]);
18407+
let privileges = self.parse_privileges()?;
18408+
self.expect_keyword_is(Keyword::ON)?;
18409+
let object_type = self.parse_default_privileges_object_type()?;
18410+
self.expect_keyword_is(if is_grant { Keyword::TO } else { Keyword::FROM })?;
18411+
let grantees = self.parse_grantees()?;
18412+
18413+
let operation = if is_grant {
18414+
AlterDefaultPrivilegesOperation::Grant {
18415+
privileges,
18416+
object_type,
18417+
grantees,
18418+
with_grant_option: self.parse_keywords(&[
18419+
Keyword::WITH,
18420+
Keyword::GRANT,
18421+
Keyword::OPTION,
18422+
]),
18423+
}
18424+
} else {
18425+
AlterDefaultPrivilegesOperation::Revoke {
18426+
grant_option_for,
18427+
privileges,
18428+
object_type,
18429+
grantees,
18430+
cascade: self.parse_cascade_option(),
18431+
}
18432+
};
18433+
18434+
Ok(AlterDefaultPrivileges {
18435+
for_roles,
18436+
in_schemas,
18437+
operation,
18438+
})
18439+
}
18440+
18441+
/// Parse `ALL [PRIVILEGES]` or a comma separated privilege list.
18442+
fn parse_privileges(&mut self) -> Result<Privileges, ParserError> {
18443+
if self.parse_keyword(Keyword::ALL) {
18444+
Ok(Privileges::All {
18445+
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
18446+
})
18447+
} else {
18448+
Ok(Privileges::Actions(self.parse_actions_list()?))
18449+
}
18450+
}
18451+
18452+
fn parse_default_privileges_object_type(
18453+
&mut self,
18454+
) -> Result<DefaultPrivilegesObjectType, ParserError> {
18455+
for (keyword, object_type) in [
18456+
(Keyword::TABLES, DefaultPrivilegesObjectType::Tables),
18457+
(Keyword::SEQUENCES, DefaultPrivilegesObjectType::Sequences),
18458+
(Keyword::FUNCTIONS, DefaultPrivilegesObjectType::Functions),
18459+
(Keyword::ROUTINES, DefaultPrivilegesObjectType::Routines),
18460+
(Keyword::TYPES, DefaultPrivilegesObjectType::Types),
18461+
(Keyword::SCHEMAS, DefaultPrivilegesObjectType::Schemas),
18462+
] {
18463+
if self.parse_keyword(keyword) {
18464+
return Ok(object_type);
18465+
}
18466+
}
18467+
self.expected_ref(
18468+
"TABLES, SEQUENCES, FUNCTIONS, ROUTINES, TYPES or SCHEMAS",
18469+
self.peek_token_ref(),
18470+
)
18471+
}
18472+
1838018473
/// Parse an REPLACE statement
1838118474
pub fn parse_replace(
1838218475
&mut self,

‎tests/sqlparser_postgres.rs‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10031,3 +10031,33 @@ fn parse_stage_table_factor_rejected() {
1003110031
ParserError::ParserError("Expected: identifier, found: @".to_string()),
1003210032
);
1003310033
}
10034+
10035+
#[test]
10036+
fn parse_alter_default_privileges() {
10037+
// What `pg_dump -s` emits for a default ACL.
10038+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT ON TABLES TO app_user");
10039+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO app");
10040+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES FOR ROLE a, b IN SCHEMA s1, s2 GRANT SELECT, INSERT ON TABLES TO app WITH GRANT OPTION");
10041+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT ALL PRIVILEGES ON SEQUENCES TO app");
10042+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO app");
10043+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON ROUTINES TO app");
10044+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT USAGE ON TYPES TO app");
10045+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT USAGE, CREATE ON SCHEMAS TO app");
10046+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES REVOKE SELECT ON TABLES FROM app");
10047+
pg().verified_stmt(
10048+
"ALTER DEFAULT PRIVILEGES REVOKE GRANT OPTION FOR ALL ON TABLES FROM app CASCADE",
10049+
);
10050+
// `FOR USER` normalises to `FOR ROLE`.
10051+
pg().one_statement_parses_to(
10052+
"ALTER DEFAULT PRIVILEGES FOR USER bob GRANT SELECT ON TABLES TO app",
10053+
"ALTER DEFAULT PRIVILEGES FOR ROLE bob GRANT SELECT ON TABLES TO app",
10054+
);
10055+
10056+
// `TABLES` is a keyword only here, so a table named `tables` still parses.
10057+
pg().verified_stmt("GRANT SELECT ON TABLES TO app");
10058+
assert_eq!(
10059+
pg().parse_sql_statements("ALTER DEFAULT PRIVILEGES FOR ROLE a")
10060+
.unwrap_err(),
10061+
ParserError::ParserError("Expected: GRANT or REVOKE, found: EOF".to_string()),
10062+
);
10063+
}

0 commit comments

Comments
 (0)