Fix parse error on some prepared statement placeholders (#604)

sqlparser can now parse all the prepared statement placeholders supported by SQLite:

 - ?
 - ?NNN
 - @VVV
 - :VVV
 - $VVV

See: https://www.sqlite.org/lang_expr.html#varparam

This does not break existing support for postgresql's '@' operator

Fixes #603
This commit is contained in:
Ophir LOJKINE 2022-09-27 15:58:26 +02:00 committed by GitHub
parent 3ac1bb5b80
commit 604f755a59
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 45 additions and 12 deletions

View file

@ -570,7 +570,7 @@ impl<'a> Parser<'a> {
})
}
}
Token::Placeholder(_) => {
Token::Placeholder(_) | Token::Colon | Token::AtSign => {
self.prev_token();
Ok(Expr::Value(self.parse_value()?))
}
@ -1774,7 +1774,7 @@ impl<'a> Parser<'a> {
.iter()
.any(|d| kw.keyword == *d) =>
{
break
break;
}
Token::RParen | Token::EOF => break,
_ => continue,
@ -3038,6 +3038,11 @@ impl<'a> Parser<'a> {
Token::EscapedStringLiteral(ref s) => Ok(Value::EscapedStringLiteral(s.to_string())),
Token::HexStringLiteral(ref s) => Ok(Value::HexStringLiteral(s.to_string())),
Token::Placeholder(ref s) => Ok(Value::Placeholder(s.to_string())),
tok @ Token::Colon | tok @ Token::AtSign => {
let ident = self.parse_identifier()?;
let placeholder = tok.to_string() + &ident.value;
Ok(Value::Placeholder(placeholder))
}
unexpected => self.expected("a value", unexpected),
}
}
@ -4892,12 +4897,12 @@ impl<'a> Parser<'a> {
Some(_) => {
return Err(ParserError::ParserError(
"expected UPDATE, DELETE or INSERT in merge clause".to_string(),
))
));
}
None => {
return Err(ParserError::ParserError(
"expected UPDATE, DELETE or INSERT in merge clause".to_string(),
))
));
}
},
);

View file

@ -678,13 +678,14 @@ impl<'a> Tokenizer<'a> {
}
}
'@' => self.consume_and_return(chars, Token::AtSign),
'?' => self.consume_and_return(chars, Token::Placeholder(String::from("?"))),
'?' => {
chars.next();
let s = peeking_take_while(chars, |ch| ch.is_numeric());
Ok(Some(Token::Placeholder(String::from("?") + &s)))
}
'$' => {
chars.next();
let s = peeking_take_while(
chars,
|ch| matches!(ch, '0'..='9' | 'A'..='Z' | 'a'..='z'),
);
let s = peeking_take_while(chars, |ch| ch.is_alphanumeric() || ch == '_');
Ok(Some(Token::Placeholder(String::from("$") + &s)))
}
//whitespace check (including unicode chars) should be last as it covers some of the chars above