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