Skip to content

Commit 7dbf31b

Browse files
authored
Add support for DROP EXTENSION (#1610)
1 parent d89cf80 commit 7dbf31b

File tree

4 files changed

+144
-1
lines changed

4 files changed

+144
-1
lines changed

src/ast/mod.rs

+27
Original file line numberDiff line numberDiff line change
@@ -2755,6 +2755,18 @@ pub enum Statement {
27552755
version: Option<Ident>,
27562756
},
27572757
/// ```sql
2758+
/// DROP EXTENSION [ IF EXISTS ] name [, ...] [ CASCADE | RESTRICT ]
2759+
///
2760+
/// Note: this is a PostgreSQL-specific statement.
2761+
/// https://www.postgresql.org/docs/current/sql-dropextension.html
2762+
/// ```
2763+
DropExtension {
2764+
names: Vec<Ident>,
2765+
if_exists: bool,
2766+
/// `CASCADE` or `RESTRICT`
2767+
cascade_or_restrict: Option<ReferentialAction>,
2768+
},
2769+
/// ```sql
27582770
/// FETCH
27592771
/// ```
27602772
/// Retrieve rows from a query using a cursor
@@ -4029,6 +4041,21 @@ impl fmt::Display for Statement {
40294041

40304042
Ok(())
40314043
}
4044+
Statement::DropExtension {
4045+
names,
4046+
if_exists,
4047+
cascade_or_restrict,
4048+
} => {
4049+
write!(f, "DROP EXTENSION")?;
4050+
if *if_exists {
4051+
write!(f, " IF EXISTS")?;
4052+
}
4053+
write!(f, " {}", display_comma_separated(names))?;
4054+
if let Some(cascade_or_restrict) = cascade_or_restrict {
4055+
write!(f, " {cascade_or_restrict}")?;
4056+
}
4057+
Ok(())
4058+
}
40324059
Statement::CreateRole {
40334060
names,
40344061
if_not_exists,

src/ast/spans.rs

+1
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ impl Spanned for Statement {
430430
Statement::DropSecret { .. } => Span::empty(),
431431
Statement::Declare { .. } => Span::empty(),
432432
Statement::CreateExtension { .. } => Span::empty(),
433+
Statement::DropExtension { .. } => Span::empty(),
433434
Statement::Fetch { .. } => Span::empty(),
434435
Statement::Flush { .. } => Span::empty(),
435436
Statement::Discard { .. } => Span::empty(),

src/parser/mod.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -5414,9 +5414,11 @@ impl<'a> Parser<'a> {
54145414
return self.parse_drop_secret(temporary, persistent);
54155415
} else if self.parse_keyword(Keyword::TRIGGER) {
54165416
return self.parse_drop_trigger();
5417+
} else if self.parse_keyword(Keyword::EXTENSION) {
5418+
return self.parse_drop_extension();
54175419
} else {
54185420
return self.expected(
5419-
"TABLE, VIEW, INDEX, ROLE, SCHEMA, DATABASE, FUNCTION, PROCEDURE, STAGE, TRIGGER, SECRET, SEQUENCE, or TYPE after DROP",
5421+
"TABLE, VIEW, INDEX, ROLE, SCHEMA, DATABASE, FUNCTION, PROCEDURE, STAGE, TRIGGER, SECRET, SEQUENCE, TYPE, or EXTENSION after DROP",
54205422
self.peek_token(),
54215423
);
54225424
};
@@ -6079,6 +6081,25 @@ impl<'a> Parser<'a> {
60796081
})
60806082
}
60816083

6084+
/// Parse a PostgreSQL-specific [Statement::DropExtension] statement.
6085+
pub fn parse_drop_extension(&mut self) -> Result<Statement, ParserError> {
6086+
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
6087+
let names = self.parse_comma_separated(|p| p.parse_identifier(false))?;
6088+
let cascade_or_restrict =
6089+
self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]);
6090+
Ok(Statement::DropExtension {
6091+
names,
6092+
if_exists,
6093+
cascade_or_restrict: cascade_or_restrict
6094+
.map(|k| match k {
6095+
Keyword::CASCADE => Ok(ReferentialAction::Cascade),
6096+
Keyword::RESTRICT => Ok(ReferentialAction::Restrict),
6097+
_ => self.expected("CASCADE or RESTRICT", self.peek_token()),
6098+
})
6099+
.transpose()?,
6100+
})
6101+
}
6102+
60826103
//TODO: Implement parsing for Skewed
60836104
pub fn parse_hive_distribution(&mut self) -> Result<HiveDistributionStyle, ParserError> {
60846105
if self.parse_keywords(&[Keyword::PARTITIONED, Keyword::BY]) {

tests/sqlparser_postgres.rs

+94
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,100 @@ fn parse_create_extension() {
662662
.verified_stmt("CREATE EXTENSION extension_name WITH SCHEMA schema_name VERSION version");
663663
}
664664

665+
#[test]
666+
fn parse_drop_extension() {
667+
assert_eq!(
668+
pg_and_generic().verified_stmt("DROP EXTENSION extension_name"),
669+
Statement::DropExtension {
670+
names: vec!["extension_name".into()],
671+
if_exists: false,
672+
cascade_or_restrict: None,
673+
}
674+
);
675+
assert_eq!(
676+
pg_and_generic().verified_stmt("DROP EXTENSION extension_name CASCADE"),
677+
Statement::DropExtension {
678+
names: vec!["extension_name".into()],
679+
if_exists: false,
680+
cascade_or_restrict: Some(ReferentialAction::Cascade),
681+
}
682+
);
683+
684+
assert_eq!(
685+
pg_and_generic().verified_stmt("DROP EXTENSION extension_name RESTRICT"),
686+
Statement::DropExtension {
687+
names: vec!["extension_name".into()],
688+
if_exists: false,
689+
cascade_or_restrict: Some(ReferentialAction::Restrict),
690+
}
691+
);
692+
693+
assert_eq!(
694+
pg_and_generic().verified_stmt("DROP EXTENSION extension_name, extension_name2 CASCADE"),
695+
Statement::DropExtension {
696+
names: vec!["extension_name".into(), "extension_name2".into()],
697+
if_exists: false,
698+
cascade_or_restrict: Some(ReferentialAction::Cascade),
699+
}
700+
);
701+
702+
assert_eq!(
703+
pg_and_generic().verified_stmt("DROP EXTENSION extension_name, extension_name2 RESTRICT"),
704+
Statement::DropExtension {
705+
names: vec!["extension_name".into(), "extension_name2".into()],
706+
if_exists: false,
707+
cascade_or_restrict: Some(ReferentialAction::Restrict),
708+
}
709+
);
710+
711+
assert_eq!(
712+
pg_and_generic().verified_stmt("DROP EXTENSION IF EXISTS extension_name"),
713+
Statement::DropExtension {
714+
names: vec!["extension_name".into()],
715+
if_exists: true,
716+
cascade_or_restrict: None,
717+
}
718+
);
719+
720+
assert_eq!(
721+
pg_and_generic().verified_stmt("DROP EXTENSION IF EXISTS extension_name CASCADE"),
722+
Statement::DropExtension {
723+
names: vec!["extension_name".into()],
724+
if_exists: true,
725+
cascade_or_restrict: Some(ReferentialAction::Cascade),
726+
}
727+
);
728+
729+
assert_eq!(
730+
pg_and_generic().verified_stmt("DROP EXTENSION IF EXISTS extension_name RESTRICT"),
731+
Statement::DropExtension {
732+
names: vec!["extension_name".into()],
733+
if_exists: true,
734+
cascade_or_restrict: Some(ReferentialAction::Restrict),
735+
}
736+
);
737+
738+
assert_eq!(
739+
pg_and_generic()
740+
.verified_stmt("DROP EXTENSION IF EXISTS extension_name1, extension_name2 CASCADE"),
741+
Statement::DropExtension {
742+
names: vec!["extension_name1".into(), "extension_name2".into()],
743+
if_exists: true,
744+
cascade_or_restrict: Some(ReferentialAction::Cascade),
745+
}
746+
);
747+
748+
assert_eq!(
749+
pg_and_generic()
750+
.verified_stmt("DROP EXTENSION IF EXISTS extension_name1, extension_name2 RESTRICT"),
751+
Statement::DropExtension {
752+
names: vec!["extension_name1".into(), "extension_name2".into()],
753+
if_exists: true,
754+
cascade_or_restrict: Some(ReferentialAction::Restrict),
755+
}
756+
);
757+
}
758+
665759
#[test]
666760
fn parse_alter_table_alter_column() {
667761
pg().one_statement_parses_to(

0 commit comments

Comments
 (0)