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        Ok(result.try_get::<i64>("", "num_items")? as u64)
91    }
92
93    /// Get the total number of pages
94    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    /// Get the total number of items and pages
101    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    /// Number of pages needed to cover `num_items` at the configured page size.
112    #[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    /// Increment the page counter
118    pub fn next(&mut self) {
119        self.page += 1;
120    }
121
122    /// Get current page number
123    pub fn cur_page(&self) -> u64 {
124        self.page
125    }
126
127    /// Set the page counter
128    pub fn set_page(&mut self, page: u64) {
129        self.page = page;
130    }
131
132    /// Fetch one page and increment the page counter
133    ///
134    /// ```
135    /// # use sea_orm::{error::*, tests_cfg::*, *};
136    /// #
137    /// # #[cfg(feature = "mock")]
138    /// # pub fn main() -> Result<(), DbErr> {
139    /// #
140    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
141    /// #     .append_query_results([
142    /// #         vec![cake::Model {
143    /// #             id: 1,
144    /// #             name: "Cake".to_owned(),
145    /// #         }],
146    /// #         vec![],
147    /// #     ])
148    /// #     .into_connection();
149    /// # let db = &owned_db;
150    /// #
151    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
152    /// let mut cake_pages = cake::Entity::find()
153    ///     .order_by_asc(cake::Column::Id)
154    ///     .paginate(db, 50);
155    ///
156    /// while let Some(cakes) = cake_pages.fetch_and_next()? {
157    ///     // Do something on cakes: Vec<cake::Model>
158    /// }
159    /// #
160    /// # Ok(())
161    /// # }
162    /// ```
163    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    /// Convert self into an stream
171    ///
172    /// ```
173    /// # use sea_orm::{error::*, tests_cfg::*, *};
174    /// #
175    /// # #[cfg(all(feature = "mock", not(feature = "sync")))]
176    /// # pub fn main() -> Result<(), DbErr> {
177    /// #
178    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
179    /// #     .append_query_results([
180    /// #         vec![cake::Model {
181    /// #             id: 1,
182    /// #             name: "Cake".to_owned(),
183    /// #         }],
184    /// #         vec![],
185    /// #     ])
186    /// #     .into_connection();
187    /// # let db = &owned_db;
188    /// #
189    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
190    /// let mut cake_stream = cake::Entity::find()
191    ///     .order_by_asc(cake::Column::Id)
192    ///     .paginate(db, 50)
193    ///     .into_stream();
194    ///
195    /// while let Some(cakes) = cake_stream.try_next()? {
196    ///     // Do something on cakes: Vec<cake::Model>
197    /// }
198    /// #
199    /// # Ok(())
200    /// # }
201    /// # #[cfg(all(feature = "mock", feature = "sync"))]
202    /// # fn main() {}
203    /// ```
204    /// (for SeaORM Sync)
205    /// ```
206    /// # use sea_orm::{error::*, tests_cfg::*, *};
207    /// # #[cfg(all(feature = "mock", feature = "sync"))]
208    /// # fn example() -> Result<(), DbErr> {
209    /// #
210    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
211    /// #     .append_query_results([
212    /// #         vec![cake::Model {
213    /// #             id: 1,
214    /// #             name: "Cake".to_owned(),
215    /// #         }],
216    /// #         vec![],
217    /// #     ])
218    /// #     .into_connection();
219    /// # let db = &owned_db;
220    /// #
221    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
222    /// let mut cake_stream = cake::Entity::find()
223    ///     .order_by_asc(cake::Column::Id)
224    ///     .paginate(db, 50)
225    ///     .into_stream();
226    ///
227    /// while let Some(cakes) = cake_stream.next() {
228    ///     // Do something on cakes: Vec<cake::Model>
229    /// }
230    /// #
231    /// # Ok(())
232    /// # }
233    /// ```
234    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)]
253/// Synchronous `Iterator` wrapper around a [`Paginator`], yielding one page
254/// of items per call.
255pub struct PaginatorStream<'db, C, S>
256where
257    C: ConnectionTrait,
258    S: SelectorTrait + 'db,
259{
260    paginator: Paginator<'db, C, S>,
261}
262
263/// Extension trait that adds `.paginate(db, page_size)` to anything
264/// page-able — [`Select`](crate::Select), [`SelectTwo`](crate::SelectTwo),
265/// [`Selector`], [`SelectorRaw`].
266pub trait PaginatorTrait<'db, C>
267where
268    C: ConnectionTrait,
269{
270    /// The [`SelectorTrait`] that materialises each row of the page.
271    type Selector: SelectorTrait + 'db;
272
273    /// Paginate the result of a select operation.
274    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>;
275
276    /// Count the number of rows this query selects, honoring any `limit` and
277    /// `offset` set on it.
278    ///
279    /// This differs from [`Paginator::num_items`], which resets `limit`/`offset`
280    /// to report the grand total for pagination. `count` wraps the query as-is in
281    /// a `SELECT COUNT(*) FROM (..)` subquery, so a query with a `limit` counts at
282    /// most that many rows.
283    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}