Skip to content

Commit 47f6ab6

Browse files
jmhainiffyio
authored andcommitted
Snowflake: support position with normal function call syntax (apache#1341)
Co-authored-by: Ifeanyi Ubah <[email protected]>
1 parent 6160914 commit 47f6ab6

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(),
@@ -1711,24 +1711,26 @@ impl<'a> Parser<'a> {
17111711
}
17121712
}
17131713

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

1718-
// Parse the subexpr till the IN keyword
1719-
let expr = self.parse_subexpr(Self::BETWEEN_PREC)?;
1720-
if self.parse_keyword(Keyword::IN) {
1721-
let from = self.parse_expr()?;
1722-
self.expect_token(&Token::RParen)?;
1719+
// Parse the subexpr till the IN keyword
1720+
let expr = p.parse_subexpr(Self::BETWEEN_PREC)?;
1721+
p.expect_keyword(Keyword::IN)?;
1722+
let from = p.parse_expr()?;
1723+
p.expect_token(&Token::RParen)?;
17231724
Ok(Expr::Position {
17241725
expr: Box::new(expr),
17251726
r#in: Box::new(from),
17261727
})
1727-
} else {
1728-
parser_err!(
1729-
"Position function must include IN keyword".to_string(),
1730-
self.peek_token().location
1731-
)
1728+
});
1729+
match position_expr {
1730+
Some(expr) => Ok(expr),
1731+
// Snowflake supports `position` as an ordinary function call
1732+
// without the special `IN` syntax.
1733+
None => self.parse_function(ObjectName(vec![ident])),
17321734
}
17331735
}
17341736

tests/sqlparser_common.rs

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

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

82568256
#[test]
82578257
fn parse_position() {
8258-
let sql = "SELECT POSITION('@' IN field)";
8259-
let select = verified_only_select(sql);
82608258
assert_eq!(
8261-
&Expr::Position {
8259+
Expr::Position {
82628260
expr: Box::new(Expr::Value(Value::SingleQuotedString("@".to_string()))),
82638261
r#in: Box::new(Expr::Identifier(Ident::new("field"))),
82648262
},
8265-
expr_from_projection(only(&select.projection))
8263+
verified_expr("POSITION('@' IN field)"),
82668264
);
8267-
}
82688265

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

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

tests/sqlparser_snowflake.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2263,3 +2263,9 @@ fn asof_joins() {
22632263
"ORDER BY s.observed",
22642264
));
22652265
}
2266+
2267+
#[test]
2268+
fn test_parse_position() {
2269+
snowflake().verified_query("SELECT position('an', 'banana', 1)");
2270+
snowflake().verified_query("SELECT n, h, POSITION(n IN h) FROM pos");
2271+
}

0 commit comments

Comments
 (0)