1use crate::{
2 ConnectionTrait, DbBackend, EntityTrait, FromQueryResult, Select, SelectModel, SelectThree,
3 SelectThreeModel, SelectTwo, SelectTwoModel, Selector, SelectorRaw, SelectorTrait, error::*,
4};
5use async_stream::stream;
6use futures_util::Stream;
7use sea_query::{Expr, SelectStatement};
8use std::{marker::PhantomData, pin::Pin};
9
10pub type PinBoxStream<'db, Item> = Pin<Box<dyn Stream<Item = Item> + 'db>>;
12
13#[derive(Clone, Debug)]
15pub struct Paginator<'db, C, S>
16where
17 C: ConnectionTrait,
18 S: SelectorTrait + 'db,
19{
20 pub(crate) query: SelectStatement,
21 pub(crate) page: u64,
22 pub(crate) page_size: u64,
23 pub(crate) db: &'db C,
24 pub(crate) selector: PhantomData<S>,
25}
26
27#[derive(Clone, Debug)]
29pub struct ItemsAndPagesNumber {
30 pub number_of_items: u64,
32 pub number_of_pages: u64,
34}
35
36impl<'db, C, S> Paginator<'db, C, S>
39where
40 C: ConnectionTrait,
41 S: SelectorTrait + 'db,
42{
43 pub async fn fetch_page(&self, page: u64) -> Result<Vec<S::Item>, DbErr> {
45 let query = self
46 .query
47 .clone()
48 .limit(self.page_size)
49 .offset(self.page_size * page)
50 .to_owned();
51 let rows = self.db.query_all(&query).await?;
52 let mut buffer = Vec::with_capacity(rows.len());
53 for row in rows.into_iter() {
54 buffer.push(S::from_raw_query_result(row)?);
56 }
57 Ok(buffer)
58 }
59
60 pub async fn fetch(&self) -> Result<Vec<S::Item>, DbErr> {
62 self.fetch_page(self.page).await
63 }
64
65 pub async fn num_items(&self) -> Result<u64, DbErr> {
67 let db_backend = self.db.get_database_backend();
68 let query = SelectStatement::new()
69 .expr(Expr::cust("COUNT(*) AS num_items"))
70 .from_subquery(
71 self.query
72 .clone()
73 .reset_limit()
74 .reset_offset()
75 .clear_order_by()
76 .to_owned(),
77 "sub_query",
78 )
79 .to_owned();
80 let result = match self.db.query_one(&query).await? {
81 Some(res) => res,
82 None => return Ok(0),
83 };
84 let num_items = match db_backend {
85 DbBackend::Postgres => result.try_get::<i64>("", "num_items")? as u64,
86 _ => result.try_get::<i32>("", "num_items")? as u64,
87 };
88 Ok(num_items)
89 }
90
91 pub async fn num_pages(&self) -> Result<u64, DbErr> {
93 let num_items = self.num_items().await?;
94 let num_pages = self.compute_pages_number(num_items);
95 Ok(num_pages)
96 }
97
98 pub async fn num_items_and_pages(&self) -> Result<ItemsAndPagesNumber, DbErr> {
100 let number_of_items = self.num_items().await?;
101 let number_of_pages = self.compute_pages_number(number_of_items);
102
103 Ok(ItemsAndPagesNumber {
104 number_of_items,
105 number_of_pages,
106 })
107 }
108
109 fn compute_pages_number(&self, num_items: u64) -> u64 {
111 (num_items / self.page_size) + (num_items % self.page_size > 0) as u64
112 }
113
114 pub fn next(&mut self) {
116 self.page += 1;
117 }
118
119 pub fn cur_page(&self) -> u64 {
121 self.page
122 }
123
124 pub async fn fetch_and_next(&mut self) -> Result<Option<Vec<S::Item>>, DbErr> {
157 let vec = self.fetch().await?;
158 self.next();
159 let opt = if !vec.is_empty() { Some(vec) } else { None };
160 Ok(opt)
161 }
162
163 pub fn into_stream(mut self) -> PinBoxStream<'db, Result<Vec<S::Item>, DbErr>> {
198 Box::pin(stream! {
199 while let Some(vec) = self.fetch_and_next().await? {
200 yield Ok(vec);
201 }
202 })
203 }
204}
205
206#[async_trait::async_trait]
207pub trait PaginatorTrait<'db, C>
209where
210 C: ConnectionTrait,
211{
212 type Selector: SelectorTrait + Send + Sync + 'db;
214
215 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>;
217
218 async fn count(self, db: &'db C) -> Result<u64, DbErr>
220 where
221 Self: Send + Sized,
222 {
223 self.paginate(db, 1).num_items().await
224 }
225}
226
227impl<'db, C, S> PaginatorTrait<'db, C> for Selector<S>
228where
229 C: ConnectionTrait,
230 S: SelectorTrait + Send + Sync + 'db,
231{
232 type Selector = S;
233
234 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, S> {
235 assert!(page_size != 0, "page_size should not be zero");
236 Paginator {
237 query: self.query,
238 page: 0,
239 page_size,
240 db,
241 selector: PhantomData,
242 }
243 }
244}
245
246impl<'db, C, S> PaginatorTrait<'db, C> for SelectorRaw<S>
247where
248 C: ConnectionTrait,
249 S: SelectorTrait + Send + Sync + 'db,
250{
251 type Selector = S;
252 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, S> {
253 assert!(page_size != 0, "page_size should not be zero");
254 let sql = self.stmt.sql.trim()[6..].trim();
255 let mut query = SelectStatement::new();
256 query.expr(if let Some(values) = self.stmt.values {
257 Expr::cust_with_values(sql, values.0)
258 } else {
259 Expr::cust(sql)
260 });
261
262 Paginator {
263 query,
264 page: 0,
265 page_size,
266 db,
267 selector: PhantomData,
268 }
269 }
270}
271
272impl<'db, C, M, E> PaginatorTrait<'db, C> for Select<E>
273where
274 C: ConnectionTrait,
275 E: EntityTrait<Model = M>,
276 M: FromQueryResult + Sized + Send + Sync + 'db,
277{
278 type Selector = SelectModel<M>;
279
280 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
281 self.into_model().paginate(db, page_size)
282 }
283}
284
285impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo<E, F>
286where
287 C: ConnectionTrait,
288 E: EntityTrait<Model = M>,
289 F: EntityTrait<Model = N>,
290 M: FromQueryResult + Sized + Send + Sync + 'db,
291 N: FromQueryResult + Sized + Send + Sync + 'db,
292{
293 type Selector = SelectTwoModel<M, N>;
294
295 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
296 self.into_model().paginate(db, page_size)
297 }
298}
299
300impl<'db, C, M, N, O, E, F, G> PaginatorTrait<'db, C> for SelectThree<E, F, G>
301where
302 C: ConnectionTrait,
303 E: EntityTrait<Model = M>,
304 F: EntityTrait<Model = N>,
305 G: EntityTrait<Model = O>,
306 M: FromQueryResult + Sized + Send + Sync + 'db,
307 N: FromQueryResult + Sized + Send + Sync + 'db,
308 O: FromQueryResult + Sized + Send + Sync + 'db,
309{
310 type Selector = SelectThreeModel<M, N, O>;
311
312 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
313 self.into_model().paginate(db, page_size)
314 }
315}
316
317#[cfg(test)]
318#[cfg(feature = "mock")]
319mod tests {
320 use super::*;
321 use crate::entity::prelude::*;
322 use crate::{DatabaseConnection, DbBackend, MockDatabase, Transaction};
323 use crate::{Statement, tests_cfg::*};
324 use futures_util::TryStreamExt;
325 use pretty_assertions::assert_eq;
326 use sea_query::{Expr, SelectStatement, Value};
327 use std::sync::LazyLock;
328
329 static RAW_STMT: LazyLock<Statement> = LazyLock::new(|| {
330 Statement::from_sql_and_values(
331 DbBackend::Postgres,
332 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
333 [],
334 )
335 });
336
337 fn setup() -> (DatabaseConnection, Vec<Vec<fruit::Model>>) {
338 let page1 = vec![
339 fruit::Model {
340 id: 1,
341 name: "Blueberry".into(),
342 cake_id: Some(1),
343 },
344 fruit::Model {
345 id: 2,
346 name: "Raspberry".into(),
347 cake_id: Some(1),
348 },
349 ];
350
351 let page2 = vec![fruit::Model {
352 id: 3,
353 name: "Strawberry".into(),
354 cake_id: Some(2),
355 }];
356
357 let page3 = Vec::<fruit::Model>::new();
358
359 let db = MockDatabase::new(DbBackend::Postgres)
360 .append_query_results([page1.clone(), page2.clone(), page3.clone()])
361 .into_connection();
362
363 (db, vec![page1, page2, page3])
364 }
365
366 fn setup_num_items() -> (DatabaseConnection, i64) {
367 let num_items = 3;
368 let db = MockDatabase::new(DbBackend::Postgres)
369 .append_query_results([[maplit::btreemap! {
370 "num_items" => Into::<Value>::into(num_items),
371 }]])
372 .into_connection();
373
374 (db, num_items)
375 }
376
377 #[smol_potat::test]
378 async fn fetch_page() -> Result<(), DbErr> {
379 let (db, pages) = setup();
380
381 let paginator = fruit::Entity::find().paginate(&db, 2);
382
383 assert_eq!(paginator.fetch_page(0).await?, pages[0].clone());
384 assert_eq!(paginator.fetch_page(1).await?, pages[1].clone());
385 assert_eq!(paginator.fetch_page(2).await?, pages[2].clone());
386
387 let mut select = SelectStatement::new()
388 .exprs([
389 Expr::col((fruit::Entity, fruit::Column::Id)),
390 Expr::col((fruit::Entity, fruit::Column::Name)),
391 Expr::col((fruit::Entity, fruit::Column::CakeId)),
392 ])
393 .from(fruit::Entity)
394 .to_owned();
395
396 let query_builder = db.get_database_backend();
397 let stmts = [
398 query_builder.build(select.clone().offset(0).limit(2)),
399 query_builder.build(select.clone().offset(2).limit(2)),
400 query_builder.build(select.offset(4).limit(2)),
401 ];
402
403 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
404 Ok(())
405 }
406
407 #[smol_potat::test]
408 async fn fetch_page_raw() -> Result<(), DbErr> {
409 let (db, pages) = setup();
410
411 let paginator = fruit::Entity::find()
412 .from_raw_sql(RAW_STMT.clone())
413 .paginate(&db, 2);
414
415 assert_eq!(paginator.fetch_page(0).await?, pages[0].clone());
416 assert_eq!(paginator.fetch_page(1).await?, pages[1].clone());
417 assert_eq!(paginator.fetch_page(2).await?, pages[2].clone());
418
419 let mut select = SelectStatement::new()
420 .exprs([
421 Expr::col((fruit::Entity, fruit::Column::Id)),
422 Expr::col((fruit::Entity, fruit::Column::Name)),
423 Expr::col((fruit::Entity, fruit::Column::CakeId)),
424 ])
425 .from(fruit::Entity)
426 .to_owned();
427
428 let query_builder = db.get_database_backend();
429 let stmts = [
430 query_builder.build(select.clone().offset(0).limit(2)),
431 query_builder.build(select.clone().offset(2).limit(2)),
432 query_builder.build(select.offset(4).limit(2)),
433 ];
434
435 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
436 Ok(())
437 }
438
439 #[smol_potat::test]
440 async fn fetch() -> Result<(), DbErr> {
441 let (db, pages) = setup();
442
443 let mut paginator = fruit::Entity::find().paginate(&db, 2);
444
445 assert_eq!(paginator.fetch().await?, pages[0].clone());
446 paginator.next();
447
448 assert_eq!(paginator.fetch().await?, pages[1].clone());
449 paginator.next();
450
451 assert_eq!(paginator.fetch().await?, pages[2].clone());
452
453 let mut select = SelectStatement::new()
454 .exprs([
455 Expr::col((fruit::Entity, fruit::Column::Id)),
456 Expr::col((fruit::Entity, fruit::Column::Name)),
457 Expr::col((fruit::Entity, fruit::Column::CakeId)),
458 ])
459 .from(fruit::Entity)
460 .to_owned();
461
462 let query_builder = db.get_database_backend();
463 let stmts = [
464 query_builder.build(select.clone().offset(0).limit(2)),
465 query_builder.build(select.clone().offset(2).limit(2)),
466 query_builder.build(select.offset(4).limit(2)),
467 ];
468
469 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
470 Ok(())
471 }
472
473 #[smol_potat::test]
474 async fn fetch_raw() -> Result<(), DbErr> {
475 let (db, pages) = setup();
476
477 let mut paginator = fruit::Entity::find()
478 .from_raw_sql(RAW_STMT.clone())
479 .paginate(&db, 2);
480
481 assert_eq!(paginator.fetch().await?, pages[0].clone());
482 paginator.next();
483
484 assert_eq!(paginator.fetch().await?, pages[1].clone());
485 paginator.next();
486
487 assert_eq!(paginator.fetch().await?, pages[2].clone());
488
489 let mut select = SelectStatement::new()
490 .exprs([
491 Expr::col((fruit::Entity, fruit::Column::Id)),
492 Expr::col((fruit::Entity, fruit::Column::Name)),
493 Expr::col((fruit::Entity, fruit::Column::CakeId)),
494 ])
495 .from(fruit::Entity)
496 .to_owned();
497
498 let query_builder = db.get_database_backend();
499 let stmts = [
500 query_builder.build(select.clone().offset(0).limit(2)),
501 query_builder.build(select.clone().offset(2).limit(2)),
502 query_builder.build(select.offset(4).limit(2)),
503 ];
504
505 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
506 Ok(())
507 }
508
509 #[smol_potat::test]
510 async fn num_pages() -> Result<(), DbErr> {
511 let (db, num_items) = setup_num_items();
512
513 let num_items = num_items as u64;
514 let page_size = 2_u64;
515 let num_pages = (num_items / page_size) + (num_items % page_size > 0) as u64;
516 let paginator = fruit::Entity::find().paginate(&db, page_size);
517
518 assert_eq!(paginator.num_pages().await?, num_pages);
519
520 let sub_query = SelectStatement::new()
521 .exprs([
522 Expr::col((fruit::Entity, fruit::Column::Id)),
523 Expr::col((fruit::Entity, fruit::Column::Name)),
524 Expr::col((fruit::Entity, fruit::Column::CakeId)),
525 ])
526 .from(fruit::Entity)
527 .to_owned();
528
529 let select = SelectStatement::new()
530 .expr(Expr::cust("COUNT(*) AS num_items"))
531 .from_subquery(sub_query, "sub_query")
532 .to_owned();
533
534 let query_builder = db.get_database_backend();
535 let stmts = [query_builder.build(&select)];
536
537 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
538 Ok(())
539 }
540
541 #[smol_potat::test]
542 async fn num_pages_raw() -> Result<(), DbErr> {
543 let (db, num_items) = setup_num_items();
544
545 let num_items = num_items as u64;
546 let page_size = 2_u64;
547 let num_pages = (num_items / page_size) + (num_items % page_size > 0) as u64;
548 let paginator = fruit::Entity::find()
549 .from_raw_sql(RAW_STMT.clone())
550 .paginate(&db, page_size);
551
552 assert_eq!(paginator.num_pages().await?, num_pages);
553
554 let sub_query = SelectStatement::new()
555 .exprs([
556 Expr::col((fruit::Entity, fruit::Column::Id)),
557 Expr::col((fruit::Entity, fruit::Column::Name)),
558 Expr::col((fruit::Entity, fruit::Column::CakeId)),
559 ])
560 .from(fruit::Entity)
561 .to_owned();
562
563 let select = SelectStatement::new()
564 .expr(Expr::cust("COUNT(*) AS num_items"))
565 .from_subquery(sub_query, "sub_query")
566 .to_owned();
567
568 let query_builder = db.get_database_backend();
569 let stmts = [query_builder.build(&select)];
570
571 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
572 Ok(())
573 }
574
575 #[smol_potat::test]
576 async fn next_and_cur_page() -> Result<(), DbErr> {
577 let (db, _) = setup();
578
579 let mut paginator = fruit::Entity::find().paginate(&db, 2);
580
581 assert_eq!(paginator.cur_page(), 0);
582 paginator.next();
583
584 assert_eq!(paginator.cur_page(), 1);
585 paginator.next();
586
587 assert_eq!(paginator.cur_page(), 2);
588 Ok(())
589 }
590
591 #[smol_potat::test]
592 async fn next_and_cur_page_raw() -> Result<(), DbErr> {
593 let (db, _) = setup();
594
595 let mut paginator = fruit::Entity::find()
596 .from_raw_sql(RAW_STMT.clone())
597 .paginate(&db, 2);
598
599 assert_eq!(paginator.cur_page(), 0);
600 paginator.next();
601
602 assert_eq!(paginator.cur_page(), 1);
603 paginator.next();
604
605 assert_eq!(paginator.cur_page(), 2);
606 Ok(())
607 }
608
609 #[smol_potat::test]
610 async fn fetch_and_next() -> Result<(), DbErr> {
611 let (db, pages) = setup();
612
613 let mut paginator = fruit::Entity::find().paginate(&db, 2);
614
615 assert_eq!(paginator.cur_page(), 0);
616 assert_eq!(paginator.fetch_and_next().await?, Some(pages[0].clone()));
617
618 assert_eq!(paginator.cur_page(), 1);
619 assert_eq!(paginator.fetch_and_next().await?, Some(pages[1].clone()));
620
621 assert_eq!(paginator.cur_page(), 2);
622 assert_eq!(paginator.fetch_and_next().await?, None);
623
624 let mut select = SelectStatement::new()
625 .exprs([
626 Expr::col((fruit::Entity, fruit::Column::Id)),
627 Expr::col((fruit::Entity, fruit::Column::Name)),
628 Expr::col((fruit::Entity, fruit::Column::CakeId)),
629 ])
630 .from(fruit::Entity)
631 .to_owned();
632
633 let query_builder = db.get_database_backend();
634 let stmts = [
635 query_builder.build(select.clone().offset(0).limit(2)),
636 query_builder.build(select.clone().offset(2).limit(2)),
637 query_builder.build(select.offset(4).limit(2)),
638 ];
639
640 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
641 Ok(())
642 }
643
644 #[smol_potat::test]
645 async fn fetch_and_next_raw() -> Result<(), DbErr> {
646 let (db, pages) = setup();
647
648 let mut paginator = fruit::Entity::find()
649 .from_raw_sql(RAW_STMT.clone())
650 .paginate(&db, 2);
651
652 assert_eq!(paginator.cur_page(), 0);
653 assert_eq!(paginator.fetch_and_next().await?, Some(pages[0].clone()));
654
655 assert_eq!(paginator.cur_page(), 1);
656 assert_eq!(paginator.fetch_and_next().await?, Some(pages[1].clone()));
657
658 assert_eq!(paginator.cur_page(), 2);
659 assert_eq!(paginator.fetch_and_next().await?, None);
660
661 let mut select = SelectStatement::new()
662 .exprs([
663 Expr::col((fruit::Entity, fruit::Column::Id)),
664 Expr::col((fruit::Entity, fruit::Column::Name)),
665 Expr::col((fruit::Entity, fruit::Column::CakeId)),
666 ])
667 .from(fruit::Entity)
668 .to_owned();
669
670 let query_builder = db.get_database_backend();
671 let stmts = [
672 query_builder.build(select.clone().offset(0).limit(2)),
673 query_builder.build(select.clone().offset(2).limit(2)),
674 query_builder.build(select.offset(4).limit(2)),
675 ];
676
677 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
678 Ok(())
679 }
680
681 #[smol_potat::test]
682 async fn into_stream() -> Result<(), DbErr> {
683 let (db, pages) = setup();
684
685 let mut fruit_stream = fruit::Entity::find().paginate(&db, 2).into_stream();
686
687 assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
688 assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
689 assert_eq!(fruit_stream.try_next().await?, None);
690
691 drop(fruit_stream);
692
693 let mut select = SelectStatement::new()
694 .exprs([
695 Expr::col((fruit::Entity, fruit::Column::Id)),
696 Expr::col((fruit::Entity, fruit::Column::Name)),
697 Expr::col((fruit::Entity, fruit::Column::CakeId)),
698 ])
699 .from(fruit::Entity)
700 .to_owned();
701
702 let query_builder = db.get_database_backend();
703 let stmts = [
704 query_builder.build(select.clone().offset(0).limit(2)),
705 query_builder.build(select.clone().offset(2).limit(2)),
706 query_builder.build(select.offset(4).limit(2)),
707 ];
708
709 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
710 Ok(())
711 }
712
713 #[smol_potat::test]
714 async fn into_stream_raw() -> Result<(), DbErr> {
715 let (db, pages) = setup();
716
717 let mut fruit_stream = fruit::Entity::find()
718 .from_raw_sql(RAW_STMT.clone())
719 .paginate(&db, 2)
720 .into_stream();
721
722 assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
723 assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
724 assert_eq!(fruit_stream.try_next().await?, None);
725
726 drop(fruit_stream);
727
728 let mut select = SelectStatement::new()
729 .exprs([
730 Expr::col((fruit::Entity, fruit::Column::Id)),
731 Expr::col((fruit::Entity, fruit::Column::Name)),
732 Expr::col((fruit::Entity, fruit::Column::CakeId)),
733 ])
734 .from(fruit::Entity)
735 .to_owned();
736
737 let query_builder = db.get_database_backend();
738 let stmts = [
739 query_builder.build(select.clone().offset(0).limit(2)),
740 query_builder.build(select.clone().offset(2).limit(2)),
741 query_builder.build(select.offset(4).limit(2)),
742 ];
743
744 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
745 Ok(())
746 }
747
748 #[smol_potat::test]
749 async fn into_stream_raw_leading_spaces() -> Result<(), DbErr> {
750 let (db, pages) = setup();
751
752 let raw_stmt = Statement::from_sql_and_values(
753 DbBackend::Postgres,
754 r#" SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" "#,
755 [],
756 );
757
758 let mut fruit_stream = fruit::Entity::find()
759 .from_raw_sql(raw_stmt.clone())
760 .paginate(&db, 2)
761 .into_stream();
762
763 assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
764 assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
765 assert_eq!(fruit_stream.try_next().await?, None);
766
767 drop(fruit_stream);
768
769 let mut select = SelectStatement::new()
770 .exprs([
771 Expr::col((fruit::Entity, fruit::Column::Id)),
772 Expr::col((fruit::Entity, fruit::Column::Name)),
773 Expr::col((fruit::Entity, fruit::Column::CakeId)),
774 ])
775 .from(fruit::Entity)
776 .to_owned();
777
778 let query_builder = db.get_database_backend();
779 let stmts = [
780 query_builder.build(select.clone().offset(0).limit(2)),
781 query_builder.build(select.clone().offset(2).limit(2)),
782 query_builder.build(select.offset(4).limit(2)),
783 ];
784
785 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
786 Ok(())
787 }
788
789 #[smol_potat::test]
790 #[should_panic]
791 async fn error() {
792 let (db, _pages) = setup();
793
794 fruit::Entity::find().paginate(&db, 0);
795 }
796}