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
3 changes: 3 additions & 0 deletions fuzz/fuzz_seeds/postgres_2712_drop_group.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
DROP GROUP staff;
DROP GROUP IF EXISTS staff, workers;
DROP GROUP "Mixed Name", "GROUP"
3 changes: 3 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8749,6 +8749,8 @@ pub enum ObjectType {
Database,
/// A role.
Role,
/// A user group.
Group,
/// A sequence.
Sequence,
/// A stage.
Expand All @@ -8774,6 +8776,7 @@ impl fmt::Display for ObjectType {
ObjectType::Schema => "SCHEMA",
ObjectType::Database => "DATABASE",
ObjectType::Role => "ROLE",
ObjectType::Group => "GROUP",
ObjectType::Sequence => "SEQUENCE",
ObjectType::Stage => "STAGE",
ObjectType::Type => "TYPE",
Expand Down
7 changes: 7 additions & 0 deletions src/dialect/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,13 @@ pub trait Dialect: Debug + Any {
false
}

/// Returns true if the dialect supports `DROP GROUP`.
///
/// <https://www.postgresql.org/docs/current/sql-dropgroup.html>
fn supports_drop_group(&self) -> bool {
false
}

/// Returns true if the dialect treats `ALTER USER` as a synonym for `ALTER ROLE`.
///
/// In PostgreSQL, `ALTER USER` and `ALTER ROLE` are synonyms that accept the same
Expand Down
4 changes: 4 additions & 0 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,10 @@ impl Dialect for PostgreSqlDialect {
true
}

fn supports_drop_group(&self) -> bool {
true
}

fn supports_alter_user_as_alter_role(&self) -> bool {
true
}
Expand Down
10 changes: 7 additions & 3 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8021,6 +8021,8 @@ impl<'a> Parser<'a> {
ObjectType::Index
} else if self.parse_keyword(Keyword::ROLE) {
ObjectType::Role
} else if self.dialect.supports_drop_group() && self.parse_keyword(Keyword::GROUP) {
ObjectType::Group
} else if self.parse_keyword(Keyword::SCHEMA) {
ObjectType::Schema
} else if self.parse_keyword(Keyword::DATABASE) {
Expand Down Expand Up @@ -8064,7 +8066,7 @@ impl<'a> Parser<'a> {
};
} else {
return self.expected_ref(
"COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP",
"COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, GROUP, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP",
self.peek_token_ref(),
);
};
Expand All @@ -8080,9 +8082,11 @@ impl<'a> Parser<'a> {
if cascade && restrict {
return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP", loc);
}
if object_type == ObjectType::Role && (cascade || restrict || purge) {
if matches!(object_type, ObjectType::Role | ObjectType::Group)
&& (cascade || restrict || purge)
{
return parser_err!(
"Cannot specify CASCADE, RESTRICT, or PURGE in DROP ROLE",
format_args!("Cannot specify CASCADE, RESTRICT, or PURGE in DROP {object_type}"),
loc
);
}
Expand Down
50 changes: 50 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10875,3 +10875,53 @@ fn parse_create_aggregate_rejects_output_arguments_and_defaults() {
);
}
}

#[test]
fn parse_drop_group() {
for (sql, expected_names, expected_if_exists) in [
("DROP GROUP staff", vec!["staff"], false),
(
"DROP GROUP IF EXISTS staff, workers",
vec!["staff", "workers"],
true,
),
] {
match pg().verified_stmt(sql) {
Statement::Drop {
object_type,
if_exists,
names,
..
} => {
assert_eq!(object_type, ObjectType::Group);
assert_eq_vec(&expected_names, &names);
assert_eq!(if_exists, expected_if_exists);
}
other => panic!("expected DROP statement, got {other:?}"),
}
}
pg().verified_stmt(r#"DROP GROUP "Mixed Name", "GROUP""#);
}

#[test]
fn parse_drop_group_rejects_drop_behaviors() {
for behavior in ["CASCADE", "RESTRICT", "PURGE"] {
assert_eq!(
pg().parse_sql_statements(&format!("DROP GROUP staff {behavior}")),
Err(ParserError::ParserError(
"Cannot specify CASCADE, RESTRICT, or PURGE in DROP GROUP".to_string()
))
);
}
}

#[test]
fn parse_drop_group_is_dialect_gated() {
assert_eq!(
all_dialects_where(|d| !d.supports_drop_group())
.parse_sql_statements("DROP GROUP staff"),
Err(ParserError::ParserError(
"Expected: COLLATION, CONNECTOR, DATABASE, EXTENSION, FUNCTION, GROUP, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW, USER or WAREHOUSE after DROP, found: GROUP".to_string()
))
);
}
Loading