This is an automated email from the ASF dual-hosted git repository. iffyio pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push: new 6932f4ad Fix placeholder spans (#1979) 6932f4ad is described below commit 6932f4ad65f442c5b44b3445a76afd8b02dc0fdb Author: xitep <xi...@users.noreply.github.com> AuthorDate: Fri Aug 1 12:40:33 2025 +0200 Fix placeholder spans (#1979) --- src/ast/spans.rs | 23 +++++++++++++++++++++++ src/parser/mod.rs | 25 +++++++++++++++++++------ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 00313c0e..58583660 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -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:?}"), + } + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 3bf03605..9b8fa17a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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()); + } + } } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@datafusion.apache.org For additional commands, e-mail: commits-h...@datafusion.apache.org