From 68ff95de4a709f8cc5579ff000bf62451bac1856 Mon Sep 17 00:00:00 2001 From: finchxxia <13153363548@163.com> Date: Mon, 13 Jul 2026 12:49:56 +0800 Subject: [PATCH 1/2] feat: support Databricks INSERT BY NAME --- src/ast/dml.rs | 7 +++++++ src/ast/spans.rs | 1 + src/dialect/snowflake.rs | 1 + src/parser/mod.rs | 5 +++++ tests/sqlparser_databricks.rs | 31 +++++++++++++++++++++++++++++++ tests/sqlparser_postgres.rs | 3 +++ 6 files changed, 48 insertions(+) diff --git a/src/ast/dml.rs b/src/ast/dml.rs index c2fe2ffd60..5fed3e9703 100644 --- a/src/ast/dml.rs +++ b/src/ast/dml.rs @@ -62,6 +62,8 @@ pub struct Insert { pub table_alias: Option, /// COLUMNS pub columns: Vec, + /// BY NAME + pub by_name: bool, /// Overwrite (Hive) pub overwrite: bool, /// A SQL query that specifies what to insert @@ -201,6 +203,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 6505b209e3..3f5dd5b241 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1353,6 +1353,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 b76cbc55e8..0052a0f02b 100644 --- a/src/dialect/snowflake.rs +++ b/src/dialect/snowflake.rs @@ -1866,6 +1866,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 09715bbc71..5d7068f642 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18338,6 +18338,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]) { @@ -18348,6 +18349,9 @@ impl<'a> Parser<'a> { self.parse_parenthesized_qualified_column_list(Optional, is_mysql)?; let partitioned = self.parse_insert_partition()?; + by_name = columns.is_empty() + && dialect_of!(self is DatabricksDialect | GenericDialect) + && 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)? @@ -18472,6 +18476,7 @@ impl<'a> Parser<'a> { ignore, into, overwrite, + by_name, partitioned, columns, after_columns, diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 0ed0b4118c..47ccebaddc 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -168,6 +168,37 @@ fn test_databricks_lambdas() { databricks().verified_expr("transform(array(1, 2, 3), x -> x + 1)"); } +#[test] +fn test_databricks_insert_by_name() { + match databricks().verified_stmt("INSERT INTO target BY NAME SELECT 1 AS a") { + Statement::Insert(Insert { + by_name, + columns, + has_table_keyword, + .. + }) => { + assert!(by_name); + assert!(columns.is_empty()); + assert!(!has_table_keyword); + } + _ => unreachable!(), + } + + match databricks().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", + ) { + Statement::Insert(Insert { + by_name, + has_table_keyword, + .. + }) => { + assert!(by_name); + assert!(has_table_keyword); + } + _ => unreachable!(), + } +} + #[test] fn test_values_clause() { let values = Values { diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 9ee6ab3d64..c0220b4d77 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -6027,6 +6027,7 @@ fn test_simple_postgres_insert_with_alias() { span: Span::empty(), }) ], + by_name: false, overwrite: false, source: Some(Box::new(Query { with: None, @@ -6107,6 +6108,7 @@ fn test_simple_postgres_insert_with_alias() { span: Span::empty(), }) ], + by_name: false, overwrite: false, source: Some(Box::new(Query { with: None, @@ -6189,6 +6191,7 @@ fn test_simple_insert_with_quoted_alias() { span: Span::empty(), }) ], + by_name: false, overwrite: false, source: Some(Box::new(Query { with: None, From 3aa09e643e297ab6ab461e536ea6e6ef69f7c883 Mon Sep 17 00:00:00 2001 From: finchxxia <13153363548@163.com> Date: Mon, 13 Jul 2026 13:19:03 +0800 Subject: [PATCH 2/2] fix: gate INSERT BY NAME by dialect capability --- src/dialect/databricks.rs | 4 +++ src/dialect/generic.rs | 4 +++ src/dialect/mod.rs | 7 ++++ src/parser/mod.rs | 2 +- tests/sqlparser_databricks.rs | 62 +++++++++++++++++------------------ 5 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/dialect/databricks.rs b/src/dialect/databricks.rs index 679d335d70..d7a9d25fa8 100644 --- a/src/dialect/databricks.rs +++ b/src/dialect/databricks.rs @@ -95,6 +95,10 @@ impl Dialect for DatabricksDialect { true } + fn supports_insert_by_name(&self) -> bool { + true + } + /// See fn supports_bang_not_operator(&self) -> bool { true diff --git a/src/dialect/generic.rs b/src/dialect/generic.rs index d408cb181a..4b51108031 100644 --- a/src/dialect/generic.rs +++ b/src/dialect/generic.rs @@ -320,4 +320,8 @@ impl Dialect for GenericDialect { fn supports_aliased_function_args(&self) -> bool { true } + + fn supports_insert_by_name(&self) -> bool { + true + } } diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index efabfeb1f4..678c4ebbeb 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -1353,6 +1353,13 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if this dialect supports `INSERT INTO ... BY NAME ...`. + /// + /// Databricks: + fn supports_insert_by_name(&self) -> bool { + false + } + /// 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/parser/mod.rs b/src/parser/mod.rs index 5d7068f642..c3948f8f84 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -18350,7 +18350,7 @@ impl<'a> Parser<'a> { let partitioned = self.parse_insert_partition()?; by_name = columns.is_empty() - && dialect_of!(self is DatabricksDialect | GenericDialect) + && self.dialect.supports_insert_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) { diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 47ccebaddc..8f30e17946 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -168,37 +168,6 @@ fn test_databricks_lambdas() { databricks().verified_expr("transform(array(1, 2, 3), x -> x + 1)"); } -#[test] -fn test_databricks_insert_by_name() { - match databricks().verified_stmt("INSERT INTO target BY NAME SELECT 1 AS a") { - Statement::Insert(Insert { - by_name, - columns, - has_table_keyword, - .. - }) => { - assert!(by_name); - assert!(columns.is_empty()); - assert!(!has_table_keyword); - } - _ => unreachable!(), - } - - match databricks().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", - ) { - Statement::Insert(Insert { - by_name, - has_table_keyword, - .. - }) => { - assert!(by_name); - assert!(has_table_keyword); - } - _ => unreachable!(), - } -} - #[test] fn test_values_clause() { let values = Values { @@ -769,3 +738,34 @@ fn parse_cte_without_as() { .parse_sql_statements("WITH cte (SELECT 1) SELECT * FROM cte") .is_err()); } + +#[test] +fn test_databricks_insert_by_name() { + match databricks_and_generic().verified_stmt("INSERT INTO target BY NAME SELECT 1 AS a") { + Statement::Insert(Insert { + by_name, + columns, + has_table_keyword, + .. + }) => { + assert!(by_name); + assert!(columns.is_empty()); + assert!(!has_table_keyword); + } + _ => unreachable!(), + } + + match 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", + ) { + Statement::Insert(Insert { + by_name, + has_table_keyword, + .. + }) => { + assert!(by_name); + assert!(has_table_keyword); + } + _ => unreachable!(), + } +}