Fix placeholder spans (#1979)

This commit is contained in:
xitep 2025-08-01 12:40:33 +02:00 committed by GitHub
parent f5f51eb6f1
commit 6932f4ad65
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 42 additions and 6 deletions

View file

@ -2525,4 +2525,27 @@ pub mod tests {
"CASE 1 WHEN 2 THEN 3 ELSE 4 END"
);
}
#[test]
fn test_placeholder_span() {
let sql = "\nSELECT\n :fooBar";
let r = Parser::parse_sql(&GenericDialect, sql).unwrap();
assert_eq!(1, r.len());
match &r[0] {
Statement::Query(q) => {
let col = &q.body.as_select().unwrap().projection[0];
match col {
SelectItem::UnnamedExpr(Expr::Value(ValueWithSpan {
value: Value::Placeholder(s),
span,
})) => {
assert_eq!(":fooBar", s);
assert_eq!(&Span::new((3, 3).into(), (3, 10).into()), span);
}
_ => panic!("expected unnamed expression; got {col:?}"),
}
}
stmt => panic!("expected query; got {stmt:?}"),
}
}
}

View file

@ -9636,16 +9636,21 @@ impl<'a> Parser<'a> {
Token::HexStringLiteral(ref s) => ok_value(Value::HexStringLiteral(s.to_string())),
Token::Placeholder(ref s) => ok_value(Value::Placeholder(s.to_string())),
tok @ Token::Colon | tok @ Token::AtSign => {
// Not calling self.parse_identifier(false)? because only in placeholder we want to check numbers as idfentifies
// This because snowflake allows numbers as placeholders
let next_token = self.next_token();
// 1. Not calling self.parse_identifier(false)?
// because only in placeholder we want to check
// numbers as idfentifies. This because snowflake
// allows numbers as placeholders
// 2. Not calling self.next_token() to enforce `tok`
// be followed immediately by a word/number, ie.
// without any whitespace in between
let next_token = self.next_token_no_skip().unwrap_or(&EOF_TOKEN).clone();
let ident = match next_token.token {
Token::Word(w) => Ok(w.into_ident(next_token.span)),
Token::Number(w, false) => Ok(Ident::new(w)),
Token::Number(w, false) => Ok(Ident::with_span(next_token.span, w)),
_ => self.expected("placeholder", next_token),
}?;
let placeholder = tok.to_string() + &ident.value;
ok_value(Value::Placeholder(placeholder))
Ok(Value::Placeholder(tok.to_string() + &ident.value)
.with_span(Span::new(span.start, ident.span.end)))
}
unexpected => self.expected(
"a value",
@ -17600,4 +17605,12 @@ mod tests {
canonical,
);
}
#[test]
fn test_placeholder_invalid_whitespace() {
for w in [" ", "/*invalid*/"] {
let sql = format!("\nSELECT\n :{w}fooBar");
assert!(Parser::parse_sql(&GenericDialect, &sql).is_err());
}
}
}