Skip to main content

sea_orm/executor/
paginator.rs

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/// Fetches a [`Select`](crate::Select)'s results one page at a time with
14/// `LIMIT` / `OFFSET`. Build one with
15/// [`PaginatorTrait::paginate`](crate::PaginatorTrait::paginate); use
16/// [`fetch_page`](Self::fetch_page) / [`fetch`](Self::fetch) to read rows
17/// and [`num_pages`](Self::num_pages) /
18/// [`num_items_and_pages`](Self::num_items_and_pages) for totals.
19///
20/// Add an `ORDER BY` to your query — without one, page contents are not
21/// deterministic across calls.
22#[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/// Pair of totals returned by [`Paginator::num_items_and_pages`].
36#[derive(Clone, Debug)]
37pub struct ItemsAndPagesNumber {
38    /// Total number of rows matched by the query.
39    pub number_of_items: u64,
40    /// Total number of pages at the configured page size.
41    pub number_of_pages: u64,
42}
43
44// LINT: warn if paginator is used without an order by clause
45
46impl<'db, C, S> Paginator<'db, C, S>
47where
48    C: ConnectionTrait,
49    S: SelectorTrait + 'db,
50{
51    /// Fetch a specific page; page index starts from zero
52    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    /// Fetch the current page
68    pub fn fetch(&self) -> Result<Vec<S::Item>, DbErr> {
69        self.fetch_page(self.page)
70    }
71
72    /// Get the total number of items
73    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    /// Get the total number of pages
98    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    /// Get the total number of items and pages
105    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    /// Number of pages needed to cover `num_items` at the configured page size.
116    #[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    /// Increment the page counter
122    pub fn next(&mut self) {
123        self.page += 1;
124    }
125
126    /// Get current page number
127    pub fn cur_page(&self) -> u64 {
128        self.page
129    }
130
131    /// Fetch one page and increment the page counter
132    ///
133    /// ```
134    /// # use sea_orm::{error::*, tests_cfg::*, *};
135    /// #
136    /// # #[cfg(feature = "mock")]
137    /// # pub fn main() -> Result<(), DbErr> {
138    /// #
139    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
140    /// #     .append_query_results([
141    /// #         vec![cake::Model {
142    /// #             id: 1,
143    /// #             name: "Cake".to_owned(),
144    /// #         }],
145    /// #         vec![],
146    /// #     ])
147    /// #     .into_connection();
148    /// # let db = &owned_db;
149    /// #
150    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
151    /// let mut cake_pages = cake::Entity::find()
152    ///     .order_by_asc(cake::Column::Id)
153    ///     .paginate(db, 50);
154    ///
155    /// while let Some(cakes) = cake_pages.fetch_and_next()? {
156    ///     // Do something on cakes: Vec<cake::Model>
157    /// }
158    /// #
159    /// # Ok(())
160    /// # }
161    /// ```
162    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    /// Convert self into an stream
170    ///
171    /// ```
172    /// # use sea_orm::{error::*, tests_cfg::*, *};
173    /// #
174    /// # #[cfg(all(feature = "mock", not(feature = "sync")))]
175    /// # pub fn main() -> Result<(), DbErr> {
176    /// #
177    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
178    /// #     .append_query_results([
179    /// #         vec![cake::Model {
180    /// #             id: 1,
181    /// #             name: "Cake".to_owned(),
182    /// #         }],
183    /// #         vec![],
184    /// #     ])
185    /// #     .into_connection();
186    /// # let db = &owned_db;
187    /// #
188    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
189    /// let mut cake_stream = cake::Entity::find()
190    ///     .order_by_asc(cake::Column::Id)
191    ///     .paginate(db, 50)
192    ///     .into_stream();
193    ///
194    /// while let Some(cakes) = cake_stream.try_next()? {
195    ///     // Do something on cakes: Vec<cake::Model>
196    /// }
197    /// #
198    /// # Ok(())
199    /// # }
200    /// # #[cfg(all(feature = "mock", feature = "sync"))]
201    /// # fn main() {}
202    /// ```
203    /// (for SeaORM Sync)
204    /// ```
205    /// # use sea_orm::{error::*, tests_cfg::*, *};
206    /// # #[cfg(all(feature = "mock", feature = "sync"))]
207    /// # fn example() -> Result<(), DbErr> {
208    /// #
209    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
210    /// #     .append_query_results([
211    /// #         vec![cake::Model {
212    /// #             id: 1,
213    /// #             name: "Cake".to_owned(),
214    /// #         }],
215    /// #         vec![],
216    /// #     ])
217    /// #     .into_connection();
218    /// # let db = &owned_db;
219    /// #
220    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
221    /// let mut cake_stream = cake::Entity::find()
222    ///     .order_by_asc(cake::Column::Id)
223    ///     .paginate(db, 50)
224    ///     .into_stream();
225    ///
226    /// while let Some(cakes) = cake_stream.next() {
227    ///     // Do something on cakes: Vec<cake::Model>
228    /// }
229    /// #
230    /// # Ok(())
231    /// # }
232    /// ```
233    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)]
252/// Synchronous `Iterator` wrapper around a [`Paginator`], yielding one page
253/// of items per call.
254pub struct PaginatorStream<'db, C, S>
255where
256    C: ConnectionTrait,
257    S: SelectorTrait + 'db,
258{
259    paginator: Paginator<'db, C, S>,
260}
261
262/// Extension trait that adds `.paginate(db, page_size)` to anything
263/// page-able — [`Select`](crate::Select), [`SelectTwo`](crate::SelectTwo),
264/// [`Selector`], [`SelectorRaw`].
265pub trait PaginatorTrait<'db, C>
266where
267    C: ConnectionTrait,
268{
269    /// The [`SelectorTrait`] that materialises each row of the page.
270    type Selector: SelectorTrait + 'db;
271
272    /// Paginate the result of a select operation.
273    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>;
274
275    /// Perform a count on the paginated results
276    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}