Skip to content

Commit cc116b9

Browse files
committed
Add support for named argument function
This commit support 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 example is snwflake db : https://docs.snowflake.com/en/sql-reference/functions/flatten.html
1 parent 9e7e302 commit cc116b9

File tree

4 files changed

+67
-2
lines changed

4 files changed

+67
-2
lines changed

src/ast/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ pub enum Expr {
235235
Subquery(Box<Query>),
236236
/// The `LISTAGG` function `SELECT LISTAGG(...) WITHIN GROUP (ORDER BY ...)`
237237
ListAgg(ListAgg),
238+
/// Argument with name (Name=>argument)
239+
NamedArgument { name: Ident, value: Box<Expr> },
238240
}
239241

240242
impl fmt::Display for Expr {
@@ -315,6 +317,7 @@ impl fmt::Display for Expr {
315317
Expr::Exists(s) => write!(f, "EXISTS ({})", s),
316318
Expr::Subquery(s) => write!(f, "({})", s),
317319
Expr::ListAgg(listagg) => write!(f, "{}", listagg),
320+
Expr::NamedArgument { name, value } => write!(f, "{} => {}", name, value),
318321
}
319322
}
320323
}

src/parser.rs

+12
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,18 @@ impl Parser {
276276
Ok(Expr::CompoundIdentifier(id_parts))
277277
}
278278
}
279+
Token::RArrow => {
280+
self.prev_token();
281+
let ident = self.parse_identifier()?;
282+
283+
// the RArrow ('=>')
284+
let _ = self.next_token();
285+
286+
Ok(Expr::NamedArgument {
287+
name: ident,
288+
value: Box::new(self.parse_expr()?),
289+
})
290+
}
279291
_ => Ok(Expr::Identifier(w.to_ident())),
280292
},
281293
}, // End of Token::Word

src/tokenizer.rs

+28-2
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
}
@@ -323,7 +326,7 @@ impl<'a> Tokenizer<'a> {
323326
}
324327
}
325328
}
326-
// identifier or keyword
329+
// identifier or Keyword
327330
ch if self.dialect.is_identifier_start(ch) => {
328331
chars.next(); // consume the first char
329332
let s = self.tokenize_word(ch, chars);
@@ -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

+24
Original file line numberDiff line numberDiff line change
@@ -1597,6 +1597,30 @@ fn parse_scalar_function_in_projection() {
15971597
);
15981598
}
15991599

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+
assert_eq!(
1605+
&Expr::Function(Function {
1606+
name: ObjectName(vec![Ident::new("FUN")]),
1607+
args: vec![
1608+
Expr::NamedArgument {
1609+
name: Ident::new("a"),
1610+
value: Box::new(Expr::Value(Value::SingleQuotedString("1".to_owned())))
1611+
},
1612+
Expr::NamedArgument {
1613+
name: Ident::new("b"),
1614+
value: Box::new(Expr::Value(Value::SingleQuotedString("2".to_owned())))
1615+
}
1616+
],
1617+
over: None,
1618+
distinct: false,
1619+
}),
1620+
expr_from_projection(only(&select.projection))
1621+
);
1622+
}
1623+
16001624
#[test]
16011625
fn parse_window_functions() {
16021626
let sql = "SELECT row_number() OVER (ORDER BY dt DESC), \

0 commit comments

Comments
 (0)