1use crate::{
2 ConnectionTrait, EntityTrait, FromQueryResult, Select, SelectModel, SelectTwo, SelectTwoModel,
3 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
10#[cfg(not(feature = "sync"))]
11type PinBoxStream<'db, Item> = Pin<Box<dyn Stream<Item = Item> + 'db>>;
12#[cfg(feature = "sync")]
13type PinBoxStream<'db, Item> = Box<dyn Iterator<Item = Item> + 'db>;
14
15#[derive(Clone, Debug)]
25pub struct Paginator<'db, C, S>
26where
27 C: ConnectionTrait,
28 S: SelectorTrait + 'db,
29{
30 pub(crate) query: SelectStatement,
31 pub(crate) page: u64,
32 pub(crate) page_size: u64,
33 pub(crate) db: &'db C,
34 pub(crate) selector: PhantomData<S>,
35}
36
37#[derive(Clone, Debug)]
39pub struct ItemsAndPagesNumber {
40 pub number_of_items: u64,
42 pub number_of_pages: u64,
44}
45
46impl<'db, C, S> Paginator<'db, C, S>
49where
50 C: ConnectionTrait,
51 S: SelectorTrait + 'db,
52{
53 pub async fn fetch_page(&self, page: u64) -> Result<Vec<S::Item>, DbErr> {
55 let query = self
56 .query
57 .clone()
58 .limit(self.page_size)
59 .offset(self.page_size * page)
60 .to_owned();
61 let rows = self.db.query_all(&query).await?;
62 let mut buffer = Vec::with_capacity(rows.len());
63 for row in rows.into_iter() {
64 buffer.push(S::from_raw_query_result(row)?);
65 }
66 Ok(buffer)
67 }
68
69 pub async fn fetch(&self) -> Result<Vec<S::Item>, DbErr> {
71 self.fetch_page(self.page).await
72 }
73
74 pub async fn num_items(&self) -> Result<u64, DbErr> {
76 let query = SelectStatement::new()
77 .expr(Expr::cust("COUNT(*) AS num_items"))
78 .from_subquery(
79 self.query
80 .clone()
81 .reset_limit()
82 .reset_offset()
83 .clear_order_by()
84 .to_owned(),
85 "sub_query",
86 )
87 .to_owned();
88 let result = match self.db.query_one(&query).await? {
89 Some(res) => res,
90 None => return Ok(0),
91 };
92 #[allow(clippy::match_single_binding)]
93 let num_items = match self.db.get_database_backend() {
94 _ => result.try_get::<i64>("", "num_items")? as u64,
95 };
96 Ok(num_items)
97 }
98
99 pub async fn num_pages(&self) -> Result<u64, DbErr> {
101 let num_items = self.num_items().await?;
102 let num_pages = self.compute_pages_number(num_items);
103 Ok(num_pages)
104 }
105
106 pub async fn num_items_and_pages(&self) -> Result<ItemsAndPagesNumber, DbErr> {
108 let number_of_items = self.num_items().await?;
109 let number_of_pages = self.compute_pages_number(number_of_items);
110
111 Ok(ItemsAndPagesNumber {
112 number_of_items,
113 number_of_pages,
114 })
115 }
116
117 #[allow(clippy::manual_is_multiple_of)]
119 fn compute_pages_number(&self, num_items: u64) -> u64 {
120 (num_items / self.page_size) + (num_items % self.page_size > 0) as u64
121 }
122
123 pub fn next(&mut self) {
125 self.page += 1;
126 }
127
128 pub fn cur_page(&self) -> u64 {
130 self.page
131 }
132
133 pub async fn fetch_and_next(&mut self) -> Result<Option<Vec<S::Item>>, DbErr> {
166 let vec = self.fetch().await?;
167 self.next();
168 let opt = if !vec.is_empty() { Some(vec) } else { None };
169 Ok(opt)
170 }
171
172 pub fn into_stream(self) -> PinBoxStream<'db, Result<Vec<S::Item>, DbErr>> {
240 #[cfg(not(feature = "sync"))]
241 {
242 let mut streamer = self;
243 Box::pin(stream! {
244 while let Some(vec) = streamer.fetch_and_next().await? {
245 yield Ok(vec);
246 }
247 })
248 }
249 #[cfg(feature = "sync")]
250 {
251 Box::new(PaginatorStream { paginator: self })
252 }
253 }
254}
255
256#[cfg(feature = "sync")]
257#[derive(Debug)]
258pub struct PaginatorStream<'db, C, S>
261where
262 C: ConnectionTrait,
263 S: SelectorTrait + 'db,
264{
265 paginator: Paginator<'db, C, S>,
266}
267
268#[async_trait::async_trait]
272pub trait PaginatorTrait<'db, C>
273where
274 C: ConnectionTrait,
275{
276 type Selector: SelectorTrait + Send + Sync + 'db;
278
279 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>;
281
282 async fn count(self, db: &'db C) -> Result<u64, DbErr>
284 where
285 Self: Send + Sized,
286 {
287 self.paginate(db, 1).num_items().await
288 }
289}
290
291impl<'db, C, S> PaginatorTrait<'db, C> for Selector<S>
292where
293 C: ConnectionTrait,
294 S: SelectorTrait + Send + Sync + 'db,
295{
296 type Selector = S;
297
298 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, S> {
299 assert!(page_size != 0, "page_size should not be zero");
300 Paginator {
301 query: self.query,
302 page: 0,
303 page_size,
304 db,
305 selector: PhantomData,
306 }
307 }
308}
309
310impl<'db, C, S> PaginatorTrait<'db, C> for SelectorRaw<S>
311where
312 C: ConnectionTrait,
313 S: SelectorTrait + Send + Sync + 'db,
314{
315 type Selector = S;
316 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, S> {
317 assert!(page_size != 0, "page_size should not be zero");
318 let sql = self.stmt.sql.trim()[6..].trim().to_owned();
319 let mut query = SelectStatement::new();
320 query.expr(if let Some(values) = self.stmt.values {
321 Expr::cust_with_values(sql, values.0)
322 } else {
323 Expr::cust(sql)
324 });
325
326 Paginator {
327 query,
328 page: 0,
329 page_size,
330 db,
331 selector: PhantomData,
332 }
333 }
334}
335
336impl<'db, C, M, E> PaginatorTrait<'db, C> for Select<E>
337where
338 C: ConnectionTrait,
339 E: EntityTrait<Model = M>,
340 M: FromQueryResult + Sized + Send + Sync + 'db,
341{
342 type Selector = SelectModel<M>;
343
344 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
345 self.into_model().paginate(db, page_size)
346 }
347}
348
349impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo<E, F>
350where
351 C: ConnectionTrait,
352 E: EntityTrait<Model = M>,
353 F: EntityTrait<Model = N>,
354 M: FromQueryResult + Sized + Send + Sync + 'db,
355 N: FromQueryResult + Sized + Send + Sync + 'db,
356{
357 type Selector = SelectTwoModel<M, N>;
358
359 fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
360 self.into_model().paginate(db, page_size)
361 }
362}
363
364#[cfg(feature = "sync")]
365impl<'db, C, S> Iterator for PaginatorStream<'db, C, S>
366where
367 C: ConnectionTrait,
368 S: SelectorTrait + 'db,
369{
370 type Item = Result<Vec<S::Item>, DbErr>;
371
372 fn next(&mut self) -> Option<Self::Item> {
373 match self.paginator.fetch_and_next() {
374 Ok(Some(vec)) => Some(Ok(vec)),
375 Ok(None) => None,
376 Err(e) => Some(Err(e)),
377 }
378 }
379}
380
381#[cfg(test)]
382#[cfg(feature = "mock")]
383mod tests {
384 use super::*;
385 use crate::entity::prelude::*;
386 #[cfg(feature = "sync")]
387 use crate::util::StreamShim;
388 use crate::{DatabaseConnection, DbBackend, MockDatabase, Transaction};
389 use crate::{QueryOrder, QuerySelect};
390 use crate::{Statement, tests_cfg::*};
391 use futures_util::{TryStreamExt, stream::TryNext};
392 use pretty_assertions::assert_eq;
393 use sea_query::{Expr, SelectStatement, Value};
394 use std::sync::LazyLock;
395
396 static RAW_STMT: LazyLock<Statement> = LazyLock::new(|| {
397 Statement::from_sql_and_values(
398 DbBackend::Postgres,
399 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
400 [],
401 )
402 });
403
404 fn setup() -> (DatabaseConnection, Vec<Vec<fruit::Model>>) {
405 let page1 = vec![
406 fruit::Model {
407 id: 1,
408 name: "Blueberry".into(),
409 cake_id: Some(1),
410 },
411 fruit::Model {
412 id: 2,
413 name: "Raspberry".into(),
414 cake_id: Some(1),
415 },
416 ];
417
418 let page2 = vec![fruit::Model {
419 id: 3,
420 name: "Strawberry".into(),
421 cake_id: Some(2),
422 }];
423
424 let page3 = Vec::<fruit::Model>::new();
425
426 let db = MockDatabase::new(DbBackend::Postgres)
427 .append_query_results([page1.clone(), page2.clone(), page3.clone()])
428 .into_connection();
429
430 (db, vec![page1, page2, page3])
431 }
432
433 fn setup_num_items() -> (DatabaseConnection, i64) {
434 let num_items = 3;
435 let db = MockDatabase::new(DbBackend::Postgres)
436 .append_query_results([[maplit::btreemap! {
437 "num_items" => Into::<Value>::into(num_items),
438 }]])
439 .into_connection();
440
441 (db, num_items)
442 }
443
444 #[smol_potat::test]
445 async fn fetch_page() -> Result<(), DbErr> {
446 let (db, pages) = setup();
447
448 let paginator = fruit::Entity::find().paginate(&db, 2);
449
450 assert_eq!(paginator.fetch_page(0).await?, pages[0].clone());
451 assert_eq!(paginator.fetch_page(1).await?, pages[1].clone());
452 assert_eq!(paginator.fetch_page(2).await?, pages[2].clone());
453
454 let mut select = SelectStatement::new()
455 .exprs([
456 Expr::col((fruit::Entity, fruit::Column::Id)),
457 Expr::col((fruit::Entity, fruit::Column::Name)),
458 Expr::col((fruit::Entity, fruit::Column::CakeId)),
459 ])
460 .from(fruit::Entity)
461 .to_owned();
462
463 let query_builder = db.get_database_backend();
464 let stmts = [
465 query_builder.build(select.clone().offset(0).limit(2)),
466 query_builder.build(select.clone().offset(2).limit(2)),
467 query_builder.build(select.offset(4).limit(2)),
468 ];
469
470 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
471 Ok(())
472 }
473
474 #[smol_potat::test]
475 async fn fetch_page_raw() -> Result<(), DbErr> {
476 let (db, pages) = setup();
477
478 let paginator = fruit::Entity::find()
479 .from_raw_sql(RAW_STMT.clone())
480 .paginate(&db, 2);
481
482 assert_eq!(paginator.fetch_page(0).await?, pages[0].clone());
483 assert_eq!(paginator.fetch_page(1).await?, pages[1].clone());
484 assert_eq!(paginator.fetch_page(2).await?, pages[2].clone());
485
486 let mut select = SelectStatement::new()
487 .exprs([
488 Expr::col((fruit::Entity, fruit::Column::Id)),
489 Expr::col((fruit::Entity, fruit::Column::Name)),
490 Expr::col((fruit::Entity, fruit::Column::CakeId)),
491 ])
492 .from(fruit::Entity)
493 .to_owned();
494
495 let query_builder = db.get_database_backend();
496 let stmts = [
497 query_builder.build(select.clone().offset(0).limit(2)),
498 query_builder.build(select.clone().offset(2).limit(2)),
499 query_builder.build(select.offset(4).limit(2)),
500 ];
501
502 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
503 Ok(())
504 }
505
506 #[smol_potat::test]
507 async fn fetch() -> Result<(), DbErr> {
508 let (db, pages) = setup();
509
510 let mut paginator = fruit::Entity::find().paginate(&db, 2);
511
512 assert_eq!(paginator.fetch().await?, pages[0].clone());
513 paginator.next();
514
515 assert_eq!(paginator.fetch().await?, pages[1].clone());
516 paginator.next();
517
518 assert_eq!(paginator.fetch().await?, pages[2].clone());
519
520 let mut select = 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 query_builder = db.get_database_backend();
530 let stmts = [
531 query_builder.build(select.clone().offset(0).limit(2)),
532 query_builder.build(select.clone().offset(2).limit(2)),
533 query_builder.build(select.offset(4).limit(2)),
534 ];
535
536 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
537 Ok(())
538 }
539
540 #[smol_potat::test]
541 async fn fetch_raw() -> Result<(), DbErr> {
542 let (db, pages) = setup();
543
544 let mut paginator = fruit::Entity::find()
545 .from_raw_sql(RAW_STMT.clone())
546 .paginate(&db, 2);
547
548 assert_eq!(paginator.fetch().await?, pages[0].clone());
549 paginator.next();
550
551 assert_eq!(paginator.fetch().await?, pages[1].clone());
552 paginator.next();
553
554 assert_eq!(paginator.fetch().await?, pages[2].clone());
555
556 let mut select = SelectStatement::new()
557 .exprs([
558 Expr::col((fruit::Entity, fruit::Column::Id)),
559 Expr::col((fruit::Entity, fruit::Column::Name)),
560 Expr::col((fruit::Entity, fruit::Column::CakeId)),
561 ])
562 .from(fruit::Entity)
563 .to_owned();
564
565 let query_builder = db.get_database_backend();
566 let stmts = [
567 query_builder.build(select.clone().offset(0).limit(2)),
568 query_builder.build(select.clone().offset(2).limit(2)),
569 query_builder.build(select.offset(4).limit(2)),
570 ];
571
572 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
573 Ok(())
574 }
575
576 #[smol_potat::test]
577 async fn num_pages() -> Result<(), DbErr> {
578 let (db, num_items) = setup_num_items();
579
580 let num_items = num_items as u64;
581 let page_size = 2_u64;
582 let num_pages = (num_items / page_size) + (num_items % page_size > 0) as u64;
583 let paginator = fruit::Entity::find().paginate(&db, page_size);
584
585 assert_eq!(paginator.num_pages().await?, num_pages);
586
587 let sub_query = SelectStatement::new()
588 .exprs([
589 Expr::col((fruit::Entity, fruit::Column::Id)),
590 Expr::col((fruit::Entity, fruit::Column::Name)),
591 Expr::col((fruit::Entity, fruit::Column::CakeId)),
592 ])
593 .from(fruit::Entity)
594 .to_owned();
595
596 let select = SelectStatement::new()
597 .expr(Expr::cust("COUNT(*) AS num_items"))
598 .from_subquery(sub_query, "sub_query")
599 .to_owned();
600
601 let query_builder = db.get_database_backend();
602 let stmts = [query_builder.build(&select)];
603
604 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
605 Ok(())
606 }
607
608 #[smol_potat::test]
609 async fn num_pages_raw() -> Result<(), DbErr> {
610 let (db, num_items) = setup_num_items();
611
612 let num_items = num_items as u64;
613 let page_size = 2_u64;
614 let num_pages = (num_items / page_size) + (num_items % page_size > 0) as u64;
615 let paginator = fruit::Entity::find()
616 .from_raw_sql(RAW_STMT.clone())
617 .paginate(&db, page_size);
618
619 assert_eq!(paginator.num_pages().await?, num_pages);
620
621 let sub_query = SelectStatement::new()
622 .exprs([
623 Expr::col((fruit::Entity, fruit::Column::Id)),
624 Expr::col((fruit::Entity, fruit::Column::Name)),
625 Expr::col((fruit::Entity, fruit::Column::CakeId)),
626 ])
627 .from(fruit::Entity)
628 .to_owned();
629
630 let select = SelectStatement::new()
631 .expr(Expr::cust("COUNT(*) AS num_items"))
632 .from_subquery(sub_query, "sub_query")
633 .to_owned();
634
635 let query_builder = db.get_database_backend();
636 let stmts = [query_builder.build(&select)];
637
638 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
639 Ok(())
640 }
641
642 #[smol_potat::test]
643 async fn next_and_cur_page() -> Result<(), DbErr> {
644 let (db, _) = setup();
645
646 let mut paginator = fruit::Entity::find().paginate(&db, 2);
647
648 assert_eq!(paginator.cur_page(), 0);
649 paginator.next();
650
651 assert_eq!(paginator.cur_page(), 1);
652 paginator.next();
653
654 assert_eq!(paginator.cur_page(), 2);
655 Ok(())
656 }
657
658 #[smol_potat::test]
659 async fn next_and_cur_page_raw() -> Result<(), DbErr> {
660 let (db, _) = setup();
661
662 let mut paginator = fruit::Entity::find()
663 .from_raw_sql(RAW_STMT.clone())
664 .paginate(&db, 2);
665
666 assert_eq!(paginator.cur_page(), 0);
667 paginator.next();
668
669 assert_eq!(paginator.cur_page(), 1);
670 paginator.next();
671
672 assert_eq!(paginator.cur_page(), 2);
673 Ok(())
674 }
675
676 #[smol_potat::test]
677 async fn fetch_and_next() -> Result<(), DbErr> {
678 let (db, pages) = setup();
679
680 let mut paginator = fruit::Entity::find().paginate(&db, 2);
681
682 assert_eq!(paginator.cur_page(), 0);
683 assert_eq!(paginator.fetch_and_next().await?, Some(pages[0].clone()));
684
685 assert_eq!(paginator.cur_page(), 1);
686 assert_eq!(paginator.fetch_and_next().await?, Some(pages[1].clone()));
687
688 assert_eq!(paginator.cur_page(), 2);
689 assert_eq!(paginator.fetch_and_next().await?, None);
690
691 let mut select = SelectStatement::new()
692 .exprs([
693 Expr::col((fruit::Entity, fruit::Column::Id)),
694 Expr::col((fruit::Entity, fruit::Column::Name)),
695 Expr::col((fruit::Entity, fruit::Column::CakeId)),
696 ])
697 .from(fruit::Entity)
698 .to_owned();
699
700 let query_builder = db.get_database_backend();
701 let stmts = [
702 query_builder.build(select.clone().offset(0).limit(2)),
703 query_builder.build(select.clone().offset(2).limit(2)),
704 query_builder.build(select.offset(4).limit(2)),
705 ];
706
707 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
708 Ok(())
709 }
710
711 #[smol_potat::test]
712 async fn fetch_and_next_raw() -> Result<(), DbErr> {
713 let (db, pages) = setup();
714
715 let mut paginator = fruit::Entity::find()
716 .from_raw_sql(RAW_STMT.clone())
717 .paginate(&db, 2);
718
719 assert_eq!(paginator.cur_page(), 0);
720 assert_eq!(paginator.fetch_and_next().await?, Some(pages[0].clone()));
721
722 assert_eq!(paginator.cur_page(), 1);
723 assert_eq!(paginator.fetch_and_next().await?, Some(pages[1].clone()));
724
725 assert_eq!(paginator.cur_page(), 2);
726 assert_eq!(paginator.fetch_and_next().await?, None);
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() -> Result<(), DbErr> {
750 let (db, pages) = setup();
751
752 let mut fruit_stream = fruit::Entity::find().paginate(&db, 2).into_stream();
753
754 assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
755 assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
756 assert_eq!(fruit_stream.try_next().await?, None);
757
758 drop(fruit_stream);
759
760 let mut select = SelectStatement::new()
761 .exprs([
762 Expr::col((fruit::Entity, fruit::Column::Id)),
763 Expr::col((fruit::Entity, fruit::Column::Name)),
764 Expr::col((fruit::Entity, fruit::Column::CakeId)),
765 ])
766 .from(fruit::Entity)
767 .to_owned();
768
769 let query_builder = db.get_database_backend();
770 let stmts = [
771 query_builder.build(select.clone().offset(0).limit(2)),
772 query_builder.build(select.clone().offset(2).limit(2)),
773 query_builder.build(select.offset(4).limit(2)),
774 ];
775
776 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
777 Ok(())
778 }
779
780 #[smol_potat::test]
781 async fn into_stream_raw() -> Result<(), DbErr> {
782 let (db, pages) = setup();
783
784 let mut fruit_stream = fruit::Entity::find()
785 .from_raw_sql(RAW_STMT.clone())
786 .paginate(&db, 2)
787 .into_stream();
788
789 assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
790 assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
791 assert_eq!(fruit_stream.try_next().await?, None);
792
793 drop(fruit_stream);
794
795 let mut select = SelectStatement::new()
796 .exprs([
797 Expr::col((fruit::Entity, fruit::Column::Id)),
798 Expr::col((fruit::Entity, fruit::Column::Name)),
799 Expr::col((fruit::Entity, fruit::Column::CakeId)),
800 ])
801 .from(fruit::Entity)
802 .to_owned();
803
804 let query_builder = db.get_database_backend();
805 let stmts = [
806 query_builder.build(select.clone().offset(0).limit(2)),
807 query_builder.build(select.clone().offset(2).limit(2)),
808 query_builder.build(select.offset(4).limit(2)),
809 ];
810
811 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
812 Ok(())
813 }
814
815 #[smol_potat::test]
816 async fn into_stream_raw_leading_spaces() -> Result<(), DbErr> {
817 let (db, pages) = setup();
818
819 let raw_stmt = Statement::from_sql_and_values(
820 DbBackend::Postgres,
821 r#" SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit" "#,
822 [],
823 );
824
825 let mut fruit_stream = fruit::Entity::find()
826 .from_raw_sql(raw_stmt.clone())
827 .paginate(&db, 2)
828 .into_stream();
829
830 assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
831 assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
832 assert_eq!(fruit_stream.try_next().await?, None);
833
834 drop(fruit_stream);
835
836 let mut select = SelectStatement::new()
837 .exprs([
838 Expr::col((fruit::Entity, fruit::Column::Id)),
839 Expr::col((fruit::Entity, fruit::Column::Name)),
840 Expr::col((fruit::Entity, fruit::Column::CakeId)),
841 ])
842 .from(fruit::Entity)
843 .to_owned();
844
845 let query_builder = db.get_database_backend();
846 let stmts = [
847 query_builder.build(select.clone().offset(0).limit(2)),
848 query_builder.build(select.clone().offset(2).limit(2)),
849 query_builder.build(select.offset(4).limit(2)),
850 ];
851
852 assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
853 Ok(())
854 }
855
856 #[smol_potat::test]
857 #[should_panic]
858 async fn error() {
859 let (db, _pages) = setup();
860
861 fruit::Entity::find().paginate(&db, 0);
862 }
863}