Skip to content

Commit 72c56b7

Browse files
Support PostgreSQL ALTER DEFAULT PRIVILEGES
1 parent 2f3b5b8 commit 72c56b7

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

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::{
@@ -4587,6 +4588,10 @@ pub enum Statement {
45874588
/// ```
45884589
Revoke(Revoke),
45894590
/// ```sql
4591+
/// ALTER DEFAULT PRIVILEGES
4592+
/// ```
4593+
AlterDefaultPrivileges(AlterDefaultPrivileges),
4594+
/// ```sql
45904595
/// DEALLOCATE [ PREPARE ] { name | ALL }
45914596
/// ```
45924597
///
@@ -6090,6 +6095,7 @@ impl fmt::Display for Statement {
60906095
Statement::Grant(grant) => write!(f, "{grant}"),
60916096
Statement::Deny(s) => write!(f, "{s}"),
60926097
Statement::Revoke(revoke) => write!(f, "{revoke}"),
6098+
Statement::AlterDefaultPrivileges(stmt) => write!(f, "{stmt}"),
60936099
Statement::Deallocate { name, prepare } => write!(
60946100
f,
60956101
"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
@@ -908,6 +908,7 @@ define_keywords!(
908908
ROLLBACK,
909909
ROLLUP,
910910
ROOT,
911+
ROUTINES,
911912
ROW,
912913
ROWGROUPSIZE,
913914
ROWID,
@@ -1095,6 +1096,7 @@ define_keywords!(
10951096
TSVECTOR,
10961097
TUPLE,
10971098
TYPE,
1099+
TYPES,
10981100
TYPMOD_IN,
10991101
TYPMOD_OUT,
11001102
UBIGINT,

src/parser/mod.rs

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

11144+
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::PRIVILEGES]) {
11145+
return self.parse_alter_default_privileges().map(Into::into);
11146+
}
11147+
1114411148
let object_type = self.expect_one_of_keywords(&[
1114511149
Keyword::VIEW,
1114611150
Keyword::TYPE,
@@ -17793,14 +17797,7 @@ impl<'a> Parser<'a> {
1779317797
pub fn parse_grant_deny_revoke_privileges_objects(
1779417798
&mut self,
1779517799
) -> Result<(Privileges, Option<GrantObjects>), ParserError> {
17796-
let privileges = if self.parse_keyword(Keyword::ALL) {
17797-
Privileges::All {
17798-
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
17799-
}
17800-
} else {
17801-
let actions = self.parse_actions_list()?;
17802-
Privileges::Actions(actions)
17803-
};
17800+
let privileges = self.parse_privileges()?;
1780417801

1780517802
let objects = if self.parse_keyword(Keyword::ON) {
1780617803
if self.parse_keywords(&[Keyword::ALL, Keyword::TABLES, Keyword::IN, Keyword::SCHEMA]) {
@@ -18328,6 +18325,102 @@ impl<'a> Parser<'a> {
1832818325
})
1832918326
}
1833018327

18328+
/// Parse `ALTER DEFAULT PRIVILEGES`, whose keywords are already consumed.
18329+
///
18330+
/// See [PostgreSQL docs](https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html).
18331+
pub fn parse_alter_default_privileges(
18332+
&mut self,
18333+
) -> Result<AlterDefaultPrivileges, ParserError> {
18334+
// `FOR USER` is a synonym of `FOR ROLE` and is normalised to it.
18335+
let for_roles = if self.parse_keyword(Keyword::FOR) {
18336+
self.expect_one_of_keywords(&[Keyword::ROLE, Keyword::USER])?;
18337+
self.parse_comma_separated(|p| p.parse_identifier())?
18338+
} else {
18339+
vec![]
18340+
};
18341+
18342+
let in_schemas = if self.parse_keywords(&[Keyword::IN, Keyword::SCHEMA]) {
18343+
self.parse_comma_separated(|p| p.parse_object_name(false))?
18344+
} else {
18345+
vec![]
18346+
};
18347+
18348+
let is_grant = if self.parse_keyword(Keyword::GRANT) {
18349+
true
18350+
} else if self.parse_keyword(Keyword::REVOKE) {
18351+
false
18352+
} else {
18353+
return self.expected_ref("GRANT or REVOKE", self.peek_token_ref());
18354+
};
18355+
18356+
let grant_option_for =
18357+
!is_grant && self.parse_keywords(&[Keyword::GRANT, Keyword::OPTION, Keyword::FOR]);
18358+
let privileges = self.parse_privileges()?;
18359+
self.expect_keyword_is(Keyword::ON)?;
18360+
let object_type = self.parse_default_privileges_object_type()?;
18361+
self.expect_keyword_is(if is_grant { Keyword::TO } else { Keyword::FROM })?;
18362+
let grantees = self.parse_grantees()?;
18363+
18364+
let operation = if is_grant {
18365+
AlterDefaultPrivilegesOperation::Grant {
18366+
privileges,
18367+
object_type,
18368+
grantees,
18369+
with_grant_option: self.parse_keywords(&[
18370+
Keyword::WITH,
18371+
Keyword::GRANT,
18372+
Keyword::OPTION,
18373+
]),
18374+
}
18375+
} else {
18376+
AlterDefaultPrivilegesOperation::Revoke {
18377+
grant_option_for,
18378+
privileges,
18379+
object_type,
18380+
grantees,
18381+
cascade: self.parse_cascade_option(),
18382+
}
18383+
};
18384+
18385+
Ok(AlterDefaultPrivileges {
18386+
for_roles,
18387+
in_schemas,
18388+
operation,
18389+
})
18390+
}
18391+
18392+
/// Parse `ALL [PRIVILEGES]` or a comma separated privilege list.
18393+
fn parse_privileges(&mut self) -> Result<Privileges, ParserError> {
18394+
if self.parse_keyword(Keyword::ALL) {
18395+
Ok(Privileges::All {
18396+
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
18397+
})
18398+
} else {
18399+
Ok(Privileges::Actions(self.parse_actions_list()?))
18400+
}
18401+
}
18402+
18403+
fn parse_default_privileges_object_type(
18404+
&mut self,
18405+
) -> Result<DefaultPrivilegesObjectType, ParserError> {
18406+
for (keyword, object_type) in [
18407+
(Keyword::TABLES, DefaultPrivilegesObjectType::Tables),
18408+
(Keyword::SEQUENCES, DefaultPrivilegesObjectType::Sequences),
18409+
(Keyword::FUNCTIONS, DefaultPrivilegesObjectType::Functions),
18410+
(Keyword::ROUTINES, DefaultPrivilegesObjectType::Routines),
18411+
(Keyword::TYPES, DefaultPrivilegesObjectType::Types),
18412+
(Keyword::SCHEMAS, DefaultPrivilegesObjectType::Schemas),
18413+
] {
18414+
if self.parse_keyword(keyword) {
18415+
return Ok(object_type);
18416+
}
18417+
}
18418+
self.expected_ref(
18419+
"TABLES, SEQUENCES, FUNCTIONS, ROUTINES, TYPES or SCHEMAS",
18420+
self.peek_token_ref(),
18421+
)
18422+
}
18423+
1833118424
/// Parse an REPLACE statement
1833218425
pub fn parse_replace(
1833318426
&mut self,

tests/sqlparser_postgres.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9663,3 +9663,33 @@ fn parse_right_deep_join_chain() {
96639663
// NATURAL JOIN followed by a constrained join must stay left-associative.
96649664
pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true");
96659665
}
9666+
9667+
#[test]
9668+
fn parse_alter_default_privileges() {
9669+
// What `pg_dump -s` emits for a default ACL.
9670+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT ON TABLES TO app_user");
9671+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO app");
9672+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES FOR ROLE a, b IN SCHEMA s1, s2 GRANT SELECT, INSERT ON TABLES TO app WITH GRANT OPTION");
9673+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT ALL PRIVILEGES ON SEQUENCES TO app");
9674+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO app");
9675+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON ROUTINES TO app");
9676+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT USAGE ON TYPES TO app");
9677+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT USAGE, CREATE ON SCHEMAS TO app");
9678+
pg().verified_stmt("ALTER DEFAULT PRIVILEGES REVOKE SELECT ON TABLES FROM app");
9679+
pg().verified_stmt(
9680+
"ALTER DEFAULT PRIVILEGES REVOKE GRANT OPTION FOR ALL ON TABLES FROM app CASCADE",
9681+
);
9682+
// `FOR USER` normalises to `FOR ROLE`.
9683+
pg().one_statement_parses_to(
9684+
"ALTER DEFAULT PRIVILEGES FOR USER bob GRANT SELECT ON TABLES TO app",
9685+
"ALTER DEFAULT PRIVILEGES FOR ROLE bob GRANT SELECT ON TABLES TO app",
9686+
);
9687+
9688+
// `TABLES` is a keyword only here, so a table named `tables` still parses.
9689+
pg().verified_stmt("GRANT SELECT ON TABLES TO app");
9690+
assert_eq!(
9691+
pg().parse_sql_statements("ALTER DEFAULT PRIVILEGES FOR ROLE a")
9692+
.unwrap_err(),
9693+
ParserError::ParserError("Expected: GRANT or REVOKE, found: EOF".to_string()),
9694+
);
9695+
}

0 commit comments

Comments
 (0)