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