Skip to content

Commit c3ba2f3

Browse files
jmhainiffyio
andauthored
Snowflake: support position with normal function call syntax (#1341)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent 7fdb2ec commit c3ba2f3

File tree

3 files changed

+39
-27
lines changed

3 files changed

+39
-27
lines changed

src/parser/mod.rs

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,7 +1038,7 @@ impl<'a> Parser<'a> {
10381038
Keyword::CEIL => self.parse_ceil_floor_expr(true),
10391039
Keyword::FLOOR => self.parse_ceil_floor_expr(false),
10401040
Keyword::POSITION if self.peek_token().token == Token::LParen => {
1041-
self.parse_position_expr()
1041+
self.parse_position_expr(w.to_ident())
10421042
}
10431043
Keyword::SUBSTRING => self.parse_substring_expr(),
10441044
Keyword::OVERLAY => self.parse_overlay_expr(),
@@ -1707,24 +1707,26 @@ impl<'a> Parser<'a> {
17071707
}
17081708
}
17091709

1710-
pub fn parse_position_expr(&mut self) -> Result<Expr, ParserError> {
1711-
// PARSE SELECT POSITION('@' in field)
1712-
self.expect_token(&Token::LParen)?;
1710+
pub fn parse_position_expr(&mut self, ident: Ident) -> Result<Expr, ParserError> {
1711+
let position_expr = self.maybe_parse(|p| {
1712+
// PARSE SELECT POSITION('@' in field)
1713+
p.expect_token(&Token::LParen)?;
17131714

1714-
// Parse the subexpr till the IN keyword
1715-
let expr = self.parse_subexpr(Self::BETWEEN_PREC)?;
1716-
if self.parse_keyword(Keyword::IN) {
1717-
let from = self.parse_expr()?;
1718-
self.expect_token(&Token::RParen)?;
1715+
// Parse the subexpr till the IN keyword
1716+
let expr = p.parse_subexpr(Self::BETWEEN_PREC)?;
1717+
p.expect_keyword(Keyword::IN)?;
1718+
let from = p.parse_expr()?;
1719+
p.expect_token(&Token::RParen)?;
17191720
Ok(Expr::Position {
17201721
expr: Box::new(expr),
17211722
r#in: Box::new(from),
17221723
})
1723-
} else {
1724-
parser_err!(
1725-
"Position function must include IN keyword".to_string(),
1726-
self.peek_token().location
1727-
)
1724+
});
1725+
match position_expr {
1726+
Some(expr) => Ok(expr),
1727+
// Snowflake supports `position` as an ordinary function call
1728+
// without the special `IN` syntax.
1729+
None => self.parse_function(ObjectName(vec![ident])),
17281730
}
17291731
}
17301732

tests/sqlparser_common.rs

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4151,7 +4151,7 @@ fn parse_scalar_function_in_projection() {
41514151

41524152
for function_name in names {
41534153
// like SELECT sqrt(id) FROM foo
4154-
let sql = dbg!(format!("SELECT {function_name}(id) FROM foo"));
4154+
let sql = format!("SELECT {function_name}(id) FROM foo");
41554155
let select = verified_only_select(&sql);
41564156
assert_eq!(
41574157
&call(function_name, [Expr::Identifier(Ident::new("id"))]),
@@ -8254,30 +8254,34 @@ fn parse_time_functions() {
82548254

82558255
#[test]
82568256
fn parse_position() {
8257-
let sql = "SELECT POSITION('@' IN field)";
8258-
let select = verified_only_select(sql);
82598257
assert_eq!(
8260-
&Expr::Position {
8258+
Expr::Position {
82618259
expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))),
82628260
r#in: Box::new(Expr::Identifier(Ident::new("field"))),
82638261
},
8264-
expr_from_projection(only(&select.projection))
8262+
verified_expr("POSITION('@' IN field)"),
82658263
);
8266-
}
82678264

8268-
#[test]
8269-
fn parse_position_negative() {
8270-
let sql = "SELECT POSITION(foo) from bar";
8271-
let res = parse_sql_statements(sql);
8265+
// some dialects (e.g. snowflake) support position as a function call (i.e. without IN)
82728266
assert_eq!(
8273-
ParserError::ParserError("Position function must include IN keyword".to_string()),
8274-
res.unwrap_err()
8267+
call(
8268+
"position",
8269+
[
8270+
Expr::Value(Value::SingleQuotedString("an".to_owned())),
8271+
Expr::Value(Value::SingleQuotedString("banana".to_owned())),
8272+
Expr::Value(number("1")),
8273+
]
8274+
),
8275+
verified_expr("position('an', 'banana', 1)")
82758276
);
8277+
}
82768278

8279+
#[test]
8280+
fn parse_position_negative() {
82778281
let sql = "SELECT POSITION(foo IN) from bar";
82788282
let res = parse_sql_statements(sql);
82798283
assert_eq!(
8280-
ParserError::ParserError("Expected: an expression:, found: )".to_string()),
8284+
ParserError::ParserError("Expected: (, found: )".to_string()),
82818285
res.unwrap_err()
82828286
);
82838287
}

tests/sqlparser_snowflake.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,3 +2256,9 @@ fn asof_joins() {
22562256
"ORDER BY s.observed",
22572257
));
22582258
}
2259+
2260+
#[test]
2261+
fn test_parse_position() {
2262+
snowflake().verified_query("SELECT position('an', 'banana', 1)");
2263+
snowflake().verified_query("SELECT n, h, POSITION(n IN h) FROM pos");
2264+
}

0 commit comments

Comments
 (0)