Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions src/ast/dcl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -526,3 +526,149 @@ impl From<Revoke> for crate::ast::Statement {
crate::ast::Statement::Revoke(v)
}
}

/// `ALTER DEFAULT PRIVILEGES`, which applies to objects created later rather
/// than to existing ones.
///
/// [PostgreSQL](https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterDefaultPrivileges {
/// `FOR ROLE <role> [, ...]`, empty when the clause is absent.
pub for_roles: Vec<Ident>,
/// `IN SCHEMA <schema> [, ...]`, empty when the clause is absent.
pub in_schemas: Vec<ObjectName>,
/// The abbreviated `GRANT` or `REVOKE` that follows.
pub operation: AlterDefaultPrivilegesOperation,
}

impl fmt::Display for AlterDefaultPrivileges {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("ALTER DEFAULT PRIVILEGES")?;
if !self.for_roles.is_empty() {
write!(f, " FOR ROLE {}", display_comma_separated(&self.for_roles))?;
}
if !self.in_schemas.is_empty() {
write!(
f,
" IN SCHEMA {}",
display_comma_separated(&self.in_schemas)
)?;
}
write!(f, " {}", self.operation)
}
}

impl From<AlterDefaultPrivileges> for crate::ast::Statement {
fn from(v: AlterDefaultPrivileges) -> Self {
crate::ast::Statement::AlterDefaultPrivileges(v)
}
}

/// The abbreviated `GRANT` or `REVOKE` of an [`AlterDefaultPrivileges`], naming
/// an object type rather than named objects and so unable to reuse [`Grant`].
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterDefaultPrivilegesOperation {
/// `GRANT <privileges> ON <object_type> TO <grantees> [WITH GRANT OPTION]`
Grant {
/// The privileges being granted.
privileges: Privileges,
/// The kind of future object the privileges apply to.
object_type: DefaultPrivilegesObjectType,
/// The roles receiving the privileges.
grantees: Vec<Grantee>,
/// Whether `WITH GRANT OPTION` is present.
with_grant_option: bool,
},
/// `REVOKE [GRANT OPTION FOR] <privileges> ON <object_type> FROM <grantees> [CASCADE | RESTRICT]`
Revoke {
/// Whether `GRANT OPTION FOR` is present.
grant_option_for: bool,
/// The privileges being revoked.
privileges: Privileges,
/// The kind of future object the privileges apply to.
object_type: DefaultPrivilegesObjectType,
/// The roles losing the privileges.
grantees: Vec<Grantee>,
/// Optional `CASCADE`/`RESTRICT` behaviour.
cascade: Option<CascadeOption>,
},
}

impl fmt::Display for AlterDefaultPrivilegesOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterDefaultPrivilegesOperation::Grant {
privileges,
object_type,
grantees,
with_grant_option,
} => {
write!(
f,
"GRANT {privileges} ON {object_type} TO {}",
display_comma_separated(grantees)
)?;
if *with_grant_option {
f.write_str(" WITH GRANT OPTION")?;
}
}
AlterDefaultPrivilegesOperation::Revoke {
grant_option_for,
privileges,
object_type,
grantees,
cascade,
} => {
f.write_str("REVOKE ")?;
if *grant_option_for {
f.write_str("GRANT OPTION FOR ")?;
}
write!(
f,
"{privileges} ON {object_type} FROM {}",
display_comma_separated(grantees)
)?;
if let Some(cascade) = cascade {
write!(f, " {cascade}")?;
}
}
}
Ok(())
}
}

/// The kind of future object an [`AlterDefaultPrivileges`] applies to.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum DefaultPrivilegesObjectType {
/// `TABLES`, which also covers views and foreign tables.
Tables,
/// `SEQUENCES`
Sequences,
/// `FUNCTIONS`
Functions,
/// `ROUTINES`, a synonym of `FUNCTIONS`.
Routines,
/// `TYPES`
Types,
/// `SCHEMAS`
Schemas,
}

