You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@datafusion.apache.org by gi...@apache.org on 2026/09/14 08:18:20 UTC

(datafusion-sqlparser-rs) branch gh-readonly-queue/main/pr-2403-b5950628a53de0ac1336d20cd1c618830e2706f0 created (now 9296011a)

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

github-merge-queue[bot] pushed a change to branch gh-readonly-queue/main/pr-2403-b5950628a53de0ac1336d20cd1c618830e2706f0
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


      at 9296011a Databricks: support INSERT BY NAME (#2403)

This branch includes the following new commits:

     new 9296011a Databricks: support INSERT BY NAME (#2403)

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: Databricks: support INSERT BY NAME (#2403)

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

github-merge-queue[bot] pushed a commit to branch gh-readonly-queue/main/pr-2403-b5950628a53de0ac1336d20cd1c618830e2706f0
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 9296011a1c2ba0905310f10c7c1287d9cc12b62c
Author: finchxxia <13...@163.com>
AuthorDate: Mon Sep 14 08:17:56 2026 +0000

    Databricks: support INSERT BY NAME (#2403)
    
    Co-authored-by: Ifeanyi Ubah <if...@yahoo.com>
    Co-authored-by: Cursor <cu...@cursor.com>
    Co-authored-by: finchxia <fi...@aviagames.com>
---
 src/ast/dml.rs                | 19 ++++++++++++++++++
 src/ast/spans.rs              |  1 +
 src/dialect/snowflake.rs      |  1 +
 src/parser/mod.rs             |  5 +++++
 tests/sqlparser_common.rs     | 45 +++++++++++++++++++++++++++++++++++++++++++
 tests/sqlparser_databricks.rs |  8 ++++++++
 tests/sqlparser_postgres.rs   | 22 +++++++++++++++++++++
 7 files changed, 101 insertions(+)

diff --git a/src/ast/dml.rs b/src/ast/dml.rs
index b804e4fc..291fc9fa 100644
--- a/src/ast/dml.rs
+++ b/src/ast/dml.rs
@@ -62,6 +62,20 @@ pub struct Insert {
     pub table_alias: Option<TableAliasWithoutColumns>,
     /// COLUMNS
     pub columns: Vec<ObjectName>,
+    /// `BY NAME` clause used by Databricks SQL.
+    ///
+    /// When present, columns from the source query are matched to columns in
+    /// the target table by name instead of by position. The syntax is:
+    ///
+    /// ```sql
+    /// INSERT INTO [TABLE] table_name
+    ///     [PARTITION (...)]
+    ///     [(column_name [, ...]) | BY NAME]
+    ///     query
+    /// ```
+    ///
+    /// See <https://docs.databricks.com/gcp/en/sql/language-manual/sql-ref-syntax-dml-insert-into>.
+    pub by_name: bool,
     /// Overwrite (Hive)
     pub overwrite: bool,
     /// A SQL query that specifies what to insert
@@ -201,6 +215,11 @@ impl Display for Insert {
             }
         }
 
+        if self.by_name {
+            write!(f, "BY NAME")?;
+            SpaceOrNewline.fmt(f)?;
+        }
+
         if !self.after_columns.is_empty() {
             write!(f, "({})", display_comma_separated(&self.after_columns))?;
             SpaceOrNewline.fmt(f)?;
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index cb06b789..7acbd7d0 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1354,6 +1354,7 @@ impl Spanned for Insert {
             table,
             table_alias,
             columns,
+            by_name: _,   // bool
             overwrite: _, // bool
             source,
             partitioned,
diff --git a/src/dialect/snowflake.rs b/src/dialect/snowflake.rs
index 0bedb12a..0bbd859e 100644
--- a/src/dialect/snowflake.rs
+++ b/src/dialect/snowflake.rs
@@ -1871,6 +1871,7 @@ fn parse_multi_table_insert(
         table: TableObject::TableName(ObjectName(vec![])), // Not used for multi-table insert
         table_alias: None,
         columns: vec![],
+        by_name: false,
         overwrite,
         source: Some(source),
         assignments: vec![],
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index c23b2705..15f135ff 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -18481,7 +18481,9 @@ impl<'a> Parser<'a> {
             let table = self.parse_keyword(Keyword::TABLE);
             let table_object = self.parse_table_object()?;
 
+            // `BY NAME` is an INSERT clause, not a table alias.
             let table_alias = if self.dialect.supports_insert_table_alias()
+                && !self.peek_keywords(&[Keyword::BY, Keyword::NAME])
                 && !self.peek_sub_query()
                 && self
                     .peek_one_of_keywords(&[Keyword::DEFAULT, Keyword::VALUES])
@@ -18505,6 +18507,7 @@ impl<'a> Parser<'a> {
 
             let is_mysql = dialect_of!(self is MySqlDialect);
 
+            let mut by_name = false;
             let (columns, partitioned, after_columns, output, source, assignments) = if self
                 .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES])
             {
@@ -18515,6 +18518,7 @@ impl<'a> Parser<'a> {
                         self.parse_parenthesized_qualified_column_list(Optional, is_mysql)?;
 
                     let partitioned = self.parse_insert_partition()?;
+                    by_name = self.parse_keywords(&[Keyword::BY, Keyword::NAME]);
                     // Hive allows you to specify columns after partitions as well if you want.
                     let after_columns = if dialect_of!(self is HiveDialect) {
                         self.parse_parenthesized_column_list(Optional, false)?
@@ -18639,6 +18643,7 @@ impl<'a> Parser<'a> {
                 ignore,
                 into,
                 overwrite,
+                by_name,
                 partitioned,
                 columns,
                 after_columns,
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 29b060a8..2de6062b 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -20015,3 +20015,48 @@ fn parse_function_arg_call_chain_no_exponential_blowup() {
     rx.recv_timeout(Duration::from_secs(5))
         .expect("parser should reject this quickly, not loop exponentially");
 }
+
+#[test]
+fn parse_insert_by_name() {
+    verified_stmt("INSERT INTO target BY NAME SELECT 1 AS a");
+
+    match verified_stmt("INSERT INTO target (a) BY NAME SELECT 1 AS a") {
+        Statement::Insert(Insert {
+            by_name, columns, ..
+        }) => {
+            assert!(by_name);
+            assert_eq!(columns.len(), 1);
+        }
+        _ => unreachable!(),
+    }
+
+    let dialects = all_dialects_where(|d| !d.supports_insert_table_alias());
+    match dialects.verified_stmt("INSERT INTO TABLE target PARTITION (p = 1) BY NAME SELECT 1 AS a")
+    {
+        Statement::Insert(Insert {
+            by_name,
+            has_table_keyword,
+            partitioned,
+            ..
+        }) => {
+            assert!(by_name);
+            assert!(has_table_keyword);
+            assert_eq!(partitioned.unwrap().len(), 1);
+        }
+        _ => unreachable!(),
+    }
+
+    // `BY NAME` does not shadow a table alias in dialects supporting one.
+    let dialects = all_dialects_where(|d| d.supports_insert_table_alias());
+    match dialects.verified_stmt("INSERT INTO target AS t BY NAME SELECT 1 AS a") {
+        Statement::Insert(Insert {
+            by_name,
+            table_alias,
+            ..
+        }) => {
+            assert!(by_name);
+            assert_eq!(table_alias.unwrap().alias.value, "t");
+        }
+        _ => unreachable!(),
+    }
+}
diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs
index 62068ef5..dd51bd19 100644
--- a/tests/sqlparser_databricks.rs
+++ b/tests/sqlparser_databricks.rs
@@ -738,6 +738,14 @@ fn parse_cte_without_as() {
         .is_err());
 }
 
+#[test]
+fn test_databricks_insert_by_name() {
+    databricks_and_generic().verified_stmt("INSERT INTO target BY NAME SELECT 1 AS a");
+    databricks_and_generic().verified_stmt(
+        "INSERT INTO TABLE lakehouse.dwd.dwd_event_quality_sla_metric_di BY NAME WITH day AS (SELECT 1 AS event_data_id) SELECT event_data_id FROM day",
+    );
+}
+
 #[test]
 fn parse_databricks_query_entry_points() {
     databricks().verified_stmt("CREATE TABLE t (attrs MAP<STRING, ARRAY<INT>>)");
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index 8f390b5e..d71e49b2 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -6121,6 +6121,7 @@ fn test_simple_postgres_insert_with_alias() {
                     span: Span::empty(),
                 })
             ],
+            by_name: false,
             overwrite: false,
             source: Some(Box::new(Query {
                 with: None,
@@ -6201,6 +6202,7 @@ fn test_simple_postgres_insert_with_alias() {
                     span: Span::empty(),
                 })
             ],
+            by_name: false,
             overwrite: false,
             source: Some(Box::new(Query {
                 with: None,
@@ -6283,6 +6285,7 @@ fn test_simple_insert_with_quoted_alias() {
                     span: Span::empty(),
                 })
             ],
+            by_name: false,
             overwrite: false,
             source: Some(Box::new(Query {
                 with: None,
@@ -9931,3 +9934,22 @@ fn parse_non_reserved_keywords_as_table_alias() {
         ));
     }
 }
+
+#[test]
+fn parse_insert_by_name_keywords_as_table_and_alias() {
+    // Without a table name, `BY NAME` is not an INSERT BY NAME clause. PostgreSQL
+    // treats `BY` as the table name and `NAME` as its implicit table alias.
+    match pg().verified_stmt("INSERT INTO BY NAME SELECT 1 AS a") {
+        Statement::Insert(Insert {
+            table: TableObject::TableName(table),
+            table_alias: Some(table_alias),
+            by_name,
+            ..
+        }) => {
+            assert_eq!(table.to_string(), "BY");
+            assert_eq!(table_alias.alias.value, "NAME");
+            assert!(!by_name);
+        }
+        statement => panic!("Expected INSERT statement, got: {statement:?}"),
+    }
+}


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