Skip to content

Commit 6932f4a

Browse files
authored
Fix placeholder spans (#1979)
1 parent f5f51eb commit 6932f4a

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

src/ast/spans.rs

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

src/parser/mod.rs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9636,16 +9636,21 @@ impl<'a> Parser<'a> {
96369636
Token::HexStringLiteral(ref s) => ok_value(Value::HexStringLiteral(s.to_string())),
96379637
Token::Placeholder(ref s) => ok_value(Value::Placeholder(s.to_string())),
96389638
tok @ Token::Colon | tok @ Token::AtSign => {
9639-
// Not calling self.parse_identifier(false)? because only in placeholder we want to check numbers as idfentifies
9640-
// This because snowflake allows numbers as placeholders
9641-
let next_token = self.next_token();
9639+
// 1. Not calling self.parse_identifier(false)?
9640+
// because only in placeholder we want to check
9641+
// numbers as idfentifies. This because snowflake
9642+
// allows numbers as placeholders
9643+
// 2. Not calling self.next_token() to enforce `tok`
9644+
// be followed immediately by a word/number, ie.
9645+
// without any whitespace in between
9646+
let next_token = self.next_token_no_skip().unwrap_or(&EOF_TOKEN).clone();
96429647
let ident = match next_token.token {
96439648
Token::Word(w) => Ok(w.into_ident(next_token.span)),
9644-
Token::Number(w, false) => Ok(Ident::new(w)),
9649+
Token::Number(w, false) => Ok(Ident::with_span(next_token.span, w)),
96459650
_ => self.expected("placeholder", next_token),
96469651
}?;
9647-
let placeholder = tok.to_string() + &ident.value;
9648-
ok_value(Value::Placeholder(placeholder))
9652+
Ok(Value::Placeholder(tok.to_string() + &ident.value)
9653+
.with_span(Span::new(span.start, ident.span.end)))
96499654
}
96509655
unexpected => self.expected(
96519656
"a value",
@@ -17600,4 +17605,12 @@ mod tests {
1760017605
canonical,
1760117606
);
1760217607
}
17608+
17609+
#[test]
17610+
fn test_placeholder_invalid_whitespace() {
17611+
for w in [" ", "/*invalid*/"] {
17612+
let sql = format!("\nSELECT\n :{w}fooBar");
17613+
assert!(Parser::parse_sql(&GenericDialect, &sql).is_err());
17614+
}
17615+
}
1760317616
}

0 commit comments

Comments
 (0)