toasty_driver_integration_suite/tests/
crud_batch_create.rs1use crate::prelude::*;
4
5use toasty::Json;
6use toasty_core::{
7 driver::{Operation, operation::Transaction},
8 schema::db,
9 stmt::{Expr, ExprFunc, ExprSet, Statement, TableRef, Value},
10};
11
12#[driver_test(scenario(crate::scenarios::two_models))]
13pub async fn batch_create_empty(test: &mut Test) -> Result<()> {
14 let mut db = setup(test).await;
15
16 let res = Post::create_many().exec(&mut db).await?;
17 assert!(res.is_empty());
18 Ok(())
19}
20
21#[driver_test(scenario(crate::scenarios::two_models))]
22pub async fn batch_create_one(test: &mut Test) -> Result<()> {
23 let mut db = setup(test).await;
24
25 test.log().clear();
26 let res = Post::create_many()
27 .item(Post::create().title("hello"))
28 .exec(&mut db)
29 .await?;
30
31 assert_eq!(1, res.len());
32 assert_eq!(res[0].title, "hello");
33
34 if test.capability().sql() {
36 assert_struct!(test.log().pop_op(), Operation::Insert(_));
37 assert!(test.log().is_empty());
38 }
39
40 let reloaded: Vec<_> = Post::filter_by_id(res[0].id).exec(&mut db).await?;
41 assert_eq!(1, reloaded.len());
42 assert_eq!(reloaded[0].id, res[0].id);
43 Ok(())
44}
45
46#[driver_test(scenario(crate::scenarios::two_models))]
47pub async fn batch_create_many(test: &mut Test) -> Result<()> {
48 let mut db = setup(test).await;
49
50 test.log().clear();
51 let res = Post::create_many()
52 .item(Post::create().title("todo 1"))
53 .item(Post::create().title("todo 2"))
54 .exec(&mut db)
55 .await?;
56
57 assert_eq!(2, res.len());
58 assert_eq!(res[0].title, "todo 1");
59 assert_eq!(res[1].title, "todo 2");
60
61 if test.capability().sql() {
64 assert_struct!(test.log().pop_op(), Operation::Insert(_));
65 assert!(test.log().is_empty());
66 }
67
68 for post in &res {
69 let reloaded: Vec<_> = Post::filter_by_id(post.id).exec(&mut db).await?;
70 assert_eq!(1, reloaded.len());
71 assert_eq!(reloaded[0].id, post.id);
72 }
73 Ok(())
74}
75
76#[driver_test(requires(and(auto_increment, returning_from_insert)))]
77pub async fn batch_create_many_auto_increment(test: &mut Test) -> Result<()> {
78 #[derive(Debug, toasty::Model)]
79 struct Item {
80 #[key]
81 #[auto]
82 id: u64,
83
84 name: String,
85 }
86
87 let mut db = test.setup_db(models!(Item)).await;
88 let items = Item::create_many()
89 .item(Item::create().name("one"))
90 .item(Item::create().name("two"))
91 .exec(&mut db)
92 .await?;
93
94 assert_struct!(items, [{ id: 1, name: "one" }, { id: 2, name: "two" }]);
95
96 Ok(())
97}
98
99#[driver_test(requires(and(auto_increment, not(returning_from_insert))))]
100pub async fn batch_create_many_auto_increment_requires_returning(test: &mut Test) -> Result<()> {
101 #[derive(Debug, toasty::Model)]
102 struct Generated {
103 #[key]
104 #[auto]
105 id: u64,
106
107 name: String,
108 }
109
110 #[derive(Debug, toasty::Model)]
111 struct Manual {
112 #[key]
113 id: u64,
114
115 name: String,
116 }
117
118 let mut db = test.setup_db(models!(Generated, Manual)).await;
119
120 test.log().clear();
121 let err = assert_err!(
122 Generated::create_many()
123 .item(Generated::create().name("one"))
124 .item(Generated::create().name("two"))
125 .exec(&mut db)
126 .await
127 );
128
129 assert!(err.is_unsupported_feature());
130 assert!(err.to_string().contains(test.capability().driver_name));
131 assert!(err.to_string().contains("generateds.id"));
132 assert!(test.log().is_empty());
133
134 let items = Manual::create_many()
135 .item(Manual::create().id(10).name("one"))
136 .item(Manual::create().id(20).name("two"))
137 .exec(&mut db)
138 .await?;
139
140 assert_struct!(items, [{ id: 10, name: "one" }, { id: 20, name: "two" }]);
141 assert_struct!(test.log().pop_op(), Operation::Insert({ ret: None }));
142 assert!(test.log().is_empty());
143
144 Ok(())
145}
146
147#[driver_test(id(ID), requires(returning_from_insert))]
156pub async fn batch_create_with_null_field(test: &mut Test) -> Result<()> {
157 #[derive(Debug, toasty::Model)]
158 struct Item {
159 #[key]
160 #[auto]
161 id: ID,
162 name: Option<String>,
163 }
164
165 let mut db = test.setup_db(models!(Item)).await;
166
167 let res = Item::create_many()
168 .item(Item::create().name("n1"))
169 .item(Item::create())
170 .item(Item::create().name("n3"))
171 .exec(&mut db)
172 .await?;
173
174 assert_eq!(3, res.len());
175 assert_eq!(res[0].name.as_deref(), Some("n1"));
176 assert_eq!(res[1].name, None);
177 assert_eq!(res[2].name.as_deref(), Some("n3"));
178
179 let reloaded = Item::get_by_id(&mut db, &res[1].id).await?;
180 assert_eq!(reloaded.name, None);
181 Ok(())
182}
183
184#[driver_test(requires(insert_values_unnest))]
185pub async fn batch_create_uses_unnest_array_params(test: &mut Test) -> Result<()> {
186 #[derive(Debug, toasty::Model)]
187 struct Item {
188 #[key]
189 id: String,
190 name: Option<String>,
191 }
192
193 let mut db = test.setup_db(models!(Item)).await;
194
195 test.log().clear();
196 Item::create_many()
197 .item(Item::create().id("item-1").name("n1"))
198 .item(Item::create().id("item-2"))
199 .exec(&mut db)
200 .await?;
201
202 let Operation::Insert(op) = test.log().pop_op() else {
203 panic!("expected Insert operation");
204 };
205 let Statement::Insert(insert) = op.stmt else {
206 panic!("expected Insert statement");
207 };
208 let ExprSet::Select(select) = insert.source.body else {
209 panic!("expected Select insert source");
210 };
211 let [TableRef::RowsFrom(funcs)] = select.source.as_table_unwrap().tables.as_slice() else {
212 panic!("expected ROWS FROM table source");
213 };
214 let [ExprFunc::Unnest(ids), ExprFunc::Unnest(names)] = funcs.as_slice() else {
215 panic!("expected one unnest function per inserted column");
216 };
217
218 assert_eq!(ids.arg.as_ref(), &Expr::arg(0));
219 assert_eq!(names.arg.as_ref(), &Expr::arg(1));
220 assert_eq!(op.params.len(), 2);
221 assert_eq!(
222 op.params[0].value,
223 Value::List(vec![Value::from("item-1"), Value::from("item-2")])
224 );
225 assert_eq!(op.params[0].ty, db::Type::list(db::Type::Text));
226 assert_eq!(
227 op.params[1].value,
228 Value::List(vec![Value::from("n1"), Value::Null])
229 );
230 assert_eq!(op.params[1].ty, db::Type::list(db::Type::Text));
231 assert!(test.log().is_empty());
232
233 let first = Item::get_by_id(&mut db, "item-1").await?;
236 assert_eq!(first.name.as_deref(), Some("n1"));
237
238 let second = Item::get_by_id(&mut db, "item-2").await?;
239 assert_eq!(second.name, None);
240
241 Ok(())
242}
243
244#[driver_test(requires(and(native_json, native_jsonb)))]
248pub async fn batch_create_document_columns(test: &mut Test) -> Result<()> {
249 #[derive(Debug, toasty::Model)]
250 struct Doc {
251 #[key]
252 id: String,
253 #[column(type = "json")]
254 text_encoded: Json<String>,
255 #[column(type = "jsonb")]
256 binary_encoded: Json<String>,
257 }
258
259 let mut db = test.setup_db(models!(Doc)).await;
260
261 let res = Doc::create_many()
262 .item(
263 Doc::create()
264 .id("a")
265 .text_encoded("one".to_string())
266 .binary_encoded("two".to_string()),
267 )
268 .item(
269 Doc::create()
270 .id("b")
271 .text_encoded("three".to_string())
272 .binary_encoded("four".to_string()),
273 )
274 .exec(&mut db)
275 .await?;
276
277 assert_eq!(2, res.len());
278
279 let reloaded = Doc::get_by_id(&mut db, "b").await?;
280 assert_struct!(reloaded, {
281 text_encoded: Json(== "three"),
282 binary_encoded: Json(== "four"),
283 });
284
285 Ok(())
286}
287
288#[driver_test]
290#[should_panic]
291pub async fn batch_create_fails_if_any_record_missing_fields(test: &mut Test) -> Result<()> {
292 #[derive(Debug, toasty::Model)]
293 struct User {
294 #[key]
295 email: String,
296
297 #[allow(dead_code)]
298 name: String,
299 }
300
301 let mut db = test.setup_db(models!(User)).await;
302
303 let res = User::create_many()
304 .item(User::create().email("user1@example.com").name("User 1"))
305 .item(User::create().email("user2@example.com"))
306 .exec(&mut db)
307 .await?;
308
309 assert!(res.is_empty());
310
311 let users: Vec<_> = User::filter_by_email("me@carllerche.com")
312 .exec(&mut db)
313 .await?;
314
315 assert!(users.is_empty());
316 Ok(())
317}
318
319#[driver_test(scenario(crate::scenarios::user_unique_email))]
320pub async fn batch_create_model_with_unique_field_index_all_unique(test: &mut Test) -> Result<()> {
321 let mut db = setup(test).await;
322
323 let mut res = User::create_many()
324 .item(User::create().email("user1@example.com"))
325 .item(User::create().email("user2@example.com"))
326 .exec(&mut db)
327 .await?;
328
329 assert_eq!(2, res.len());
330
331 res.sort_by_key(|user| user.email.clone());
332
333 assert_eq!(res[0].email, "user1@example.com");
334 assert_eq!(res[1].email, "user2@example.com");
335
336 for user in &res {
338 let found = User::get_by_id(&mut db, user.id).await?;
339 assert_eq!(found.id, user.id);
340 assert_eq!(found.email, user.email);
341
342 let found = User::get_by_email(&mut db, &user.email).await?;
343 assert_eq!(found.id, user.id);
344 assert_eq!(found.email, user.email);
345 }
346 Ok(())
347}
348
349#[driver_test(scenario(crate::scenarios::user_unique_email))]
350#[should_panic]
351pub async fn batch_create_model_with_unique_field_index_all_dups(test: &mut Test) -> Result<()> {
352 let mut db = setup(test).await;
353
354 let _res = User::create_many()
355 .item(User::create().email("user@example.com"))
356 .item(User::create().email("user@example.com"))
357 .exec(&mut db)
358 .await?;
359 Ok(())
360}
361
362#[driver_test(requires(sql), scenario(crate::scenarios::user_unique_email))]
365pub async fn batch_create_unique_violation_rolls_back(t: &mut Test) -> Result<()> {
366 let mut db = setup(t).await;
367
368 User::create()
370 .email("taken@example.com")
371 .exec(&mut db)
372 .await?;
373
374 t.log().clear();
375 assert_err!(
376 User::create_many()
377 .item(User::create().email("new@example.com"))
378 .item(User::create().email("taken@example.com"))
379 .exec(&mut db)
380 .await
381 );
382
383 assert!(t.log().is_empty());
385
386 let users = User::all().exec(&mut db).await?;
388 assert_eq!(1, users.len());
389
390 Ok(())
391}
392
393#[driver_test(requires(sql), scenario(crate::scenarios::two_models))]
396pub async fn batch_create_inside_transaction_uses_savepoints(t: &mut Test) -> Result<()> {
397 let mut db = setup(t).await;
398
399 t.log().clear();
400 let mut tx = db.transaction().await?;
401
402 assert_struct!(
403 t.log().pop_op(),
404 Operation::Transaction(Transaction::Start {
405 isolation: None,
406 read_only: false,
407 ..
408 })
409 );
410
411 Post::create_many()
412 .item(Post::create().title("a"))
413 .item(Post::create().title("b"))
414 .exec(&mut tx)
415 .await?;
416
417 assert_struct!(t.log().pop_op(), Operation::Insert(_));
419
420 tx.commit().await?;
421
422 assert_struct!(
423 t.log().pop_op(),
424 Operation::Transaction(Transaction::Commit)
425 );
426 assert!(t.log().is_empty());
427
428 Ok(())
429}