You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by lu...@apache.org on 2026/10/02 19:59:25 UTC

(datafusion-sqlparser-rs) branch upstream/sqlite-insert-value-and-nested-dml created (now 86e0bf86)

This is an automated email from the ASF dual-hosted git repository.

LucaCappelletti94 pushed a change to branch upstream/sqlite-insert-value-and-nested-dml
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


      at 86e0bf86 SQLite: Reject DML in nested queries

This branch includes the following new commits:

     new 86e0bf86 SQLite: Reject DML in nested queries

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org


(datafusion-sqlparser-rs) 01/01: SQLite: Reject DML in nested queries

Posted by lu...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

LucaCappelletti94 pushed a commit to branch upstream/sqlite-insert-value-and-nested-dml
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 86e0bf868395828942cf61315a8c4c4ff364964f
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Fri Oct 2 21:59:09 2026 +0200

    SQLite: Reject DML in nested queries
---
 src/dialect/mod.rs        |  5 +++++
 src/dialect/sqlite.rs     |  5 +++++
 src/parser/mod.rs         | 17 +++++++++++------
 tests/sqlparser_common.rs |  2 +-
 tests/sqlparser_sqlite.rs | 25 +++++++++++++++++++++++++
 5 files changed, 47 insertions(+), 7 deletions(-)

diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 2beb5655..4ad996e0 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1457,6 +1457,11 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if the dialect accepts `INSERT`, `UPDATE`, `DELETE` or `MERGE` in a subquery, CTE or `INSERT` source.
+    fn supports_nested_dml_query(&self) -> bool {
+        true
+    }
+
     /// Returns true if this dialect supports `SET` statements without an explicit
     /// assignment operator such as `=`. For example: `SET SHOWPLAN_XML ON`.
     fn supports_set_stmt_without_operator(&self) -> bool {
diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs
index 6f9ec0d2..e679a340 100644
--- a/src/dialect/sqlite.rs
+++ b/src/dialect/sqlite.rs
@@ -142,4 +142,9 @@ impl Dialect for SQLiteDialect {
     fn supports_national_string_literal(&self) -> bool {
         false
     }
+
+    /// See <https://sqlite.org/lang_select.html>
+    fn supports_nested_dml_query(&self) -> bool {
+        false
+    }
 }
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1a46c6e5..2c5a48b7 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -650,7 +650,7 @@ impl<'a> Parser<'a> {
                 }
                 Keyword::SELECT | Keyword::WITH | Keyword::VALUES | Keyword::FROM => {
                     self.prev_token();
-                    self.parse_query().map(Into::into)
+                    self.parse_query_allowing_dml(true).map(Into::into)
                 }
                 Keyword::TRUNCATE => self.parse_truncate().map(Into::into),
                 Keyword::ATTACH => {
@@ -14728,8 +14728,13 @@ impl<'a> Parser<'a> {
     /// preceded with some `WITH` CTE declarations and optionally followed
     /// by `ORDER BY`. Unlike some other parse_... methods, this one doesn't
     /// expect the initial keyword to be already consumed
-    #[cfg_attr(feature = "recursive-protection", recursive::recursive)]
     pub fn parse_query(&mut self) -> Result<Box<Query>, ParserError> {
+        self.parse_query_allowing_dml(self.dialect.supports_nested_dml_query())
+    }
+
+    /// Parses a query whose body may be `INSERT`, `UPDATE`, `DELETE` or `MERGE` when `allow_dml` is set.
+    #[cfg_attr(feature = "recursive-protection", recursive::recursive)]
+    fn parse_query_allowing_dml(&mut self, allow_dml: bool) -> Result<Box<Query>, ParserError> {
         let _guard = self.recursion_counter.try_decrease()?;
         let with = if self.parse_keyword(Keyword::WITH) {
             let with_token = self.get_current_token();
@@ -14741,7 +14746,7 @@ impl<'a> Parser<'a> {
         } else {
             None
         };
-        if self.parse_keyword(Keyword::INSERT) {
+        if allow_dml && self.parse_keyword(Keyword::INSERT) {
             Ok(Query {
                 with,
                 body: self.parse_insert_setexpr_boxed(self.get_current_token().clone())?,
@@ -14755,7 +14760,7 @@ impl<'a> Parser<'a> {
                 pipe_operators: vec![],
             }
             .into())
-        } else if self.parse_keyword(Keyword::UPDATE) {
+        } else if allow_dml && self.parse_keyword(Keyword::UPDATE) {
             Ok(Query {
                 with,
                 body: self.parse_update_setexpr_boxed(self.get_current_token().clone())?,
@@ -14769,7 +14774,7 @@ impl<'a> Parser<'a> {
                 pipe_operators: vec![],
             }
             .into())
-        } else if self.parse_keyword(Keyword::DELETE) {
+        } else if allow_dml && self.parse_keyword(Keyword::DELETE) {
             Ok(Query {
                 with,
                 body: self.parse_delete_setexpr_boxed(self.get_current_token().clone())?,
@@ -14783,7 +14788,7 @@ impl<'a> Parser<'a> {
                 pipe_operators: vec![],
             }
             .into())
-        } else if self.parse_keyword(Keyword::MERGE) {
+        } else if allow_dml && self.parse_keyword(Keyword::MERGE) {
             Ok(Query {
                 with,
                 body: self.parse_merge_setexpr_boxed(self.get_current_token().clone())?,
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 8e08c657..73865a4d 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -10710,7 +10710,7 @@ VALUES (1, 'abc')";
 
 #[test]
 fn test_merge_in_cte() {
-    verified_only_select(
+    all_dialects_where(|d| d.supports_nested_dml_query()).verified_only_select(
         "WITH x AS (\
             MERGE INTO t USING (VALUES (1)) ON 1 = 1 \
             WHEN MATCHED THEN DELETE \
diff --git a/tests/sqlparser_sqlite.rs b/tests/sqlparser_sqlite.rs
index b8fad7e9..4753331d 100644
--- a/tests/sqlparser_sqlite.rs
+++ b/tests/sqlparser_sqlite.rs
@@ -1024,6 +1024,31 @@ fn parse_n_prefix_not_national_string() {
     all_dialects_where(|d| d.supports_national_string_literal()).verified_stmt("SELECT N'hello'");
 }
 
+#[test]
+fn parse_nested_dml_rejected() {
+    for sql in [
+        "INSERT INTO u INSERT INTO u SELECT 1",
+        "INSERT INTO u (a) INSERT INTO u SELECT 1",
+        "INSERT INTO u WITH x AS (SELECT 1) INSERT INTO u SELECT 1",
+        "SELECT * FROM (INSERT INTO u SELECT 1)",
+        "WITH x AS (INSERT INTO u SELECT 1) SELECT 1",
+        "WITH x AS (UPDATE u SET a = 1) SELECT 1",
+        "WITH x AS (DELETE FROM u) SELECT 1",
+    ] {
+        assert!(sqlite().parse_sql_statements(sql).is_err(), "{sql}");
+    }
+
+    let nested_dml = all_dialects_where(|d| d.supports_nested_dml_query());
+    nested_dml.verified_stmt("INSERT INTO u (a) INSERT INTO u SELECT 1");
+    nested_dml.verified_stmt("SELECT * FROM (INSERT INTO u SELECT 1)");
+    nested_dml.verified_stmt("WITH x AS (INSERT INTO u SELECT 1) SELECT 1");
+
+    sqlite().verified_stmt("WITH x AS (SELECT 1) INSERT INTO u SELECT * FROM x");
+    sqlite().verified_stmt("WITH x AS (SELECT 1) UPDATE u SET a = 2");
+    sqlite().verified_stmt("WITH x AS (SELECT 1) DELETE FROM u");
+    sqlite().verified_stmt("INSERT INTO u (a) WITH x AS (SELECT 1) SELECT * FROM x");
+}
+
 fn sqlite() -> TestedDialects {
     TestedDialects::new(vec![Box::new(SQLiteDialect {})])
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@datafusion.apache.org
For additional commands, e-mail: commits-help@datafusion.apache.org