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 48f025f6 SQLite: Allow dollar signs in placeholder names (#1620)
48f025f6 is described below

commit 48f025f65861b1187702ef5aa6614b84baf1bcef
Author: Hans Ott <[email protected]>
AuthorDate: Sat Dec 28 14:20:48 2024 +0100

    SQLite: Allow dollar signs in placeholder names (#1620)
---
 src/dialect/mod.rs        |  6 ++++++
 src/dialect/sqlite.rs     |  4 ++++
 src/tokenizer.rs          | 37 +++++++++++++++++++++++++++++++++----
 tests/sqlparser_sqlite.rs | 10 ++++++++++
 4 files changed, 53 insertions(+), 4 deletions(-)

diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index aee7b599..1343efca 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -636,6 +636,12 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if this dialect allows dollar placeholders
+    /// e.g. `SELECT $var` (SQLite)
+    fn supports_dollar_placeholder(&self) -> bool {
+        false
+    }
+
     /// Does the dialect support with clause in create index statement?
     /// e.g. `CREATE INDEX idx ON t WITH (key = value, key2)`
     fn supports_create_index_with_clause(&self) -> bool {
diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs
index 95717f9f..138c4692 100644
--- a/src/dialect/sqlite.rs
+++ b/src/dialect/sqlite.rs
@@ -81,4 +81,8 @@ impl Dialect for SQLiteDialect {
     fn supports_asc_desc_in_column_definition(&self) -> bool {
         true
     }
+
+    fn supports_dollar_placeholder(&self) -> bool {
+        true
+    }
 }
diff --git a/src/tokenizer.rs b/src/tokenizer.rs
index 9269f4fe..da61303b 100644
--- a/src/tokenizer.rs
+++ b/src/tokenizer.rs
@@ -1509,7 +1509,8 @@ impl<'a> Tokenizer<'a> {
 
         chars.next();
 
-        if let Some('$') = chars.peek() {
+        // If the dialect does not support dollar-quoted strings, then `$$` is 
rather a placeholder.
+        if matches!(chars.peek(), Some('$')) && 
!self.dialect.supports_dollar_placeholder() {
             chars.next();
 
             let mut is_terminated = false;
@@ -1543,10 +1544,14 @@ impl<'a> Tokenizer<'a> {
             };
         } else {
             value.push_str(&peeking_take_while(chars, |ch| {
-                ch.is_alphanumeric() || ch == '_'
+                ch.is_alphanumeric()
+                    || ch == '_'
+                    // Allow $ as a placeholder character if the dialect 
supports it
+                    || matches!(ch, '$' if 
self.dialect.supports_dollar_placeholder())
             }));
 
-            if let Some('$') = chars.peek() {
+            // If the dialect does not support dollar-quoted strings, don't 
look for the end delimiter.
+            if matches!(chars.peek(), Some('$')) && 
!self.dialect.supports_dollar_placeholder() {
                 chars.next();
 
                 'searching_for_end: loop {
@@ -2137,7 +2142,7 @@ fn take_char_from_hex_digits(
 mod tests {
     use super::*;
     use crate::dialect::{
-        BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, 
MySqlDialect,
+        BigQueryDialect, ClickHouseDialect, HiveDialect, MsSqlDialect, 
MySqlDialect, SQLiteDialect,
     };
     use core::fmt::Debug;
 
@@ -2573,6 +2578,30 @@ mod tests {
         );
     }
 
+    #[test]
+    fn tokenize_dollar_placeholder() {
+        let sql = String::from("SELECT $$, $$ABC$$, $ABC$, $ABC");
+        let dialect = SQLiteDialect {};
+        let tokens = Tokenizer::new(&dialect, &sql).tokenize().unwrap();
+        assert_eq!(
+            tokens,
+            vec![
+                Token::make_keyword("SELECT"),
+                Token::Whitespace(Whitespace::Space),
+                Token::Placeholder("$$".into()),
+                Token::Comma,
+                Token::Whitespace(Whitespace::Space),
+                Token::Placeholder("$$ABC$$".into()),
+                Token::Comma,
+                Token::Whitespace(Whitespace::Space),
+                Token::Placeholder("$ABC$".into()),
+                Token::Comma,
+                Token::Whitespace(Whitespace::Space),
+                Token::Placeholder("$ABC".into()),
+            ]
+        );
+    }
+
     #[test]
     fn tokenize_dollar_quoted_string_untagged() {
         let sql =
diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs
index ff0b54ef..0adf7f75 100644
--- a/tests/sqlparser_sqlite.rs
+++ b/tests/sqlparser_sqlite.rs
@@ -561,6 +561,16 @@ fn test_dollar_identifier_as_placeholder() {
         }
         _ => unreachable!(),
     }
+
+    // $$ is a valid placeholder in SQLite
+    match sqlite().verified_expr("id = $$") {
+        Expr::BinaryOp { op, left, right } => {
+            assert_eq!(op, BinaryOperator::Eq);
+            assert_eq!(left, Box::new(Expr::Identifier(Ident::new("id"))));
+            assert_eq!(right, 
Box::new(Expr::Value(Placeholder("$$".to_string()))));
+        }
+        _ => unreachable!(),
+    }
 }
 
 fn sqlite() -> TestedDialects {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to