iffyio commented on code in PR #2148:
URL: 
https://github.com/apache/datafusion-sqlparser-rs/pull/2148#discussion_r2716169592


##########
src/ast/dml.rs:
##########
@@ -90,10 +90,80 @@ pub struct Insert {
     ///
     /// [ClickHouse formats JSON 
insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
     pub format_clause: Option<InputFormatClause>,
+    /// For multi-table insert: `INSERT FIRST` vs `INSERT ALL`
+    ///
+    /// When `true`, this is an `INSERT FIRST` statement (only the first 
matching WHEN clause is executed).
+    /// When `false` with non-empty `clauses`, this is an `INSERT ALL` 
statement.
+    ///
+    /// See: 
<https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+    pub insert_first: bool,
+    /// For multi-table insert: additional INTO clauses (unconditional)
+    ///
+    /// Used for `INSERT ALL INTO t1 INTO t2 ... SELECT ...`
+    ///
+    /// See: 
<https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+    pub multi_table_into_clauses: Vec<MultiTableInsertIntoClause>,
+    /// For conditional multi-table insert: WHEN clauses
+    ///
+    /// Used for `INSERT ALL/FIRST WHEN cond THEN INTO t1 ... SELECT ...`
+    ///
+    /// See: 
<https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+    pub multi_table_when_clauses: Vec<MultiTableInsertWhenClause>,
+    /// For conditional multi-table insert: ELSE clause
+    ///
+    /// See: 
<https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+    pub multi_table_else_clause: Option<Vec<MultiTableInsertIntoClause>>,
 }
 
 impl Display for Insert {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        // Check if this is a Snowflake multi-table insert
+        let is_multi_table = !self.multi_table_into_clauses.is_empty()
+            || !self.multi_table_when_clauses.is_empty();
+
+        if is_multi_table {

Review Comment:
   Can we would need to merge this logic with the rest of the function, instead 
vs having a clause that is dedicated to the multi_table mode currently? with 
the latter is unclear how much is covered by the clause vs the rest of the 
statement and whether we could end up with incorrect formatting as a result. 
essentially that we update the current function at the points where we have new 
entries to display
   



##########
src/dialect/snowflake.rs:
##########
@@ -318,6 +320,30 @@ impl Dialect for SnowflakeDialect {
             parser.prev_token();
         }
 
+        // Check for multi-table INSERT (Snowflake specific)
+        // INSERT [OVERWRITE] ALL ... or INSERT [OVERWRITE] FIRST ...

Review Comment:
   ```suggestion
           // Check for multi-table INSERT
           // `INSERT [OVERWRITE] ALL ... or INSERT [OVERWRITE] FIRST ...`
   ```



##########
src/dialect/snowflake.rs:
##########
@@ -1628,3 +1654,173 @@ fn parse_show_objects(terse: bool, parser: &mut Parser) 
-> Result<Statement, Par
         show_options,
     }))
 }
