@@ -96,10 +96,35 @@ pub struct Insert {
9696 ///
9797 /// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
9898 pub format_clause : Option < InputFormatClause > ,
99+ /// For Snowflake multi-table insert: specifies the type (`ALL` or `FIRST`)
100+ ///
101+ /// - `None` means this is a regular single-table INSERT
102+ /// - `Some(All)` means `INSERT ALL` (all matching WHEN clauses are executed)
103+ /// - `Some(First)` means `INSERT FIRST` (only the first matching WHEN clause is executed)
104+ ///
105+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
106+ pub multi_table_insert_type : Option < MultiTableInsertType > ,
107+ /// For multi-table insert: additional INTO clauses (unconditional)
108+ ///
109+ /// Used for `INSERT ALL INTO t1 INTO t2 ... SELECT ...`
110+ ///
111+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
112+ pub multi_table_into_clauses : Vec < MultiTableInsertIntoClause > ,
113+ /// For conditional multi-table insert: WHEN clauses
114+ ///
115+ /// Used for `INSERT ALL/FIRST WHEN cond THEN INTO t1 ... SELECT ...`
116+ ///
117+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
118+ pub multi_table_when_clauses : Vec < MultiTableInsertWhenClause > ,
119+ /// For conditional multi-table insert: ELSE clause
120+ ///
121+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
122+ pub multi_table_else_clause : Option < Vec < MultiTableInsertIntoClause > > ,
99123}
100124
101125impl Display for Insert {
102126 fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
127+ // SQLite OR conflict has a special format: INSERT OR ... INTO table_name
103128 let table_name = if let Some ( alias) = & self . table_alias {
104129 format ! ( "{0} AS {alias}" , self . table)
105130 } else {
@@ -126,29 +151,46 @@ impl Display for Insert {
126151 write ! ( f, " {hint}" ) ?;
127152 }
128153 if let Some ( priority) = self . priority {
129- write ! ( f, " {priority}" , ) ?;
154+ write ! ( f, " {priority}" ) ?;
130155 }
131156
132- write ! (
133- f,
134- "{ignore}{over}{int}{tbl} {table_name} " ,
135- table_name = table_name,
136- ignore = if self . ignore { " IGNORE" } else { "" } ,
137- over = if self . overwrite { " OVERWRITE" } else { "" } ,
138- int = if self . into { " INTO" } else { "" } ,
139- tbl = if self . has_table_keyword { " TABLE" } else { "" } ,
140- ) ?;
157+ if self . ignore {
158+ write ! ( f, " IGNORE" ) ?;
159+ }
160+
161+ if self . overwrite {
162+ write ! ( f, " OVERWRITE" ) ?;
163+ }
164+
165+ if let Some ( insert_type) = & self . multi_table_insert_type {
166+ write ! ( f, " {}" , insert_type) ?;
167+ }
168+
169+ if self . into {
170+ write ! ( f, " INTO" ) ?;
171+ }
172+
173+ if self . has_table_keyword {
174+ write ! ( f, " TABLE" ) ?;
175+ }
176+
177+ if !table_name. is_empty ( ) {
178+ write ! ( f, " {table_name} " ) ?;
179+ }
141180 }
181+
142182 if !self . columns . is_empty ( ) {
143183 write ! ( f, "({})" , display_comma_separated( & self . columns) ) ?;
144184 SpaceOrNewline . fmt ( f) ?;
145185 }
186+
146187 if let Some ( ref parts) = self . partitioned {
147188 if !parts. is_empty ( ) {
148189 write ! ( f, "PARTITION ({})" , display_comma_separated( parts) ) ?;
149190 SpaceOrNewline . fmt ( f) ?;
150191 }
151192 }
193+
152194 if !self . after_columns . is_empty ( ) {
153195 write ! ( f, "({})" , display_comma_separated( & self . after_columns) ) ?;
154196 SpaceOrNewline . fmt ( f) ?;
@@ -159,7 +201,31 @@ impl Display for Insert {
159201 SpaceOrNewline . fmt ( f) ?;
160202 }
161203
204+ for into_clause in & self . multi_table_into_clauses {
205+ SpaceOrNewline . fmt ( f) ?;
206+ write ! ( f, "{}" , into_clause) ?;
207+ }
208+
209+ for when_clause in & self . multi_table_when_clauses {
210+ SpaceOrNewline . fmt ( f) ?;
211+ write ! ( f, "{}" , when_clause) ?;
212+ }
213+
214+ if let Some ( else_clauses) = & self . multi_table_else_clause {
215+ SpaceOrNewline . fmt ( f) ?;
216+ write ! ( f, "ELSE" ) ?;
217+ for into_clause in else_clauses {
218+ SpaceOrNewline . fmt ( f) ?;
219+ write ! ( f, "{}" , into_clause) ?;
220+ }
221+ }
222+
162223 if let Some ( source) = & self . source {
224+ if !self . multi_table_into_clauses . is_empty ( )
225+ || !self . multi_table_when_clauses . is_empty ( )
226+ {
227+ SpaceOrNewline . fmt ( f) ?;
228+ }
163229 source. fmt ( f) ?;
164230 } else if !self . assignments . is_empty ( ) {
165231 write ! ( f, "SET" ) ?;
@@ -189,6 +255,7 @@ impl Display for Insert {
189255 f. write_str ( "RETURNING" ) ?;
190256 indented_list ( f, returning) ?;
191257 }
258+
192259 Ok ( ( ) )
193260 }
194261}
@@ -695,3 +762,114 @@ impl fmt::Display for OutputClause {
695762 }
696763 }
697764}
765+
766+ /// A WHEN clause in a conditional multi-table INSERT.
767+ ///
768+ /// Syntax:
769+ /// ```sql
770+ /// WHEN n1 > 100 THEN
771+ /// INTO t1
772+ /// INTO t2 (c1, c2) VALUES (n1, n2)
773+ /// ```
774+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
775+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
776+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
777+ pub struct MultiTableInsertWhenClause {
778+ /// The condition for this WHEN clause
779+ pub condition : Expr ,
780+ /// The INTO clauses to execute when the condition is true
781+ pub into_clauses : Vec < MultiTableInsertIntoClause > ,
782+ }
783+
784+ impl Display for MultiTableInsertWhenClause {
785+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
786+ write ! ( f, "WHEN {} THEN" , self . condition) ?;
787+ for into_clause in & self . into_clauses {
788+ SpaceOrNewline . fmt ( f) ?;
789+ write ! ( f, "{}" , into_clause) ?;
790+ }
791+ Ok ( ( ) )
792+ }
793+ }
794+
795+ /// An INTO clause in a multi-table INSERT.
796+ ///
797+ /// Syntax:
798+ /// ```sql
799+ /// INTO <target_table> [ ( <target_col_name> [ , ... ] ) ] [ VALUES ( { <source_col_name> | DEFAULT | NULL } [ , ... ] ) ]
800+ /// ```
801+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
802+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
803+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
804+ pub struct MultiTableInsertIntoClause {
805+ /// The target table
806+ pub table_name : ObjectName ,
807+ /// The target columns (optional)
808+ pub columns : Vec < Ident > ,
809+ /// The VALUES clause (optional)
810+ pub values : Option < MultiTableInsertValues > ,
811+ }
812+
813+ impl Display for MultiTableInsertIntoClause {
814+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
815+ write ! ( f, "INTO {}" , self . table_name) ?;
816+ if !self . columns . is_empty ( ) {
817+ write ! ( f, " ({})" , display_comma_separated( & self . columns) ) ?;
818+ }
819+ if let Some ( values) = & self . values {
820+ write ! ( f, " VALUES ({})" , display_comma_separated( & values. values) ) ?;
821+ }
822+ Ok ( ( ) )
823+ }
824+ }
825+
826+ /// The VALUES clause in a multi-table INSERT INTO clause.
827+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
828+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
829+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
830+ pub struct MultiTableInsertValues {
831+ /// The values to insert (can be column references, DEFAULT, or NULL)
832+ pub values : Vec < MultiTableInsertValue > ,
833+ }
834+
835+ /// A value in a multi-table INSERT VALUES clause.
836+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
837+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
838+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
839+ pub enum MultiTableInsertValue {
840+ /// A column reference or expression from the source
841+ Expr ( Expr ) ,
842+ /// The DEFAULT keyword
843+ Default ,
844+ }
845+
846+ impl Display for MultiTableInsertValue {
847+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
848+ match self {
849+ MultiTableInsertValue :: Expr ( expr) => write ! ( f, "{}" , expr) ,
850+ MultiTableInsertValue :: Default => write ! ( f, "DEFAULT" ) ,
851+ }
852+ }
853+ }
854+
855+ /// The type of multi-table INSERT statement(Snowflake).
856+ ///
857+ /// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
858+ #[ derive( Debug , Clone , PartialEq , PartialOrd , Eq , Ord , Hash ) ]
859+ #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
860+ #[ cfg_attr( feature = "visitor" , derive( Visit , VisitMut ) ) ]
861+ pub enum MultiTableInsertType {
862+ /// `INSERT ALL` - all matching WHEN clauses are executed
863+ All ,
864+ /// `INSERT FIRST` - only the first matching WHEN clause is executed
865+ First ,
866+ }
867+
868+ impl Display for MultiTableInsertType {
869+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
870+ match self {
871+ MultiTableInsertType :: All => write ! ( f, "ALL" ) ,
872+ MultiTableInsertType :: First => write ! ( f, "FIRST" ) ,
873+ }
874+ }
875+ }
0 commit comments