Skip to content

Commit 1cc3bf4

Browse files
authored
Support named arguments in function invocations (#250)
This commit supports functions with argument names. the format is : "Select some_function( a => exp, b => exp2 .. ) FROM table1 OR "select * from table(function(a => exp)) f;" see: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#named-argument-assignment-token or the motivating example from snowflake: https://docs.snowflake.com/en/sql-reference/functions/flatten.html
1 parent 580e4b1 commit 1cc3bf4

File tree

5 files changed

+90
-10
lines changed

5 files changed

+90
-10
lines changed

src/ast/mod.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -873,12 +873,28 @@ impl fmt::Display for Assignment {
873873
}
874874
}
875875

876+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
877+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
878+
pub enum FunctionArg {
879+
Named { name: Ident, arg: Expr },
880+
Unnamed(Expr),
881+
}
882+
883+
impl fmt::Display for FunctionArg {
884+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
885+
match self {
886+
FunctionArg::Named { name, arg } => write!(f, "{} => {}", name, arg),
887+
FunctionArg::Unnamed(unnamed_arg) => write!(f, "{}", unnamed_arg),
888+
}
889+
}
890+
}
891+
876892
/// A function call
877893
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
878894
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
879895
pub struct Function {
880896
pub name: ObjectName,
881-
pub args: Vec<Expr>,
897+
pub args: Vec<FunctionArg>,
882898
pub over: Option<WindowSpec>,
883899
// aggregate functions may specify eg `COUNT(DISTINCT x)`
884900
pub distinct: bool,

src/ast/query.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ pub enum TableFactor {
226226
/// Arguments of a table-valued function, as supported by Postgres
227227
/// and MSSQL. Note that deprecated MSSQL `FROM foo (NOLOCK)` syntax
228228
/// will also be parsed as `args`.
229-
args: Vec<Expr>,
229+
args: Vec<FunctionArg>,
230230
/// MSSQL-specific `WITH (...)` hints such as NOLOCK.
231231
with_hints: Vec<Expr>,
232232
},

src/parser.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -2203,11 +2203,24 @@ impl Parser {
22032203
Ok(Assignment { id, value })
22042204
}
22052205

2206-
pub fn parse_optional_args(&mut self) -> Result<Vec<Expr>, ParserError> {
2206+
fn parse_function_args(&mut self) -> Result<FunctionArg, ParserError> {
2207+
if self.peek_nth_token(1) == Token::RArrow {
2208+
let name = self.parse_identifier()?;
2209+
2210+
self.expect_token(&Token::RArrow)?;
2211+
let arg = self.parse_expr()?;
2212+
2213+
Ok(FunctionArg::Named { name, arg })
2214+
} else {
2215+
Ok(FunctionArg::Unnamed(self.parse_expr()?))
2216+
}
2217+
}
2218+
2219+
pub fn parse_optional_args(&mut self) -> Result<Vec<FunctionArg>, ParserError> {
22072220
if self.consume_token(&Token::RParen) {
22082221
Ok(vec![])
22092222
} else {
2210-
let args = self.parse_comma_separated(Parser::parse_expr)?;
2223+
let args = self.parse_comma_separated(Parser::parse_function_args)?;
22112224
self.expect_token(&Token::RParen)?;
22122225
Ok(args)
22132226
}

src/tokenizer.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ pub enum Token {
9999
LBrace,
100100
/// Right brace `}`
101101
RBrace,
102+
/// Right Arrow `=>`
103+
RArrow,
102104
}
103105

104106
impl fmt::Display for Token {
@@ -139,6 +141,7 @@ impl fmt::Display for Token {
139141
Token::Pipe => f.write_str("|"),
140142
Token::LBrace => f.write_str("{"),
141143
Token::RBrace => f.write_str("}"),
144+
Token::RArrow => f.write_str("=>"),
142145
}
143146
}
144147
}
@@ -400,7 +403,13 @@ impl<'a> Tokenizer<'a> {
400403
_ => Ok(Some(Token::Pipe)),
401404
}
402405
}
403-
'=' => self.consume_and_return(chars, Token::Eq),
406+
'=' => {
407+
chars.next(); // consume
408+
match chars.peek() {
409+
Some('>') => self.consume_and_return(chars, Token::RArrow),
410+
_ => Ok(Some(Token::Eq)),
411+
}
412+
}
404413
'.' => self.consume_and_return(chars, Token::Period),
405414
'!' => {
406415
chars.next(); // consume
@@ -766,6 +775,23 @@ mod tests {
766775
compare(expected, tokens);
767776
}
768777

778+
#[test]
779+
fn tokenize_right_arrow() {
780+
let sql = String::from("FUNCTION(key=>value)");
781+
let dialect = GenericDialect {};
782+
let mut tokenizer = Tokenizer::new(&dialect, &sql);
783+
let tokens = tokenizer.tokenize().unwrap();
784+
let expected = vec![
785+
Token::make_word("FUNCTION", None),
786+
Token::LParen,
787+
Token::make_word("key", None),
788+
Token::RArrow,
789+
Token::make_word("value", None),
790+
Token::RParen,
791+
];
792+
compare(expected, tokens);
793+
}
794+
769795
#[test]
770796
fn tokenize_is_null() {
771797
let sql = String::from("a IS NULL");

tests/sqlparser_common.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@ fn parse_select_count_wildcard() {
325325
assert_eq!(
326326
&Expr::Function(Function {
327327
name: ObjectName(vec![Ident::new("COUNT")]),
328-
args: vec![Expr::Wildcard],
328+
args: vec![FunctionArg::Unnamed(Expr::Wildcard)],
329329
over: None,
330330
distinct: false,
331331
}),
@@ -340,10 +340,10 @@ fn parse_select_count_distinct() {
340340
assert_eq!(
341341
&Expr::Function(Function {
342342
name: ObjectName(vec![Ident::new("COUNT")]),
343-
args: vec![Expr::UnaryOp {
343+
args: vec![FunctionArg::Unnamed(Expr::UnaryOp {
344344
op: UnaryOperator::Plus,
345345
expr: Box::new(Expr::Identifier(Ident::new("x")))
346-
}],
346+
})],
347347
over: None,
348348
distinct: true,
349349
}),
@@ -883,7 +883,7 @@ fn parse_select_having() {
883883
Some(Expr::BinaryOp {
884884
left: Box::new(Expr::Function(Function {
885885
name: ObjectName(vec![Ident::new("COUNT")]),
886-
args: vec![Expr::Wildcard],
886+
args: vec![FunctionArg::Unnamed(Expr::Wildcard)],
887887
over: None,
888888
distinct: false
889889
})),
@@ -1589,7 +1589,32 @@ fn parse_scalar_function_in_projection() {
15891589
assert_eq!(
15901590
&Expr::Function(Function {
15911591
name: ObjectName(vec![Ident::new("sqrt")]),
1592-
args: vec![Expr::Identifier(Ident::new("id"))],
1592+
args: vec![FunctionArg::Unnamed(Expr::Identifier(Ident::new("id")))],
1593+
over: None,
1594+
distinct: false,
1595+
}),
1596+
expr_from_projection(only(&select.projection))
1597+
);
1598+
}
1599+
1600+
#[test]
1601+
fn parse_named_argument_function() {
1602+
let sql = "SELECT FUN(a => '1', b => '2') FROM foo";
1603+
let select = verified_only_select(sql);
1604+
1605+
assert_eq!(
1606+
&Expr::Function(Function {
1607+
name: ObjectName(vec![Ident::new("FUN")]),
1608+
args: vec![
1609+
FunctionArg::Named {
1610+
name: Ident::new("a"),
1611+
arg: Expr::Value(Value::SingleQuotedString("1".to_owned()))
1612+
},
1613+
FunctionArg::Named {
1614+
name: Ident::new("b"),
1615+
arg: Expr::Value(Value::SingleQuotedString("2".to_owned()))
1616+
},
1617+
],
15931618
over: None,
15941619
distinct: false,
15951620
}),

0 commit comments

Comments
 (0)