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