impl fmt::Display for DefaultPrivilegesObjectType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
DefaultPrivilegesObjectType::Tables => "TABLES",
DefaultPrivilegesObjectType::Sequences => "SEQUENCES",
DefaultPrivilegesObjectType::Functions => "FUNCTIONS",
DefaultPrivilegesObjectType::Routines => "ROUTINES",
DefaultPrivilegesObjectType::Types => "TYPES",
DefaultPrivilegesObjectType::Schemas => "SCHEMAS",
})
}
}
8 changes: 7 additions & 1 deletion src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ pub use self::data_type::{
ExactNumberInfo, IntervalFields, MapBracketKind, StructBracketKind, TimezoneInfo,
};
pub use self::dcl::{
AlterRoleOperation, CreateRole, Grant, ResetConfig, Revoke, RoleOption, SecondaryRoles,
AlterDefaultPrivileges, AlterDefaultPrivilegesOperation, AlterRoleOperation, CreateRole,
DefaultPrivilegesObjectType, Grant, ResetConfig, Revoke, RoleOption, SecondaryRoles,
SetConfigValue, Use,
};
pub use self::ddl::{
Expand Down Expand Up @@ -4587,6 +4588,10 @@ pub enum Statement {
/// ```
Revoke(Revoke),
/// ```sql
/// ALTER DEFAULT PRIVILEGES
/// ```
AlterDefaultPrivileges(AlterDefaultPrivileges),
/// ```sql
/// DEALLOCATE [ PREPARE ] { name | ALL }
/// ```
///
Expand Down Expand Up @@ -6090,6 +6095,7 @@ impl fmt::Display for Statement {
Statement::Grant(grant) => write!(f, "{grant}"),
Statement::Deny(s) => write!(f, "{s}"),
Statement::Revoke(revoke) => write!(f, "{revoke}"),
Statement::AlterDefaultPrivileges(stmt) => write!(f, "{stmt}"),
Statement::Deallocate { name, prepare } => write!(
f,
"DEALLOCATE {prepare}{name}",
Expand Down
2 changes: 2 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ impl Spanned for Values {
/// - [Statement::Assert]
/// - [Statement::Grant]
/// - [Statement::Revoke]
/// - [Statement::AlterDefaultPrivileges]
/// - [Statement::Deallocate]
/// - [Statement::Execute]
/// - [Statement::Prepare]
Expand Down Expand Up @@ -466,6 +467,7 @@ impl Spanned for Statement {
Statement::Grant { .. } => Span::empty(),
Statement::Deny { .. } => Span::empty(),
Statement::Revoke { .. } => Span::empty(),
Statement::AlterDefaultPrivileges { .. } => Span::empty(),
Statement::Deallocate { .. } => Span::empty(),
Statement::Execute { .. } => Span::empty(),
Statement::Prepare { .. } => Span::empty(),
Expand Down
2 changes: 2 additions & 0 deletions src/keywords.rs
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,7 @@ define_keywords!(
ROLLBACK,
ROLLUP,
ROOT,
ROUTINES,
ROW,
ROWGROUPSIZE,
ROWID,
Expand Down Expand Up @@ -1095,6 +1096,7 @@ define_keywords!(
TSVECTOR,
TUPLE,
TYPE,
TYPES,
TYPMOD_IN,
TYPMOD_OUT,
UBIGINT,
Expand Down
109 changes: 101 additions & 8 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11141,6 +11141,10 @@ impl<'a> Parser<'a> {
return self.parse_alter_text_search().map(Into::into);
}

if self.parse_keywords(&[Keyword::DEFAULT, Keyword::PRIVILEGES]) {
return self.parse_alter_default_privileges().map(Into::into);
}

let object_type = self.expect_one_of_keywords(&[
Keyword::VIEW,
Keyword::TYPE,
Expand Down Expand Up @@ -17793,14 +17797,7 @@ impl<'a> Parser<'a> {
pub fn parse_grant_deny_revoke_privileges_objects(
&mut self,
) -> Result<(Privileges, Option<GrantObjects>), ParserError> {
let privileges = if self.parse_keyword(Keyword::ALL) {
Privileges::All {
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
}
} else {
let actions = self.parse_actions_list()?;
Privileges::Actions(actions)
};
let privileges = self.parse_privileges()?;

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

/// Parse `ALTER DEFAULT PRIVILEGES`, whose keywords are already consumed.
///
/// See [PostgreSQL docs](https://www.postgresql.org/docs/current/sql-alterdefaultprivileges.html).
pub fn parse_alter_default_privileges(
&mut self,
) -> Result<AlterDefaultPrivileges, ParserError> {
// `FOR USER` is a synonym of `FOR ROLE` and is normalised to it.
let for_roles = if self.parse_keyword(Keyword::FOR) {
self.expect_one_of_keywords(&[Keyword::ROLE, Keyword::USER])?;
self.parse_comma_separated(|p| p.parse_identifier())?
} else {
vec![]
};

let in_schemas = if self.parse_keywords(&[Keyword::IN, Keyword::SCHEMA]) {
self.parse_comma_separated(|p| p.parse_object_name(false))?
} else {
vec![]
};

let is_grant = if self.parse_keyword(Keyword::GRANT) {
true
} else if self.parse_keyword(Keyword::REVOKE) {
false
} else {
return self.expected_ref("GRANT or REVOKE", self.peek_token_ref());
};

let grant_option_for =
!is_grant && self.parse_keywords(&[Keyword::GRANT, Keyword::OPTION, Keyword::FOR]);
let privileges = self.parse_privileges()?;
self.expect_keyword_is(Keyword::ON)?;
let object_type = self.parse_default_privileges_object_type()?;
self.expect_keyword_is(if is_grant { Keyword::TO } else { Keyword::FROM })?;
let grantees = self.parse_grantees()?;

let operation = if is_grant {
AlterDefaultPrivilegesOperation::Grant {
privileges,
object_type,
grantees,
with_grant_option: self.parse_keywords(&[
Keyword::WITH,
Keyword::GRANT,
Keyword::OPTION,
]),
}
} else {
AlterDefaultPrivilegesOperation::Revoke {
grant_option_for,
privileges,
object_type,
grantees,
cascade: self.parse_cascade_option(),
}
};

Ok(AlterDefaultPrivileges {
for_roles,
in_schemas,
operation,
})
}

/// Parse `ALL [PRIVILEGES]` or a comma separated privilege list.
fn parse_privileges(&mut self) -> Result<Privileges, ParserError> {
if self.parse_keyword(Keyword::ALL) {
Ok(Privileges::All {
with_privileges_keyword: self.parse_keyword(Keyword::PRIVILEGES),
})
} else {
Ok(Privileges::Actions(self.parse_actions_list()?))
}
}

fn parse_default_privileges_object_type(
&mut self,
) -> Result<DefaultPrivilegesObjectType, ParserError> {
for (keyword, object_type) in [
(Keyword::TABLES, DefaultPrivilegesObjectType::Tables),
(Keyword::SEQUENCES, DefaultPrivilegesObjectType::Sequences),
(Keyword::FUNCTIONS, DefaultPrivilegesObjectType::Functions),
(Keyword::ROUTINES, DefaultPrivilegesObjectType::Routines),
(Keyword::TYPES, DefaultPrivilegesObjectType::Types),
(Keyword::SCHEMAS, DefaultPrivilegesObjectType::Schemas),
] {
if self.parse_keyword(keyword) {
return Ok(object_type);
}
}
self.expected_ref(
"TABLES, SEQUENCES, FUNCTIONS, ROUTINES, TYPES or SCHEMAS",
self.peek_token_ref(),
)
}

/// Parse an REPLACE statement
pub fn parse_replace(
&mut self,
Expand Down
30 changes: 30 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9663,3 +9663,33 @@ fn parse_right_deep_join_chain() {
// NATURAL JOIN followed by a constrained join must stay left-associative.
pg().verified_stmt("SELECT * FROM t0 NATURAL JOIN t1 INNER JOIN t2 ON true");
}

#[test]
fn parse_alter_default_privileges() {
// What `pg_dump -s` emits for a default ACL.
pg().verified_stmt("ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT SELECT ON TABLES TO app_user");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT SELECT ON TABLES TO app");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES FOR ROLE a, b IN SCHEMA s1, s2 GRANT SELECT, INSERT ON TABLES TO app WITH GRANT OPTION");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT ALL PRIVILEGES ON SEQUENCES TO app");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON FUNCTIONS TO app");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT EXECUTE ON ROUTINES TO app");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT USAGE ON TYPES TO app");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES GRANT USAGE, CREATE ON SCHEMAS TO app");
pg().verified_stmt("ALTER DEFAULT PRIVILEGES REVOKE SELECT ON TABLES FROM app");
pg().verified_stmt(
"ALTER DEFAULT PRIVILEGES REVOKE GRANT OPTION FOR ALL ON TABLES FROM app CASCADE",
);
// `FOR USER` normalises to `FOR ROLE`.
pg().one_statement_parses_to(
"ALTER DEFAULT PRIVILEGES FOR USER bob GRANT SELECT ON TABLES TO app",
"ALTER DEFAULT PRIVILEGES FOR ROLE bob GRANT SELECT ON TABLES TO app",
);

// `TABLES` is a keyword only here, so a table named `tables` still parses.
pg().verified_stmt("GRANT SELECT ON TABLES TO app");
assert_eq!(
pg().parse_sql_statements("ALTER DEFAULT PRIVILEGES FOR ROLE a")
.unwrap_err(),
ParserError::ParserError("Expected: GRANT or REVOKE, found: EOF".to_string()),
);
}
Loading