diff --git a/src/ast/dml.rs b/src/ast/dml.rs index c2fe2ffd6..5fed3e970 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 6505b209e..3f5dd5b24 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/databricks.rs b/src/dialect/databricks.rs index 679d335d7..d7a9d25fa 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 d408cb181..4b5110803 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 efabfeb1f..678c4ebbe 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/dialect/snowflake.rs b/src/dialect/snowflake.rs index b76cbc55e..0052a0f02 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 09715bbc7..c3948f8f8 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() + && 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) { 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 0ed0b4118..8f30e1794 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -738,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!(), + } +} diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 9ee6ab3d6..c0220b4d7 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,