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