+
+/// Parse multi-table INSERT statement.
+///
+/// Syntax:
+/// ```sql
+/// -- Unconditional multi-table insert
+/// INSERT [ OVERWRITE ] ALL
+///   intoClause [ ... ]
+/// <subquery>
+///
+/// -- Conditional multi-table insert
+/// INSERT [ OVERWRITE ] { FIRST | ALL }
+///   { WHEN <condition> THEN intoClause [ ... ] }
+///   [ ... ]
+///   [ ELSE intoClause ]
+/// <subquery>
+/// ```
+///
+/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+fn parse_multi_table_insert(
+    parser: &mut Parser,
+    insert_token: TokenWithSpan,
+    overwrite: bool,
+    insert_first: bool,
+) -> Result<Statement, ParserError> {
+    // Check if this is conditional (has WHEN clauses) or unconditional 
(direct INTO clauses)
+    let is_conditional = parser.peek_keyword(Keyword::WHEN);
+
+    let (multi_table_into_clauses, multi_table_when_clauses, 
multi_table_else_clause) =
+        if is_conditional {
+            // Conditional multi-table insert: WHEN clauses
+            let (when_clauses, else_clause) = 
parse_multi_table_insert_when_clauses(parser)?;
+            (vec![], when_clauses, else_clause)
+        } else {
+            // Unconditional multi-table insert: direct INTO clauses
+            let into_clauses = parse_multi_table_insert_into_clauses(parser)?;
+            (into_clauses, vec![], None)
+        };
+
+    // Parse the source query
+    let source = parser.parse_query()?;
+
+    Ok(Statement::Insert(Insert {
+        insert_token: insert_token.into(),
+        or: None,
+        ignore: false,
+        into: false,
+        table: TableObject::TableName(ObjectName(vec![])), // Not used for 
multi-table insert
+        table_alias: None,
+        columns: vec![],
+        overwrite,
+        source: Some(source),
+        assignments: vec![],
+        partitioned: None,
+        after_columns: vec![],
+        has_table_keyword: false,
+        on: None,
+        returning: None,
+        replace_into: false,
+        priority: None,
+        insert_alias: None,
+        settings: None,
+        format_clause: None,
+        insert_first,
+        multi_table_into_clauses,
+        multi_table_when_clauses,
+        multi_table_else_clause,
+    }))
+}
+
+/// Parse one or more INTO clauses for multi-table INSERT.
+fn parse_multi_table_insert_into_clauses(
+    parser: &mut Parser,
+) -> Result<Vec<MultiTableInsertIntoClause>, ParserError> {
+    let mut into_clauses = vec![];
+    while parser.parse_keyword(Keyword::INTO) {
+        into_clauses.push(parse_multi_table_insert_into_clause(parser)?);
+    }
+    if into_clauses.is_empty() {
+        return parser.expected("INTO clause in multi-table INSERT", 
parser.peek_token());
+    }
+    Ok(into_clauses)
+}
+
+/// Parse a single INTO clause for multi-table INSERT.
+///
+/// Syntax: `INTO <table> [ ( <columns> ) ] [ VALUES ( <values> ) ]`
+fn parse_multi_table_insert_into_clause(
+    parser: &mut Parser,
+) -> Result<MultiTableInsertIntoClause, ParserError> {
+    let table_name = parser.parse_object_name(false)?;
+
+    // Parse optional column list
+    let columns = if parser.peek_token() == Token::LParen && 
!parser.peek_keyword(Keyword::VALUES) {

Review Comment:
   It looks like this and the nested if statement can be collapsed to `if 
self.consume_token(LParen)`?



##########
src/dialect/snowflake.rs:
##########
@@ -1628,3 +1654,173 @@ fn parse_show_objects(terse: bool, parser: &mut Parser) 
-> Result<Statement, Par
         show_options,
     }))
 }
+
+/// Parse multi-table INSERT statement.
+///
+/// Syntax:
+/// ```sql
+/// -- Unconditional multi-table insert
+/// INSERT [ OVERWRITE ] ALL
+///   intoClause [ ... ]
+/// <subquery>
+///
+/// -- Conditional multi-table insert
+/// INSERT [ OVERWRITE ] { FIRST | ALL }
+///   { WHEN <condition> THEN intoClause [ ... ] }
+///   [ ... ]
+///   [ ELSE intoClause ]
+/// <subquery>
+/// ```
+///
+/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+fn parse_multi_table_insert(
+    parser: &mut Parser,
+    insert_token: TokenWithSpan,
+    overwrite: bool,
+    insert_first: bool,
+) -> Result<Statement, ParserError> {
+    // Check if this is conditional (has WHEN clauses) or unconditional 
(direct INTO clauses)
+    let is_conditional = parser.peek_keyword(Keyword::WHEN);
+
+    let (multi_table_into_clauses, multi_table_when_clauses, 
multi_table_else_clause) =
+        if is_conditional {
+            // Conditional multi-table insert: WHEN clauses
+            let (when_clauses, else_clause) = 
parse_multi_table_insert_when_clauses(parser)?;
+            (vec![], when_clauses, else_clause)
+        } else {
+            // Unconditional multi-table insert: direct INTO clauses
+            let into_clauses = parse_multi_table_insert_into_clauses(parser)?;
+            (into_clauses, vec![], None)
+        };
+
+    // Parse the source query
+    let source = parser.parse_query()?;
+
+    Ok(Statement::Insert(Insert {
+        insert_token: insert_token.into(),
+        or: None,
+        ignore: false,
+        into: false,
+        table: TableObject::TableName(ObjectName(vec![])), // Not used for 
multi-table insert
+        table_alias: None,
+        columns: vec![],
+        overwrite,
+        source: Some(source),
+        assignments: vec![],
+        partitioned: None,
+        after_columns: vec![],
+        has_table_keyword: false,
+        on: None,
+        returning: None,
+        replace_into: false,
+        priority: None,
+        insert_alias: None,
+        settings: None,
+        format_clause: None,
+        insert_first,
+        multi_table_into_clauses,
+        multi_table_when_clauses,
+        multi_table_else_clause,
+    }))
+}
+
+/// Parse one or more INTO clauses for multi-table INSERT.
+fn parse_multi_table_insert_into_clauses(
+    parser: &mut Parser,
+) -> Result<Vec<MultiTableInsertIntoClause>, ParserError> {
+    let mut into_clauses = vec![];
+    while parser.parse_keyword(Keyword::INTO) {
+        into_clauses.push(parse_multi_table_insert_into_clause(parser)?);
+    }
+    if into_clauses.is_empty() {
+        return parser.expected("INTO clause in multi-table INSERT", 
parser.peek_token());
+    }
+    Ok(into_clauses)
+}
+
+/// Parse a single INTO clause for multi-table INSERT.
+///
+/// Syntax: `INTO <table> [ ( <columns> ) ] [ VALUES ( <values> ) ]`
+fn parse_multi_table_insert_into_clause(
+    parser: &mut Parser,
+) -> Result<MultiTableInsertIntoClause, ParserError> {
+    let table_name = parser.parse_object_name(false)?;
+
+    // Parse optional column list
+    let columns = if parser.peek_token() == Token::LParen && 
!parser.peek_keyword(Keyword::VALUES) {
+        // Check if this is a column list (not VALUES)
+        let peeked = parser.peek_tokens::<2>();

Review Comment:
   can we use pattern matching instead of array indexing the result? since the 
latter is error prone. just in case, if we only need to peek one item we can 
call `peek_token_ref` instead



##########
src/dialect/snowflake.rs:
##########
@@ -1628,3 +1654,173 @@ fn parse_show_objects(terse: bool, parser: &mut Parser) 
-> Result<Statement, Par
         show_options,
     }))
 }
