@@ -90,10 +90,80 @@ pub struct Insert {
9090 ///
9191 /// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
9292 pub format_clause : Option < InputFormatClause > ,
93+ /// For multi-table insert: `INSERT FIRST` vs `INSERT ALL`
94+ ///
95+ /// When `true`, this is an `INSERT FIRST` statement (only the first matching WHEN clause is executed).
96+ /// When `false` with non-empty `clauses`, this is an `INSERT ALL` statement.
97+ ///
98+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
99+ pub insert_first : bool ,
100+ /// For multi-table insert: additional INTO clauses (unconditional)
101+ ///
102+ /// Used for `INSERT ALL INTO t1 INTO t2 ... SELECT ...`
103+ ///
104+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
105+ pub multi_table_into_clauses : Vec < MultiTableInsertIntoClause > ,
106+ /// For conditional multi-table insert: WHEN clauses
107+ ///
108+ /// Used for `INSERT ALL/FIRST WHEN cond THEN INTO t1 ... SELECT ...`
109+ ///
110+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
111+ pub multi_table_when_clauses : Vec < MultiTableInsertWhenClause > ,
112+ /// For conditional multi-table insert: ELSE clause
113+ ///
114+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
115+ pub multi_table_else_clause : Option < Vec < MultiTableInsertIntoClause > > ,
93116}
94117
95118impl Display for Insert {
96119 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
120+ // Check if this is a Snowflake multi-table insert
121+ let is_multi_table = !self . multi_table_into_clauses . is_empty ( )
122+ || !self . multi_table_when_clauses . is_empty ( ) ;
123+
124+ if is_multi_table {
125+ // Snowflake multi-table INSERT format
126+ write ! ( f, "INSERT" ) ?;
127+ if self . overwrite {
128+ write ! ( f, " OVERWRITE" ) ?;
129+ }
130+ if self . insert_first {
131+ write ! ( f, " FIRST" ) ?;
132+ } else {
133+ write ! ( f, " ALL" ) ?;
134+ }
135+
136+ // Unconditional multi-table insert: INTO clauses directly after ALL
137+ for into_clause in & self . multi_table_into_clauses {
138+ SpaceOrNewline . fmt ( f) ?;
139+ write ! ( f, "{}" , into_clause) ?;
140+ }
141+
142+ // Conditional multi-table insert: WHEN clauses
143+ for when_clause in & self . multi_table_when_clauses {
144+ SpaceOrNewline . fmt ( f) ?;
145+ write ! ( f, "{}" , when_clause) ?;
146+ }
147+
148+ // ELSE clause
149+ if let Some ( else_clauses) = & self . multi_table_else_clause {
150+ SpaceOrNewline . fmt ( f) ?;
151+ write ! ( f, "ELSE" ) ?;
152+ for into_clause in else_clauses {
153+ SpaceOrNewline . fmt ( f) ?;
154+ write ! ( f, "{}" , into_clause) ?;
155+ }
156+ }
157+
158+ // Source query
159+ if let Some ( source) = & self . source {
160+ SpaceOrNewline . fmt ( f) ?;
161+ source. fmt ( f) ?;
162+ }
163+ return Ok ( ( ) ) ;
164+ }
165+
166+ // Standard INSERT format
97167 let table_name = if let Some ( alias) = & self . table_alias {
98168 format ! ( "{0} AS {alias}" , self . table)
99169 } else {
@@ -644,3 +714,92 @@ impl fmt::Display for OutputClause {
644714 }
645715 }
646716}
717+
718+ /// A WHEN clause in a conditional multi-table INSERT.
719+ ///
720+ /// Syntax:
721+ /// ```sql
722+ /// WHEN n1 > 100 THEN
723+ /// INTO t1
724+ /// INTO t2 (c1, c2) VALUES (n1, n2)
725+ /// ```
726+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
727+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
728+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
729+ pub struct MultiTableInsertWhenClause {
730+ /// The condition for this WHEN clause
731+ pub condition : Expr ,
732+ /// The INTO clauses to execute when the condition is true
733+ pub into_clauses : Vec < MultiTableInsertIntoClause > ,
734+ }
735+
736+ impl Display for MultiTableInsertWhenClause {
737+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
738+ write ! ( f, "WHEN {} THEN" , self . condition) ?;
739+ for into_clause in & self . into_clauses {
740+ SpaceOrNewline . fmt ( f) ?;
741+ write ! ( f, "{}" , into_clause) ?;
742+ }
743+ Ok ( ( ) )
744+ }
745+ }
746+
747+ /// An INTO clause in a multi-table INSERT.
748+ ///
749+ /// Syntax:
750+ /// ```sql
751+ /// INTO <target_table> [ ( <target_col_name> [ , ... ] ) ] [ VALUES ( { <source_col_name> | DEFAULT | NULL } [ , ... ] ) ]
752+ /// ```
753+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
754+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
755+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
756+ pub struct MultiTableInsertIntoClause {
757+ /// The target table
758+ pub table_name : ObjectName ,
759+ /// The target columns (optional)
760+ pub columns : Vec < Ident > ,
761+ /// The VALUES clause (optional)
762+ pub values : Option < MultiTableInsertValues > ,
763+ }
764+
765+ impl Display for MultiTableInsertIntoClause {
766+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
767+ write ! ( f, "INTO {}" , self . table_name) ?;
768+ if !self . columns . is_empty ( ) {
769+ write ! ( f, " ({})" , display_comma_separated( & self . columns) ) ?;
770+ }
771+ if let Some ( values) = & self . values {
772+ write ! ( f, " VALUES ({})" , display_comma_separated( & values. values) ) ?;
773+ }
774+ Ok ( ( ) )
775+ }
776+ }
777+
778+ /// The VALUES clause in a multi-table INSERT INTO clause.
779+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
780+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
781+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
782+ pub struct MultiTableInsertValues {
783+ /// The values to insert (can be column references, DEFAULT, or NULL)
784+ pub values : Vec < MultiTableInsertValue > ,
785+ }
786+
787+ /// A value in a multi-table INSERT VALUES clause.
788+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
789+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
790+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
791+ pub enum MultiTableInsertValue {
792+ /// A column reference or expression from the source
793+ Expr ( Expr ) ,
794+ /// The DEFAULT keyword
795+ Default ,
796+ }
797+
798+ impl Display for MultiTableInsertValue {
799+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
800+ match self {
801+ MultiTableInsertValue :: Expr ( expr) => write ! ( f, "{}" , expr) ,
802+ MultiTableInsertValue :: Default => write ! ( f, "DEFAULT" ) ,
803+ }
804+ }
805+ }
0 commit comments