diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 87618bbc5f..d3ffdc2ab0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -7825,29 +7825,16 @@ pub enum GrantObjects { ReplicationGroup(Vec), /// Grant privileges on external volumes ExternalVolumes(Vec), - /// Grant privileges on a procedure. In dialects that - /// support overloading, the argument types must be specified. + /// Grant privileges on procedures, each with an optional argument list. /// /// For example: /// `GRANT USAGE ON PROCEDURE foo(varchar) TO ROLE role1` - Procedure { - /// The procedure name. - name: ObjectName, - /// Optional argument types for overloaded procedures. - arg_types: Vec, - }, - - /// Grant privileges on a function. In dialects that - /// support overloading, the argument types must be specified. + Procedures(Vec), + /// Grant privileges on functions, each with an optional argument list. /// /// For example: - /// `GRANT USAGE ON FUNCTION foo(varchar) TO ROLE role1` - Function { - /// The function name. - name: ObjectName, - /// Optional argument types for overloaded functions. - arg_types: Vec, - }, + /// `GRANT EXECUTE ON FUNCTION foo(p_id uuid), bar() TO role1` + Functions(Vec), } impl fmt::Display for GrantObjects { @@ -7979,19 +7966,11 @@ impl fmt::Display for GrantObjects { GrantObjects::ExternalVolumes(objects) => { write!(f, "EXTERNAL VOLUME {}", display_comma_separated(objects)) } - GrantObjects::Procedure { name, arg_types } => { - write!(f, "PROCEDURE {name}")?; - if !arg_types.is_empty() { - write!(f, "({})", display_comma_separated(arg_types))?; - } - Ok(()) + GrantObjects::Procedures(procedures) => { + write!(f, "PROCEDURE {}", display_comma_separated(procedures)) } - GrantObjects::Function { name, arg_types } => { - write!(f, "FUNCTION {name}")?; - if !arg_types.is_empty() { - write!(f, "({})", display_comma_separated(arg_types))?; - } - Ok(()) + GrantObjects::Functions(functions) => { + write!(f, "FUNCTION {}", display_comma_separated(functions)) } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0b31e49907..8f2a293070 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18650,6 +18650,14 @@ impl<'a> Parser<'a> { Some(GrantObjects::ExternalVolumes( self.parse_comma_separated(|p| p.parse_object_name(false))?, )) + } else if self.parse_keyword(Keyword::FUNCTION) { + Some(GrantObjects::Functions( + self.parse_comma_separated(Parser::parse_function_desc)?, + )) + } else if self.parse_keyword(Keyword::PROCEDURE) { + Some(GrantObjects::Procedures( + self.parse_comma_separated(Parser::parse_function_desc)?, + )) } else { let object_type = self.parse_one_of_keywords(&[ Keyword::SEQUENCE, @@ -18664,8 +18672,6 @@ impl<'a> Parser<'a> { Keyword::INTEGRATION, Keyword::USER, Keyword::CONNECTION, - Keyword::PROCEDURE, - Keyword::FUNCTION, ]); let objects = self.parse_comma_separated(|p| p.parse_object_name_inner(false, true)); @@ -18678,13 +18684,6 @@ impl<'a> Parser<'a> { Some(Keyword::VIEW) => Some(GrantObjects::Views(objects?)), Some(Keyword::USER) => Some(GrantObjects::Users(objects?)), Some(Keyword::CONNECTION) => Some(GrantObjects::Connections(objects?)), - kw @ (Some(Keyword::PROCEDURE) | Some(Keyword::FUNCTION)) => { - if let Some(name) = objects?.first() { - self.parse_grant_procedure_or_function(name, &kw)? - } else { - self.expected_ref("procedure or function name", self.peek_token_ref())? - } - } Some(Keyword::TABLE) | None => Some(GrantObjects::Tables(objects?)), Some(unexpected_keyword) => return Err(ParserError::ParserError( format!("Internal parser error: unexpected keyword `{unexpected_keyword}` in grant objects"), @@ -18698,31 +18697,6 @@ impl<'a> Parser<'a> { Ok((privileges, objects)) } - fn parse_grant_procedure_or_function( - &mut self, - name: &ObjectName, - kw: &Option, - ) -> Result, ParserError> { - let arg_types = if self.consume_token(&Token::LParen) { - let list = self.parse_comma_separated0(Self::parse_data_type, Token::RParen)?; - self.expect_token(&Token::RParen)?; - list - } else { - vec![] - }; - match kw { - Some(Keyword::PROCEDURE) => Ok(Some(GrantObjects::Procedure { - name: name.clone(), - arg_types, - })), - Some(Keyword::FUNCTION) => Ok(Some(GrantObjects::Function { - name: name.clone(), - arg_types, - })), - _ => self.expected_ref("procedure or function keywords", self.peek_token_ref())?, - } - } - /// Parse a single grantable permission/action (used within GRANT statements). pub fn parse_grant_permission(&mut self) -> Result { fn parse_columns(parser: &mut Parser) -> Result>, ParserError> { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 90a11b5647..7649790742 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -22341,3 +22341,48 @@ fn parse_delimited_identifiers() { _ => panic!("Expected ExprWithAlias"), } } + +#[test] +fn parse_grant_function_signature() { + let objects = |sql: &str| match verified_stmt(sql) { + Statement::Grant(grant) => grant.objects, + Statement::Revoke(revoke) => revoke.objects, + stmt => panic!("unexpected statement: {stmt:?}"), + }; + let function = |args| FunctionDesc { + name: ObjectName::from(vec![Ident::new("f")]), + args, + }; + + assert_eq!( + objects("GRANT EXECUTE ON FUNCTION f() TO bob"), + Some(GrantObjects::Functions(vec![function(Some(vec![]))])) + ); + assert_eq!( + objects("GRANT EXECUTE ON FUNCTION f TO bob"), + Some(GrantObjects::Functions(vec![function(None)])) + ); + assert_eq!( + objects("REVOKE ALL ON PROCEDURE f(IN p_id UUID, INT) FROM bob"), + Some(GrantObjects::Procedures(vec![function(Some(vec![ + OperateFunctionArg { + mode: Some(ArgMode::In), + name: Some(Ident::new("p_id")), + data_type: DataType::Uuid, + default_expr: None, + }, + OperateFunctionArg::unnamed(DataType::Int(None)), + ]))])) + ); + + verified_stmt("REVOKE ALL ON FUNCTION public.f(p_id UUID) FROM bob"); + verified_stmt("GRANT EXECUTE ON FUNCTION f(INOUT x TEXT), s.g, h() TO bob"); + verified_stmt("GRANT USAGE ON PROCEDURE p(OUT INT), q TO ROLE role1"); + + assert_eq!( + all_dialects() + .parse_sql_statements("GRANT EXECUTE ON FUNCTION f(INT") + .unwrap_err(), + ParserError::ParserError("Expected: ), found: EOF".to_string()) + ); +}