@@ -3,7 +3,7 @@ use crate::{
33 PrimaryKeyTrait , QueryTrait ,
44} ;
55use core:: marker:: PhantomData ;
6- use sea_query:: { Expr , InsertStatement , OnConflict , ValueTuple } ;
6+ use sea_query:: { Expr , InsertStatement , Keyword , OnConflict , SimpleExpr , Value , ValueTuple } ;
77
88/// Performs INSERT operations on a ActiveModel
99#[ derive( Debug ) ]
@@ -112,7 +112,7 @@ where
112112 ///
113113 /// # Panics
114114 ///
115- /// Panics if the column value has discrepancy across rows
115+ /// Panics if the rows have different column sets from what've previously been cached in the query statement
116116 #[ allow( clippy:: should_implement_trait) ]
117117 pub fn add < M > ( mut self , m : M ) -> Self
118118 where
@@ -149,15 +149,91 @@ where
149149 self
150150 }
151151
152+ /// Add many Models to Self. This is the legacy implementation priori to `1.1.3`.
153+ ///
154+ /// # Panics
155+ ///
156+ /// Panics if the rows have different column sets
157+ #[ deprecated(
158+ since = "1.1.3" ,
159+ note = "Please use [`Insert::add_many`] which does not panic"
160+ ) ]
161+ pub fn add_multi < M , I > ( mut self , models : I ) -> Self
162+ where
163+ M : IntoActiveModel < A > ,
164+ I : IntoIterator < Item = M > ,
165+ {
166+ for model in models. into_iter ( ) {
167+ self = self . add ( model) ;
168+ }
169+ self
170+ }
171+
152172 /// Add many Models to Self
153173 pub fn add_many < M , I > ( mut self , models : I ) -> Self
154174 where
155175 M : IntoActiveModel < A > ,
156176 I : IntoIterator < Item = M > ,
157177 {
178+ let mut columns: Vec < _ > = <A :: Entity as EntityTrait >:: Column :: iter ( )
179+ . map ( |_| None )
180+ . collect ( ) ;
181+ let mut null_value: Vec < Option < Value > > =
182+ std:: iter:: repeat ( None ) . take ( columns. len ( ) ) . collect ( ) ;
183+ let mut all_values: Vec < Vec < SimpleExpr > > = Vec :: new ( ) ;
184+
158185 for model in models. into_iter ( ) {
159- self = self . add ( model) ;
186+ let mut am: A = model. into_active_model ( ) ;
187+ self . primary_key =
188+ if !<<A :: Entity as EntityTrait >:: PrimaryKey as PrimaryKeyTrait >:: auto_increment ( ) {
189+ am. get_primary_key_value ( )
190+ } else {
191+ None
192+ } ;
193+ let mut values = Vec :: with_capacity ( columns. len ( ) ) ;
194+ for ( idx, col) in <A :: Entity as EntityTrait >:: Column :: iter ( ) . enumerate ( ) {
195+ let av = am. take ( col) ;
196+ match av {
197+ ActiveValue :: Set ( value) | ActiveValue :: Unchanged ( value) => {
198+ columns[ idx] = Some ( col) ; // mark the column as used
199+ null_value[ idx] = Some ( value. as_null ( ) ) ; // store the null value with the correct type
200+ values. push ( col. save_as ( Expr :: val ( value) ) ) ; // same as add() above
201+ }
202+ ActiveValue :: NotSet => {
203+ values. push ( SimpleExpr :: Keyword ( Keyword :: Null ) ) ; // indicate a missing value
204+ }
205+ }
206+ }
207+ all_values. push ( values) ;
208+ }
209+
210+ if !all_values. is_empty ( ) {
211+ // filter only used column
212+ self . query . columns ( columns. iter ( ) . cloned ( ) . flatten ( ) ) ;
213+
214+ // flag used column
215+ self . columns = columns. iter ( ) . map ( Option :: is_some) . collect ( ) ;
216+ }
217+
218+ for values in all_values {
219+ // since we've aligned the column set, this never panics
220+ self . query
221+ . values_panic ( values. into_iter ( ) . enumerate ( ) . filter_map ( |( i, v) | {
222+ if columns[ i] . is_some ( ) {
223+ // only if the column is used
224+ if !matches ! ( v, SimpleExpr :: Keyword ( Keyword :: Null ) ) {
225+ // use the value expression
226+ Some ( v)
227+ } else {
228+ // use null as standin, which must be Some
229+ null_value[ i] . clone ( ) . map ( SimpleExpr :: Value )
230+ }
231+ } else {
232+ None
233+ }
234+ } ) ) ;
160235 }
236+
161237 self
162238 }
163239
@@ -209,16 +285,15 @@ where
209285 self
210286 }
211287
212- /// Allow insert statement return safely if inserting nothing.
213- /// The database will not be affected.
288+ /// Allow insert statement to return without error if nothing's been inserted
214289 pub fn do_nothing ( self ) -> TryInsert < A >
215290 where
216291 A : ActiveModelTrait ,
217292 {
218293 TryInsert :: from_insert ( self )
219294 }
220295
221- /// alias to do_nothing
296+ /// Alias to ` do_nothing`
222297 pub fn on_empty_do_nothing ( self ) -> TryInsert < A >
223298 where
224299 A : ActiveModelTrait ,
@@ -393,8 +468,11 @@ where
393468mod tests {
394469 use sea_query:: OnConflict ;
395470
396- use crate :: tests_cfg:: cake:: { self } ;
397- use crate :: { ActiveValue , DbBackend , DbErr , EntityTrait , Insert , IntoActiveModel , QueryTrait } ;
471+ use crate :: tests_cfg:: { cake, cake_filling} ;
472+ use crate :: {
473+ ActiveValue , DbBackend , DbErr , EntityTrait , Insert , IntoActiveModel , NotSet , QueryTrait ,
474+ Set ,
475+ } ;
398476
399477 #[ test]
400478 fn insert_1 ( ) {
@@ -439,7 +517,7 @@ mod tests {
439517 }
440518
441519 #[ test]
442- fn insert_4 ( ) {
520+ fn insert_many_1 ( ) {
443521 assert_eq ! (
444522 Insert :: <cake:: ActiveModel >:: new( )
445523 . add_many( [
@@ -459,22 +537,41 @@ mod tests {
459537 }
460538
461539 #[ test]
462- #[ should_panic( expected = "columns mismatch" ) ]
463- fn insert_5 ( ) {
464- let apple = cake:: ActiveModel {
465- name : ActiveValue :: set ( "Apple" . to_owned ( ) ) ,
466- ..Default :: default ( )
540+ fn insert_many_2 ( ) {
541+ assert_eq ! (
542+ Insert :: <cake:: ActiveModel >:: new( )
543+ . add_many( [
544+ cake:: ActiveModel {
545+ id: NotSet ,
546+ name: Set ( "Apple Pie" . to_owned( ) ) ,
547+ } ,
548+ cake:: ActiveModel {
549+ id: NotSet ,
550+ name: Set ( "Orange Scone" . to_owned( ) ) ,
551+ }
552+ ] )
553+ . build( DbBackend :: Postgres )
554+ . to_string( ) ,
555+ r#"INSERT INTO "cake" ("name") VALUES ('Apple Pie'), ('Orange Scone')"# ,
556+ ) ;
557+ }
558+
559+ #[ test]
560+ fn insert_many_3 ( ) {
561+ let apple = cake_filling:: ActiveModel {
562+ cake_id : ActiveValue :: set ( 2 ) ,
563+ filling_id : ActiveValue :: NotSet ,
467564 } ;
468- let orange = cake :: ActiveModel {
469- id : ActiveValue :: set ( 2 ) ,
470- name : ActiveValue :: set ( "Orange" . to_owned ( ) ) ,
565+ let orange = cake_filling :: ActiveModel {
566+ cake_id : ActiveValue :: NotSet ,
567+ filling_id : ActiveValue :: set ( 3 ) ,
471568 } ;
472569 assert_eq ! (
473- Insert :: <cake :: ActiveModel >:: new( )
570+ Insert :: <cake_filling :: ActiveModel >:: new( )
474571 . add_many( [ apple, orange] )
475572 . build( DbBackend :: Postgres )
476573 . to_string( ) ,
477- r#"INSERT INTO "cake " ("id ", "name ") VALUES (NULL, 'Apple' ), (2, 'Orange' )"# ,
574+ r#"INSERT INTO "cake_filling " ("cake_id ", "filling_id ") VALUES (2, NULL ), (NULL, 3 )"# ,
478575 ) ;
479576 }
480577
0 commit comments