Skip to content
Merged
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
39 changes: 9 additions & 30 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7825,29 +7825,16 @@ pub enum GrantObjects {
ReplicationGroup(Vec<ObjectName>),
/// Grant privileges on external volumes
ExternalVolumes(Vec<ObjectName>),
/// 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<DataType>,
},

/// Grant privileges on a function. In dialects that
/// support overloading, the argument types must be specified.
Procedures(Vec<FunctionDesc>),
/// 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<DataType>,
},
/// `GRANT EXECUTE ON FUNCTION foo(p_id uuid), bar() TO role1`
Functions(Vec<FunctionDesc>),
}

impl fmt::Display for GrantObjects {
Expand Down Expand Up @@ -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))
}
}
}
Expand Down
42 changes: 8 additions & 34 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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));
Expand All @@ -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"),
Expand All @@ -18698,31 +18697,6 @@ impl<'a> Parser<'a> {
Ok((privileges, objects))
}

fn parse_grant_procedure_or_function(
&mut self,
name: &ObjectName,
kw: &Option<Keyword>,
) -> Result<Option<GrantObjects>, 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<Action, ParserError> {
fn parse_columns(parser: &mut Parser) -> Result<Option<Vec<Ident>>, ParserError> {
Expand Down
45 changes: 45 additions & 0 deletions tests/sqlparser_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
);
}
Loading