diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 83646d298..03db24fcd 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2546,6 +2546,16 @@ pub enum Statement { name: Ident, storage_specifier: Option, }, + ///```sql + /// DROP POLICY + /// ``` + /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-droppolicy.html) + DropPolicy { + if_exists: bool, + name: Ident, + table_name: ObjectName, + option: Option, + }, /// ```sql /// DECLARE /// ``` @@ -4258,6 +4268,22 @@ impl fmt::Display for Statement { } Ok(()) } + Statement::DropPolicy { + if_exists, + name, + table_name, + option, + } => { + write!(f, "DROP POLICY")?; + if *if_exists { + write!(f, " IF EXISTS")?; + } + write!(f, " {name} ON {table_name}")?; + if let Some(option) = option { + write!(f, " {option}")?; + } + Ok(()) + } Statement::Discard { object_type } => { write!(f, "DISCARD {object_type}")?; Ok(()) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 4c3f8788d..78c3aa635 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -4887,6 +4887,8 @@ impl<'a> Parser<'a> { ObjectType::Stage } else if self.parse_keyword(Keyword::FUNCTION) { return self.parse_drop_function(); + } else if self.parse_keyword(Keyword::POLICY) { + return self.parse_drop_policy(); } else if self.parse_keyword(Keyword::PROCEDURE) { return self.parse_drop_procedure(); } else if self.parse_keyword(Keyword::SECRET) { @@ -4928,6 +4930,14 @@ impl<'a> Parser<'a> { }) } + fn parse_optional_referential_action(&mut self) -> Option { + match self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]) { + Some(Keyword::CASCADE) => Some(ReferentialAction::Cascade), + Some(Keyword::RESTRICT) => Some(ReferentialAction::Restrict), + _ => None, + } + } + /// ```sql /// DROP FUNCTION [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...] /// [ CASCADE | RESTRICT ] @@ -4935,11 +4945,7 @@ impl<'a> Parser<'a> { fn parse_drop_function(&mut self) -> Result { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let func_desc = self.parse_comma_separated(Parser::parse_function_desc)?; - let option = match self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]) { - Some(Keyword::CASCADE) => Some(ReferentialAction::Cascade), - Some(Keyword::RESTRICT) => Some(ReferentialAction::Restrict), - _ => None, - }; + let option = self.parse_optional_referential_action(); Ok(Statement::DropFunction { if_exists, func_desc, @@ -4947,6 +4953,25 @@ impl<'a> Parser<'a> { }) } + /// ```sql + /// DROP POLICY [ IF EXISTS ] name ON table_name [ CASCADE | RESTRICT ] + /// ``` + /// + /// [PostgreSQL Documentation](https://www.postgresql.org/docs/current/sql-droppolicy.html) + fn parse_drop_policy(&mut self) -> Result { + let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); + let name = self.parse_identifier(false)?; + self.expect_keyword(Keyword::ON)?; + let table_name = self.parse_object_name(false)?; + let option = self.parse_optional_referential_action(); + Ok(Statement::DropPolicy { + if_exists, + name, + table_name, + option, + }) + } + /// ```sql /// DROP PROCEDURE [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...] /// [ CASCADE | RESTRICT ] @@ -4954,12 +4979,7 @@ impl<'a> Parser<'a> { fn parse_drop_procedure(&mut self) -> Result { let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]); let proc_desc = self.parse_comma_separated(Parser::parse_function_desc)?; - let option = match self.parse_one_of_keywords(&[Keyword::CASCADE, Keyword::RESTRICT]) { - Some(Keyword::CASCADE) => Some(ReferentialAction::Cascade), - Some(Keyword::RESTRICT) => Some(ReferentialAction::Restrict), - Some(_) => unreachable!(), // parse_one_of_keywords does not return other keywords - None => None, - }; + let option = self.parse_optional_referential_action(); Ok(Statement::DropProcedure { if_exists, proc_desc, diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 711070034..48c4aafe1 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -11089,3 +11089,44 @@ fn test_create_policy() { "sql parser error: Expected: (, found: EOF" ); } + +#[test] +fn test_drop_policy() { + let sql = "DROP POLICY IF EXISTS my_policy ON my_table RESTRICT"; + match all_dialects().verified_stmt(sql) { + Statement::DropPolicy { + if_exists, + name, + table_name, + option, + } => { + assert_eq!(if_exists, true); + assert_eq!(name.to_string(), "my_policy"); + assert_eq!(table_name.to_string(), "my_table"); + assert_eq!(option, Some(ReferentialAction::Restrict)); + } + _ => unreachable!(), + } + + // omit IF EXISTS is allowed + all_dialects().verified_stmt("DROP POLICY my_policy ON my_table CASCADE"); + // omit option is allowed + all_dialects().verified_stmt("DROP POLICY my_policy ON my_table"); + + // missing table name + assert_eq!( + all_dialects() + .parse_sql_statements("DROP POLICY my_policy") + .unwrap_err() + .to_string(), + "sql parser error: Expected: ON, found: EOF" + ); + // Wrong option name + assert_eq!( + all_dialects() + .parse_sql_statements("DROP POLICY my_policy ON my_table WRONG") + .unwrap_err() + .to_string(), + "sql parser error: Expected: end of statement, found: WRONG" + ); +}