+
+/// Parse multi-table INSERT statement.
+///
+/// Syntax:
+/// ```sql
+/// -- Unconditional multi-table insert
+/// INSERT [ OVERWRITE ] ALL
+///   intoClause [ ... ]
+/// <subquery>
+///
+/// -- Conditional multi-table insert
+/// INSERT [ OVERWRITE ] { FIRST | ALL }
+///   { WHEN <condition> THEN intoClause [ ... ] }
+///   [ ... ]
+///   [ ELSE intoClause ]
+/// <subquery>
+/// ```
+///
+/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
+fn parse_multi_table_insert(
+    parser: &mut Parser,
+    insert_token: TokenWithSpan,
+    overwrite: bool,
+    insert_first: bool,
+) -> Result<Statement, ParserError> {
+    // Check if this is conditional (has WHEN clauses) or unconditional 
(direct INTO clauses)
+    let is_conditional = parser.peek_keyword(Keyword::WHEN);
+
+    let (multi_table_into_clauses, multi_table_when_clauses, 
multi_table_else_clause) =
+        if is_conditional {
+            // Conditional multi-table insert: WHEN clauses
+            let (when_clauses, else_clause) = 
parse_multi_table_insert_when_clauses(parser)?;
+            (vec![], when_clauses, else_clause)
+        } else {
+            // Unconditional multi-table insert: direct INTO clauses
+            let into_clauses = parse_multi_table_insert_into_clauses(parser)?;
+            (into_clauses, vec![], None)
+        };
+
+    // Parse the source query
+    let source = parser.parse_query()?;
+
+    Ok(Statement::Insert(Insert {
+        insert_token: insert_token.into(),
+        or: None,
+        ignore: false,
+        into: false,
+        table: TableObject::TableName(ObjectName(vec![])), // Not used for 
multi-table insert
+        table_alias: None,
+        columns: vec![],
+        overwrite,
+        source: Some(source),
+        assignments: vec![],
+        partitioned: None,
+        after_columns: vec![],
+        has_table_keyword: false,
+        on: None,
+        returning: None,
+        replace_into: false,
+        priority: None,
+        insert_alias: None,
+        settings: None,
+        format_clause: None,
+        insert_first,
+        multi_table_into_clauses,
+        multi_table_when_clauses,
+        multi_table_else_clause,
+    }))
+}
+
+/// Parse one or more INTO clauses for multi-table INSERT.
+fn parse_multi_table_insert_into_clauses(
+    parser: &mut Parser,
+) -> Result<Vec<MultiTableInsertIntoClause>, ParserError> {
+    let mut into_clauses = vec![];
+    while parser.parse_keyword(Keyword::INTO) {
+        into_clauses.push(parse_multi_table_insert_into_clause(parser)?);
+    }
+    if into_clauses.is_empty() {
+        return parser.expected("INTO clause in multi-table INSERT", 
parser.peek_token());
+    }
+    Ok(into_clauses)
+}
+
+/// Parse a single INTO clause for multi-table INSERT.
+///
+/// Syntax: `INTO <table> [ ( <columns> ) ] [ VALUES ( <values> ) ]`
+fn parse_multi_table_insert_into_clause(
+    parser: &mut Parser,
+) -> Result<MultiTableInsertIntoClause, ParserError> {
+    let table_name = parser.parse_object_name(false)?;
+
+    // Parse optional column list
+    let columns = if parser.peek_token() == Token::LParen && 
!parser.peek_keyword(Keyword::VALUES) {

Review Comment:
   is the negated peek values condition always true?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to