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/08 19:10:14 UTC

(datafusion-sqlparser-rs) branch upstream/values-scalar-subquery created (now cdb1a024)

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

LucaCappelletti94 pushed a change to branch upstream/values-scalar-subquery
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


      at cdb1a024 PostgreSQL: Parse (VALUES ...) in expressions as a subquery

This branch includes the following new commits:

     new cdb1a024 PostgreSQL: Parse (VALUES ...) in expressions as a subquery

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: PostgreSQL: Parse (VALUES ...) in expressions as a subquery

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

LucaCappelletti94 pushed a commit to branch upstream/values-scalar-subquery
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit cdb1a024ad46ad2bc2de7fa7139aae7b9058cde7
Author: LucaCappelletti94 <ca...@gmail.com>
AuthorDate: Thu Oct 8 21:10:06 2026 +0200

    PostgreSQL: Parse (VALUES ...) in expressions as a subquery
---
 src/dialect/duckdb.rs     |  5 +++++
 src/dialect/generic.rs    |  4 ++++
 src/dialect/mod.rs        | 13 +++++++++++++
 src/dialect/postgresql.rs |  5 +++++
 src/dialect/sqlite.rs     |  5 +++++
 src/parser/mod.rs         |  4 +++-
 tests/sqlparser_common.rs | 27 +++++++++++++++++++++++++++
 7 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/src/dialect/duckdb.rs b/src/dialect/duckdb.rs
index 28ee2402..3d7f42c2 100644
--- a/src/dialect/duckdb.rs
+++ b/src/dialect/duckdb.rs
@@ -143,6 +143,11 @@ impl Dialect for DuckDbDialect {
         true
     }
 
+    /// See <https://duckdb.org/docs/stable/sql/query_syntax/values>
+    fn supports_values_subquery_expr(&self) -> bool {
+        true
+    }
+
     fn supports_nested_comments(&self) -> bool {
         true
     }
diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs
index 09a55185..dbb9abcd 100644
--- a/src/dialect/generic.rs
+++ b/src/dialect/generic.rs
@@ -133,6 +133,10 @@ impl Dialect for GenericDialect {
         true
     }
 
+    fn supports_values_subquery_expr(&self) -> bool {
+        true
+    }
+
     fn supports_create_index_with_clause(&self) -> bool {
         true
     }
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 2beb5655..9c6899f3 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -1189,6 +1189,19 @@ pub trait Dialect: Debug + Any {
         false
     }
 
+    /// Returns true if `VALUES` starts a subquery in expression position,
+    /// rather than calling a function named `VALUES` (MySQL).
+    ///
+    /// Example:
+    /// ```sql
+    /// SELECT (VALUES (1)), 1 = ANY (VALUES (1), (2));
+    /// ```
+    ///
+    /// [PostgreSQL](https://www.postgresql.org/docs/current/sql-values.html)
+    fn supports_values_subquery_expr(&self) -> bool {
+        false
+    }
+
     /// Returns true if this dialect allows dollar placeholders
     /// e.g. `SELECT $var` (SQLite)
     fn supports_dollar_placeholder(&self) -> bool {
diff --git a/src/dialect/postgresql.rs b/src/dialect/postgresql.rs
index 3bec6ceb..120836f3 100644
--- a/src/dialect/postgresql.rs
+++ b/src/dialect/postgresql.rs
@@ -306,6 +306,11 @@ impl Dialect for PostgreSqlDialect {
         true
     }
 
+    /// See <https://www.postgresql.org/docs/current/sql-values.html>
+    fn supports_values_subquery_expr(&self) -> bool {
+        true
+    }
+
     fn supports_order_by_using_operator(&self) -> bool {
         true
     }
diff --git a/src/dialect/sqlite.rs b/src/dialect/sqlite.rs
index 6f9ec0d2..2ff59819 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_values_subquery_expr(&self) -> bool {
+        true
+    }
 }
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 1a46c6e5..d68e72e2 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -21205,10 +21205,12 @@ impl<'a> Parser<'a> {
         self.tokens
     }
 
-    /// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH
+    /// Returns true if the next keyword indicates a sub query, i.e. SELECT or WITH,
+    /// or VALUES where [`Dialect::supports_values_subquery_expr`] holds
     fn peek_sub_query(&mut self) -> bool {
         self.peek_one_of_keywords(&[Keyword::SELECT, Keyword::WITH])
             .is_some()
+            || (self.dialect.supports_values_subquery_expr() && self.peek_keyword(Keyword::VALUES))
     }
 
     pub(crate) fn parse_show_stmt_options(&mut self) -> Result<ShowStatementOptions, ParserError> {
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 8e08c657..f68879a5 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -20433,3 +20433,30 @@ fn parse_bang_not_renders_apart_from_operand() {
     dialects.verified_stmt("SET eaac_cion = ! !o");
     dialects.one_statement_parses_to("SET eaac_cion = ! ! o", "SET eaac_cion = ! !o");
 }
+
+#[test]
+fn parse_values_subquery_expr() {
+    let dialects = all_dialects_where(|d| d.supports_values_subquery_expr());
+    let select = dialects.verified_only_select("SELECT (VALUES (1), (2))");
+    match expr_from_projection(only(&select.projection)) {
+        Expr::Subquery(query) => assert!(matches!(*query.body, SetExpr::Values(_))),
+        expr => panic!("expected a subquery, got {expr:?}"),
+    }
+    match dialects.verified_expr("1 = ANY(VALUES (1), (2))") {
+        Expr::AnyOp { right, .. } => assert!(matches!(*right, Expr::Subquery(_))),
+        expr => panic!("expected ANY over a subquery, got {expr:?}"),
+    }
+    dialects.verified_stmt("SELECT (VALUES (1) LIMIT 1) + 1");
+    assert_eq!(
+        dialects
+            .parse_sql_statements("SELECT (VALUES 1)")
+            .unwrap_err(),
+        ParserError::ParserError("Expected: (, found: 1".to_string())
+    );
+
+    let dialects = all_dialects_where(|d| !d.supports_values_subquery_expr());
+    match dialects.verified_expr("(VALUES(1))") {
+        Expr::Nested(expr) => assert!(matches!(*expr, Expr::Function(_))),
+        expr => panic!("expected a nested function call, got {expr:?}"),
+    }
+}


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