Support DROP PROCEDURE statement (#1324)

Co-authored-by: Andrew Lamb <andrew@nerdnetworks.org>
This commit is contained in:
Lorrens Pantelis 2024-07-09 13:46:49 +02:00 committed by GitHub
parent bbee052890
commit 9f60eb1571
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 151 additions and 1 deletions

View file

@ -2215,6 +2215,16 @@ pub enum Statement {
option: Option<ReferentialAction>,
},
/// ```sql
/// DROP PROCEDURE
/// ```
DropProcedure {
if_exists: bool,
/// One or more function to drop
proc_desc: Vec<DropFunctionDesc>,
/// `CASCADE` or `RESTRICT`
option: Option<ReferentialAction>,
},
/// ```sql
/// DROP SECRET
/// ```
DropSecret {
@ -3644,6 +3654,22 @@ impl fmt::Display for Statement {
}
Ok(())
}
Statement::DropProcedure {
if_exists,
proc_desc,
option,
} => {
write!(
f,
"DROP PROCEDURE{} {}",
if *if_exists { " IF EXISTS" } else { "" },
display_comma_separated(proc_desc),
)?;
if let Some(op) = option {
write!(f, " {op}")?;
}
Ok(())
}
Statement::DropSecret {
if_exists,
temporary,

View file

@ -4524,11 +4524,13 @@ impl<'a> Parser<'a> {
ObjectType::Stage
} else if self.parse_keyword(Keyword::FUNCTION) {
return self.parse_drop_function();
} else if self.parse_keyword(Keyword::PROCEDURE) {
return self.parse_drop_procedure();
} else if self.parse_keyword(Keyword::SECRET) {
return self.parse_drop_secret(temporary, persistent);
} else {
return self.expected(
"TABLE, VIEW, INDEX, ROLE, SCHEMA, FUNCTION, STAGE or SEQUENCE after DROP",
"TABLE, VIEW, INDEX, ROLE, SCHEMA, FUNCTION, PROCEDURE, STAGE or SEQUENCE after DROP",
self.peek_token(),
);
};
@ -4580,6 +4582,26 @@ impl<'a> Parser<'a> {
})
}
/// ```sql
/// DROP PROCEDURE [ IF EXISTS ] name [ ( [ [ argmode ] [ argname ] argtype [, ...] ] ) ] [, ...]
/// [ CASCADE | RESTRICT ]
/// ```
fn parse_drop_procedure(&mut self) -> Result<Statement, ParserError> {
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let proc_desc = self.parse_comma_separated(Parser::parse_drop_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,
};
Ok(Statement::DropProcedure {
if_exists,
proc_desc,
option,
})
}
fn parse_drop_function_desc(&mut self) -> Result<DropFunctionDesc, ParserError> {
let name = self.parse_object_name(false)?;

View file

@ -3629,6 +3629,108 @@ fn parse_drop_function() {
);
}
#[test]
fn parse_drop_procedure() {
let sql = "DROP PROCEDURE IF EXISTS test_proc";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropProcedure {
if_exists: true,
proc_desc: vec![DropFunctionDesc {
name: ObjectName(vec![Ident {
value: "test_proc".to_string(),
quote_style: None
}]),
args: None
}],
option: None
}
);
let sql = "DROP PROCEDURE IF EXISTS test_proc(a INTEGER, IN b INTEGER = 1)";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropProcedure {
if_exists: true,
proc_desc: vec![DropFunctionDesc {
name: ObjectName(vec![Ident {
value: "test_proc".to_string(),
quote_style: None
}]),
args: Some(vec![
OperateFunctionArg::with_name("a", DataType::Integer(None)),
OperateFunctionArg {
mode: Some(ArgMode::In),
name: Some("b".into()),
data_type: DataType::Integer(None),
default_expr: Some(Expr::Value(Value::Number("1".parse().unwrap(), false))),
}
]),
}],
option: None
}
);
let sql = "DROP PROCEDURE IF EXISTS test_proc1(a INTEGER, IN b INTEGER = 1), test_proc2(a VARCHAR, IN b INTEGER = 1)";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropProcedure {
if_exists: true,
proc_desc: vec![
DropFunctionDesc {
name: ObjectName(vec![Ident {
value: "test_proc1".to_string(),
quote_style: None
}]),
args: Some(vec![
OperateFunctionArg::with_name("a", DataType::Integer(None)),
OperateFunctionArg {
mode: Some(ArgMode::In),
name: Some("b".into()),
data_type: DataType::Integer(None),
default_expr: Some(Expr::Value(Value::Number(
"1".parse().unwrap(),
false
))),
}
]),
},
DropFunctionDesc {
name: ObjectName(vec![Ident {
value: "test_proc2".to_string(),
quote_style: None
}]),
args: Some(vec![
OperateFunctionArg::with_name("a", DataType::Varchar(None)),
OperateFunctionArg {
mode: Some(ArgMode::In),
name: Some("b".into()),
data_type: DataType::Integer(None),
default_expr: Some(Expr::Value(Value::Number(
"1".parse().unwrap(),
false
))),
}
]),
}
],
option: None
}
);
let res = pg().parse_sql_statements("DROP PROCEDURE testproc DROP");
assert_eq!(
ParserError::ParserError("Expected: end of statement, found: DROP".to_string()),
res.unwrap_err()
);
let res = pg().parse_sql_statements("DROP PROCEDURE testproc SET NULL");
assert_eq!(
ParserError::ParserError("Expected: end of statement, found: SET".to_string()),
res.unwrap_err()
);
}
#[test]
fn parse_dollar_quoted_string() {
let sql = "SELECT $$hello$$, $tag_name$world$tag_name$, $$Foo$Bar$$, $$Foo$Bar$$col_name, $$$$, $tag_name$$tag_name$";