Skip to content

Commit cc822f2

Browse files
xitepayman-sigma
authored andcommitted
Fix placeholder spans (apache#1979)
1 parent 2c7ed70 commit cc822f2

2 files changed

Lines changed: 42 additions & 6 deletions

File tree

src/ast/spans.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,4 +2534,27 @@ pub mod tests {
25342534
"CASE 1 WHEN 2 THEN 3 ELSE 4 END"
25352535
);
25362536
}
2537+
2538+
#[test]
2539+
fn test_placeholder_span() {
2540+
let sql = "\nSELECT\n :fooBar";
2541+
let r = Parser::parse_sql(&GenericDialect, sql).unwrap();
2542+
assert_eq!(1, r.len());
2543+
match &r[0] {
2544+
Statement::Query(q) => {
2545+
let col = &q.body.as_select().unwrap().projection[0];
2546+
match col {
2547+
SelectItem::UnnamedExpr(Expr::Value(ValueWithSpan {
2548+
value: Value::Placeholder(s),
2549+
span,
2550+
})) => {
2551+
assert_eq!(":fooBar", s);
2552+
assert_eq!(&Span::new((3, 3).into(), (3, 10).into()), span);
2553+
}
2554+
_ => panic!("expected unnamed expression; got {col:?}"),
2555+
}
2556+
}
2557+
stmt => panic!("expected query; got {stmt:?}"),
2558+
}
2559+
}
25372560
}

src/parser/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9667,16 +9667,21 @@ impl<'a> Parser<'a> {
96679667
Token::HexStringLiteral(ref s) => ok_value(Value::HexStringLiteral(s.to_string())),
96689668
Token::Placeholder(ref s) => ok_value(Value::Placeholder(s.to_string())),
96699669
tok @ Token::Colon | tok @ Token::AtSign => {
9670-
// Not calling self.parse_identifier(false)? because only in placeholder we want to check numbers as idfentifies
9671-
// This because snowflake allows numbers as placeholders
9672-
let next_token = self.next_token();
9670+
// 1. Not calling self.parse_identifier(false)?
9671+
// because only in placeholder we want to check
9672+
// numbers as idfentifies. This because snowflake
9673+
// allows numbers as placeholders
9674+
// 2. Not calling self.next_token() to enforce `tok`
9675+
// be followed immediately by a word/number, ie.
9676+
// without any whitespace in between
9677+
let next_token = self.next_token_no_skip().unwrap_or(&EOF_TOKEN).clone();
96739678
let ident = match next_token.token {
96749679
Token::Word(w) => Ok(w.into_ident(next_token.span)),
9675-
Token::Number(w, false) => Ok(Ident::new(w)),
9680+
Token::Number(w, false) => Ok(Ident::with_span(next_token.span, w)),
96769681
_ => self.expected("placeholder", next_token),
96779682
}?;
9678-
let placeholder = tok.to_string() + &ident.value;
9679-
ok_value(Value::Placeholder(placeholder))
9683+
Ok(Value::Placeholder(tok.to_string() + &ident.value)
9684+
.with_span(Span::new(span.start, ident.span.end)))
96809685
}
96819686
unexpected => self.expected(
96829687
"a value",
@@ -17632,4 +17637,12 @@ mod tests {
1763217637
canonical,
1763317638
);
1763417639
}
17640+
17641+
#[test]
17642+
fn test_placeholder_invalid_whitespace() {
17643+
for w in [" ", "/*invalid*/"] {
17644+
let sql = format!("\nSELECT\n :{w}fooBar");
17645+
assert!(Parser::parse_sql(&GenericDialect, &sql).is_err());
17646+
}
17647+
}
1763517648
}

0 commit comments

Comments
 (0)