Skip to content

Commit f7a2ef7

Browse files
committed
Apply patch for dollar placeholder
See apache#1620
1 parent 6359358 commit f7a2ef7

File tree

4 files changed

+53
-4
lines changed

4 files changed

+53
-4
lines changed

src/dialect/mod.rs

+6
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,12 @@ pub trait Dialect: Debug + Any {
592592
false
593593
}
594594

595+
/// Returns true if this dialect allows dollar placeholders
596+
/// e.g. `SELECT $var` (SQLite)
597+
fn supports_dollar_placeholder(&self) -> bool {
598+
false
599+
}
600+
595601
/// Does the dialect support with clause in create index statement?
596602
/// e.g. `CREATE INDEX idx ON t WITH (key = value, key2)`
597603
fn supports_create_index_with_clause(&self) -> bool {

src/dialect/sqlite.rs

+4
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,8 @@ impl Dialect for SQLiteDialect {
8181
fn supports_asc_desc_in_column_definition(&self) -> bool {
8282
true
8383
}
84+
85+
fn supports_dollar_placeholder(&self) -> bool {
86+
true
87+
}
8488
}

src/tokenizer.rs

+33-4
Original file line numberDiff line numberDiff line change
@@ -1473,7 +1473,8 @@ impl<'a> Tokenizer<'a> {
14731473

14741474
chars.next();
14751475

1476-
if let Some('$') = chars.peek() {
1476+
// If the dialect does not support dollar-quoted strings, then `$$` is rather a placeholder.
1477+
if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
14771478
chars.next();
14781479

14791480
let mut is_terminated = false;
@@ -1507,10 +1508,14 @@ impl<'a> Tokenizer<'a> {
15071508
};
15081509
} else {
15091510
value.push_str(&peeking_take_while(chars, |ch| {
1510-
ch.is_alphanumeric() || ch == '_'
1511+
ch.is_alphanumeric()
1512+
|| ch == '_'
1513+
// Allow $ as a placeholder character if the dialect supports it
1514+
|| matches!(ch, '$' if self.dialect.supports_dollar_placeholder())
15111515
}));
15121516

1513-
if let Some('$') = chars.peek() {
1517+
// If the dialect does not support dollar-quoted strings, don't look for the end delimiter.
1518+
if matches!(chars.peek(), Some('$')) && !self.dialect.supports_dollar_placeholder() {
15141519
chars.next();
15151520

15161521
'searching_for_end: loop {
@@ -2080,7 +2085,7 @@ fn take_char_from_hex_digits(
20802085
mod tests {
20812086
use super::*;
20822087
use crate::dialect::{
2083-
BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, MySqlDialect,
2088+
BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, MySqlDialect, SQLiteDialect,
20842089
};
20852090
use core::fmt::Debug;
20862091

@@ -2516,6 +2521,30 @@ mod tests {
25162521
);
25172522
}
25182523

2524+
#[test]
2525+
fn tokenize_dollar_placeholder() {
2526+
let sql = String::from("SELECT $$, $$ABC$$, $ABC$, $ABC");
2527+
let dialect = SQLiteDialect {};
2528+
let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
2529+
assert_eq!(
2530+
tokens,
2531+
vec![
2532+
Token::make_keyword("SELECT"),
2533+
Token::Whitespace(Whitespace::Space),
2534+
Token::Placeholder("$$".into()),
2535+
Token::Comma,
2536+
Token::Whitespace(Whitespace::Space),
2537+
Token::Placeholder("$$ABC$$".into()),
2538+
Token::Comma,
2539+
Token::Whitespace(Whitespace::Space),
2540+
Token::Placeholder("$ABC$".into()),
2541+
Token::Comma,
2542+
Token::Whitespace(Whitespace::Space),
2543+
Token::Placeholder("$ABC".into()),
2544+
]
2545+
);
2546+
}
2547+
25192548
#[test]
25202549
fn tokenize_dollar_quoted_string_untagged() {
25212550
let sql =

tests/sqlparser_sqlite.rs

+10
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,16 @@ fn test_dollar_identifier_as_placeholder() {
570570
}
571571
_ => unreachable!(),
572572
}
573+
574+
// $$ is a valid placeholder in SQLite
575+
match sqlite().verified_expr("id = $$") {
576+
Expr::BinaryOp { op, left, right } => {
577+
assert_eq!(op, BinaryOperator::Eq);
578+
assert_eq!(left, Box::new(Expr::Identifier(Ident::new("id"))));
579+
assert_eq!(right, Box::new(Expr::Value(Placeholder("$$".to_string()))));
580+
}
581+
_ => unreachable!(),
582+
}
573583
}
574584

575585
fn sqlite() -> TestedDialects {

0 commit comments

Comments
 (0)