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 async_stream::stream;
6use futures_util::Stream;
7use sea_query::{Expr, SelectStatement};
8use std::{marker::PhantomData, pin::Pin};
9
10#[cfg(not(feature = "sync"))]
11type PinBoxStream<'db, Item> = Pin<Box<dyn Stream<Item = Item> + 'db>>;
12#[cfg(feature = "sync")]
13type PinBoxStream<'db, Item> = Box<dyn Iterator<Item = Item> + 'db>;
14
15/// Fetches a [`Select`](crate::Select)'s results one page at a time with
16/// `LIMIT` / `OFFSET`. Build one with
17/// [`PaginatorTrait::paginate`](crate::PaginatorTrait::paginate); use
18/// [`fetch_page`](Self::fetch_page) / [`fetch`](Self::fetch) to read rows
19/// and [`num_pages`](Self::num_pages) /
20/// [`num_items_and_pages`](Self::num_items_and_pages) for totals.
21///
22/// Add an `ORDER BY` to your query — without one, page contents are not
23/// deterministic across calls.
24#[derive(Clone, Debug)]
25pub struct Paginator<'db, C, S>
26where
27    C: ConnectionTrait,
28    S: SelectorTrait + 'db,
29{
30    pub(crate) query: SelectStatement,
31    pub(crate) page: u64,
32    pub(crate) page_size: u64,
33    pub(crate) db: &'db C,
34    pub(crate) selector: PhantomData<S>,
35}
36
37/// Pair of totals returned by [`Paginator::num_items_and_pages`].
38#[derive(Clone, Debug)]
39pub struct ItemsAndPagesNumber {
40    /// Total number of rows matched by the query.
41    pub number_of_items: u64,
42    /// Total number of pages at the configured page size.
43    pub number_of_pages: u64,
44}
45
46// LINT: warn if paginator is used without an order by clause
47
48impl<'db, C, S> Paginator<'db, C, S>
49where
50    C: ConnectionTrait,
51    S: SelectorTrait + 'db,
52{
53    /// Fetch a specific page; page index starts from zero
54    pub async fn fetch_page(&self, page: u64) -> Result<Vec<S::Item>, DbErr> {
55        let query = self
56            .query
57            .clone()
58            .limit(self.page_size)
59            .offset(self.page_size * page)
60            .to_owned();
61        let rows = self.db.query_all(&query).await?;
62        let mut buffer = Vec::with_capacity(rows.len());
63        for row in rows.into_iter() {
64            buffer.push(S::from_raw_query_result(row)?);
65        }
66        Ok(buffer)
67    }
68
69    /// Fetch the current page
70    pub async fn fetch(&self) -> Result<Vec<S::Item>, DbErr> {
71        self.fetch_page(self.page).await
72    }
73
74    /// Get the total number of items
75    pub async fn num_items(&self) -> Result<u64, DbErr> {
76        let query = SelectStatement::new()
77            .expr(Expr::cust("COUNT(*) AS num_items"))
78            .from_subquery(
79                self.query
80                    .clone()
81                    .reset_limit()
82                    .reset_offset()
83                    .clear_order_by()
84                    .to_owned(),
85                "sub_query",
86            )
87            .to_owned();
88        let result = match self.db.query_one(&query).await? {
89            Some(res) => res,
90            None => return Ok(0),
91        };
92        Ok(result.try_get::<i64>("", "num_items")? as u64)
93    }
94
95    /// Get the total number of pages
96    pub async fn num_pages(&self) -> Result<u64, DbErr> {
97        let num_items = self.num_items().await?;
98        let num_pages = self.compute_pages_number(num_items);
99        Ok(num_pages)
100    }
101
102    /// Get the total number of items and pages
103    pub async fn num_items_and_pages(&self) -> Result<ItemsAndPagesNumber, DbErr> {
104        let number_of_items = self.num_items().await?;
105        let number_of_pages = self.compute_pages_number(number_of_items);
106
107        Ok(ItemsAndPagesNumber {
108            number_of_items,
109            number_of_pages,
110        })
111    }
112
113    /// Number of pages needed to cover `num_items` at the configured page size.
114    #[allow(clippy::manual_is_multiple_of)]
115    fn compute_pages_number(&self, num_items: u64) -> u64 {
116        (num_items / self.page_size) + (num_items % self.page_size > 0) as u64
117    }
118
119    /// Increment the page counter
120    pub fn next(&mut self) {
121        self.page += 1;
122    }
123
124    /// Get current page number
125    pub fn cur_page(&self) -> u64 {
126        self.page
127    }
128
129    /// Set the page counter
130    pub fn set_page(&mut self, page: u64) {
131        self.page = page;
132    }
133
134    /// Fetch one page and increment the page counter
135    ///
136    /// ```
137    /// # use sea_orm::{error::*, tests_cfg::*, *};
138    /// #
139    /// # #[smol_potat::main]
140    /// # #[cfg(feature = "mock")]
141    /// # pub async fn main() -> Result<(), DbErr> {
142    /// #
143    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
144    /// #     .append_query_results([
145    /// #         vec![cake::Model {
146    /// #             id: 1,
147    /// #             name: "Cake".to_owned(),
148    /// #         }],
149    /// #         vec![],
150    /// #     ])
151    /// #     .into_connection();
152    /// # let db = &owned_db;
153    /// #
154    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
155    /// let mut cake_pages = cake::Entity::find()
156    ///     .order_by_asc(cake::Column::Id)
157    ///     .paginate(db, 50);
158    ///
159    /// while let Some(cakes) = cake_pages.fetch_and_next().await? {
160    ///     // Do something on cakes: Vec<cake::Model>
161    /// }
162    /// #
163    /// # Ok(())
164    /// # }
165    /// ```
166    pub async fn fetch_and_next(&mut self) -> Result<Option<Vec<S::Item>>, DbErr> {
167        let vec = self.fetch().await?;
168        self.next();
169        let opt = if !vec.is_empty() { Some(vec) } else { None };
170        Ok(opt)
171    }
172
173    /// Convert self into an async stream
174    ///
175    /// ```
176    /// # use sea_orm::{error::*, tests_cfg::*, *};
177    /// #
178    /// # #[smol_potat::main]
179    /// # #[cfg(all(feature = "mock", not(feature = "sync")))]
180    /// # pub async fn main() -> Result<(), DbErr> {
181    /// #
182    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
183    /// #     .append_query_results([
184    /// #         vec![cake::Model {
185    /// #             id: 1,
186    /// #             name: "Cake".to_owned(),
187    /// #         }],
188    /// #         vec![],
189    /// #     ])
190    /// #     .into_connection();
191    /// # let db = &owned_db;
192    /// #
193    /// use futures_util::TryStreamExt;
194    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
195    /// let mut cake_stream = cake::Entity::find()
196    ///     .order_by_asc(cake::Column::Id)
197    ///     .paginate(db, 50)
198    ///     .into_stream();
199    ///
200    /// while let Some(cakes) = cake_stream.try_next().await? {
201    ///     // Do something on cakes: Vec<cake::Model>
202    /// }
203    /// #
204    /// # Ok(())
205    /// # }
206    /// # #[cfg(all(feature = "mock", feature = "sync"))]
207    /// # fn main() {}
208    /// ```
209    /// (for SeaORM Sync)
210    /// ```
211    /// # use sea_orm::{error::*, tests_cfg::*, *};
212    /// # #[cfg(all(feature = "mock", feature = "sync"))]
213    /// # fn example() -> Result<(), DbErr> {
214    /// #
215    /// # let owned_db = MockDatabase::new(DbBackend::Postgres)
216    /// #     .append_query_results([
217    /// #         vec![cake::Model {
218    /// #             id: 1,
219    /// #             name: "Cake".to_owned(),
220    /// #         }],
221    /// #         vec![],
222    /// #     ])
223    /// #     .into_connection();
224    /// # let db = &owned_db;
225    /// #
226    /// use futures_util::TryStreamExt;
227    /// use sea_orm::{entity::*, query::*, tests_cfg::cake};
228    /// let mut cake_stream = cake::Entity::find()
229    ///     .order_by_asc(cake::Column::Id)
230    ///     .paginate(db, 50)
231    ///     .into_stream();
232    ///
233    /// while let Some(cakes) = cake_stream.next() {
234    ///     // Do something on cakes: Vec<cake::Model>
235    /// }
236    /// #
237    /// # Ok(())
238    /// # }
239    /// ```
240    pub fn into_stream(self) -> PinBoxStream<'db, Result<Vec<S::Item>, DbErr>> {
241        #[cfg(not(feature = "sync"))]
242        {
243            let mut streamer = self;
244            Box::pin(stream! {
245                while let Some(vec) = streamer.fetch_and_next().await? {
246                    yield Ok(vec);
247                }
248            })
249        }
250        #[cfg(feature = "sync")]
251        {
252            Box::new(PaginatorStream { paginator: self })
253        }
254    }
255}
256
257#[cfg(feature = "sync")]
258#[derive(Debug)]
259/// Synchronous `Iterator` wrapper around a [`Paginator`], yielding one page
260/// of items per call.
261pub struct PaginatorStream<'db, C, S>
262where
263    C: ConnectionTrait,
264    S: SelectorTrait + 'db,
265{
266    paginator: Paginator<'db, C, S>,
267}
268
269/// Extension trait that adds `.paginate(db, page_size)` to anything
270/// page-able — [`Select`](crate::Select), [`SelectTwo`](crate::SelectTwo),
271/// [`Selector`], [`SelectorRaw`].
272#[async_trait::async_trait]
273pub trait PaginatorTrait<'db, C>
274where
275    C: ConnectionTrait,
276{
277    /// The [`SelectorTrait`] that materialises each row of the page.
278    type Selector: SelectorTrait + Send + Sync + 'db;
279
280    /// Paginate the result of a select operation.
281    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector>;
282
283    /// Count the number of rows this query selects, honoring any `limit` and
284    /// `offset` set on it.
285    ///
286    /// This differs from [`Paginator::num_items`], which resets `limit`/`offset`
287    /// to report the grand total for pagination. `count` wraps the query as-is in
288    /// a `SELECT COUNT(*) FROM (..)` subquery, so a query with a `limit` counts at
289    /// most that many rows.
290    async fn count(self, db: &'db C) -> Result<u64, DbErr>
291    where
292        Self: Send + Sized,
293    {
294        let paginator = self.paginate(db, 1);
295        let query = SelectStatement::new()
296            .expr(Expr::cust("COUNT(*) AS num_items"))
297            .from_subquery(
298                paginator.query.clone().clear_order_by().to_owned(),
299                "sub_query",
300            )
301            .to_owned();
302        let result = match db.query_one(&query).await? {
303            Some(res) => res,
304            None => return Ok(0),
305        };
306        Ok(result.try_get::<i64>("", "num_items")? as u64)
307    }
308}
309
310impl<'db, C, S> PaginatorTrait<'db, C> for Selector<S>
311where
312    C: ConnectionTrait,
313    S: SelectorTrait + Send + Sync + 'db,
314{
315    type Selector = S;
316
317    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, S> {
318        assert!(page_size != 0, "page_size should not be zero");
319        Paginator {
320            query: self.query,
321            page: 0,
322            page_size,
323            db,
324            selector: PhantomData,
325        }
326    }
327}
328
329impl<'db, C, S> PaginatorTrait<'db, C> for SelectorRaw<S>
330where
331    C: ConnectionTrait,
332    S: SelectorTrait + Send + Sync + 'db,
333{
334    type Selector = S;
335    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, S> {
336        assert!(page_size != 0, "page_size should not be zero");
337        let sql = self.stmt.sql.trim()[6..].trim().to_owned();
338        let mut query = SelectStatement::new();
339        query.expr(if let Some(values) = self.stmt.values {
340            Expr::cust_with_values(sql, values.0)
341        } else {
342            Expr::cust(sql)
343        });
344
345        Paginator {
346            query,
347            page: 0,
348            page_size,
349            db,
350            selector: PhantomData,
351        }
352    }
353}
354
355impl<'db, C, M, E> PaginatorTrait<'db, C> for Select<E>
356where
357    C: ConnectionTrait,
358    E: EntityTrait<Model = M>,
359    M: FromQueryResult + Sized + Send + Sync + 'db,
360{
361    type Selector = SelectModel<M>;
362
363    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
364        self.into_model().paginate(db, page_size)
365    }
366}
367
368impl<'db, C, M, N, E, F> PaginatorTrait<'db, C> for SelectTwo<E, F>
369where
370    C: ConnectionTrait,
371    E: EntityTrait<Model = M>,
372    F: EntityTrait<Model = N>,
373    M: FromQueryResult + Sized + Send + Sync + 'db,
374    N: FromQueryResult + Sized + Send + Sync + 'db,
375{
376    type Selector = SelectTwoModel<M, N>;
377
378    fn paginate(self, db: &'db C, page_size: u64) -> Paginator<'db, C, Self::Selector> {
379        self.into_model().paginate(db, page_size)
380    }
381}
382
383#[cfg(feature = "sync")]
384impl<'db, C, S> Iterator for PaginatorStream<'db, C, S>
385where
386    C: ConnectionTrait,
387    S: SelectorTrait + 'db,
388{
389    type Item = Result<Vec<S::Item>, DbErr>;
390
391    fn next(&mut self) -> Option<Self::Item> {
392        match self.paginator.fetch_and_next() {
393            Ok(Some(vec)) => Some(Ok(vec)),
394            Ok(None) => None,
395            Err(e) => Some(Err(e)),
396        }
397    }
398}
399
400#[cfg(test)]
401#[cfg(feature = "mock")]
402mod tests {
403    use super::*;
404    use crate::entity::prelude::*;
405    #[cfg(feature = "sync")]
406    use crate::util::StreamShim;
407    use crate::{DatabaseConnection, DbBackend, MockDatabase, Transaction};
408    use crate::{QueryOrder, QuerySelect};
409    use crate::{Statement, tests_cfg::*};
410    use futures_util::{TryStreamExt, stream::TryNext};
411    use pretty_assertions::assert_eq;
412    use sea_query::{Expr, SelectStatement, Value};
413    use std::sync::LazyLock;
414
415    static RAW_STMT: LazyLock<Statement> = LazyLock::new(|| {
416        Statement::from_sql_and_values(
417            DbBackend::Postgres,
418            r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit""#,
419            [],
420        )
421    });
422
423    fn setup() -> (DatabaseConnection, Vec<Vec<fruit::Model>>) {
424        let page1 = vec![
425            fruit::Model {
426                id: 1,
427                name: "Blueberry".into(),
428                cake_id: Some(1),
429            },
430            fruit::Model {
431                id: 2,
432                name: "Raspberry".into(),
433                cake_id: Some(1),
434            },
435        ];
436
437        let page2 = vec![fruit::Model {
438            id: 3,
439            name: "Strawberry".into(),
440            cake_id: Some(2),
441        }];
442
443        let page3 = Vec::<fruit::Model>::new();
444
445        let db = MockDatabase::new(DbBackend::Postgres)
446            .append_query_results([page1.clone(), page2.clone(), page3.clone()])
447            .into_connection();
448
449        (db, vec![page1, page2, page3])
450    }
451
452    fn setup_num_items() -> (DatabaseConnection, i64) {
453        let num_items = 3;
454        let db = MockDatabase::new(DbBackend::Postgres)
455            .append_query_results([[maplit::btreemap! {
456                "num_items" => Into::<Value>::into(num_items),
457            }]])
458            .into_connection();
459
460        (db, num_items)
461    }
462
463    #[smol_potat::test]
464    async fn fetch_page() -> Result<(), DbErr> {
465        let (db, pages) = setup();
466
467        let paginator = fruit::Entity::find().paginate(&db, 2);
468
469        assert_eq!(paginator.fetch_page(0).await?, pages[0].clone());
470        assert_eq!(paginator.fetch_page(1).await?, pages[1].clone());
471        assert_eq!(paginator.fetch_page(2).await?, pages[2].clone());
472
473        let mut select = SelectStatement::new()
474            .exprs([
475                Expr::col((fruit::Entity, fruit::Column::Id)),
476                Expr::col((fruit::Entity, fruit::Column::Name)),
477                Expr::col((fruit::Entity, fruit::Column::CakeId)),
478            ])
479            .from(fruit::Entity)
480            .to_owned();
481
482        let query_builder = db.get_database_backend();
483        let stmts = [
484            query_builder.build(select.clone().offset(0).limit(2)),
485            query_builder.build(select.clone().offset(2).limit(2)),
486            query_builder.build(select.offset(4).limit(2)),
487        ];
488
489        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
490        Ok(())
491    }
492
493    #[smol_potat::test]
494    async fn fetch_page_raw() -> Result<(), DbErr> {
495        let (db, pages) = setup();
496
497        let paginator = fruit::Entity::find()
498            .from_raw_sql(RAW_STMT.clone())
499            .paginate(&db, 2);
500
501        assert_eq!(paginator.fetch_page(0).await?, pages[0].clone());
502        assert_eq!(paginator.fetch_page(1).await?, pages[1].clone());
503        assert_eq!(paginator.fetch_page(2).await?, pages[2].clone());
504
505        let mut select = SelectStatement::new()
506            .exprs([
507                Expr::col((fruit::Entity, fruit::Column::Id)),
508                Expr::col((fruit::Entity, fruit::Column::Name)),
509                Expr::col((fruit::Entity, fruit::Column::CakeId)),
510            ])
511            .from(fruit::Entity)
512            .to_owned();
513
514        let query_builder = db.get_database_backend();
515        let stmts = [
516            query_builder.build(select.clone().offset(0).limit(2)),
517            query_builder.build(select.clone().offset(2).limit(2)),
518            query_builder.build(select.offset(4).limit(2)),
519        ];
520
521        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
522        Ok(())
523    }
524
525    #[smol_potat::test]
526    async fn fetch() -> Result<(), DbErr> {
527        let (db, pages) = setup();
528
529        let mut paginator = fruit::Entity::find().paginate(&db, 2);
530
531        assert_eq!(paginator.fetch().await?, pages[0].clone());
532        paginator.next();
533
534        assert_eq!(paginator.fetch().await?, pages[1].clone());
535        paginator.next();
536
537        assert_eq!(paginator.fetch().await?, pages[2].clone());
538
539        let mut select = SelectStatement::new()
540            .exprs([
541                Expr::col((fruit::Entity, fruit::Column::Id)),
542                Expr::col((fruit::Entity, fruit::Column::Name)),
543                Expr::col((fruit::Entity, fruit::Column::CakeId)),
544            ])
545            .from(fruit::Entity)
546            .to_owned();
547
548        let query_builder = db.get_database_backend();
549        let stmts = [
550            query_builder.build(select.clone().offset(0).limit(2)),
551            query_builder.build(select.clone().offset(2).limit(2)),
552            query_builder.build(select.offset(4).limit(2)),
553        ];
554
555        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
556        Ok(())
557    }
558
559    #[smol_potat::test]
560    async fn fetch_raw() -> Result<(), DbErr> {
561        let (db, pages) = setup();
562
563        let mut paginator = fruit::Entity::find()
564            .from_raw_sql(RAW_STMT.clone())
565            .paginate(&db, 2);
566
567        assert_eq!(paginator.fetch().await?, pages[0].clone());
568        paginator.next();
569
570        assert_eq!(paginator.fetch().await?, pages[1].clone());
571        paginator.next();
572
573        assert_eq!(paginator.fetch().await?, pages[2].clone());
574
575        let mut select = SelectStatement::new()
576            .exprs([
577                Expr::col((fruit::Entity, fruit::Column::Id)),
578                Expr::col((fruit::Entity, fruit::Column::Name)),
579                Expr::col((fruit::Entity, fruit::Column::CakeId)),
580            ])
581            .from(fruit::Entity)
582            .to_owned();
583
584        let query_builder = db.get_database_backend();
585        let stmts = [
586            query_builder.build(select.clone().offset(0).limit(2)),
587            query_builder.build(select.clone().offset(2).limit(2)),
588            query_builder.build(select.offset(4).limit(2)),
589        ];
590
591        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
592        Ok(())
593    }
594
595    #[smol_potat::test]
596    async fn num_pages() -> Result<(), DbErr> {
597        let (db, num_items) = setup_num_items();
598
599        let num_items = num_items as u64;
600        let page_size = 2_u64;
601        let num_pages = (num_items / page_size) + (num_items % page_size > 0) as u64;
602        let paginator = fruit::Entity::find().paginate(&db, page_size);
603
604        assert_eq!(paginator.num_pages().await?, num_pages);
605
606        let sub_query = SelectStatement::new()
607            .exprs([
608                Expr::col((fruit::Entity, fruit::Column::Id)),
609                Expr::col((fruit::Entity, fruit::Column::Name)),
610                Expr::col((fruit::Entity, fruit::Column::CakeId)),
611            ])
612            .from(fruit::Entity)
613            .to_owned();
614
615        let select = SelectStatement::new()
616            .expr(Expr::cust("COUNT(*) AS num_items"))
617            .from_subquery(sub_query, "sub_query")
618            .to_owned();
619
620        let query_builder = db.get_database_backend();
621        let stmts = [query_builder.build(&select)];
622
623        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
624        Ok(())
625    }
626
627    #[smol_potat::test]
628    async fn num_pages_raw() -> Result<(), DbErr> {
629        let (db, num_items) = setup_num_items();
630
631        let num_items = num_items as u64;
632        let page_size = 2_u64;
633        let num_pages = (num_items / page_size) + (num_items % page_size > 0) as u64;
634        let paginator = fruit::Entity::find()
635            .from_raw_sql(RAW_STMT.clone())
636            .paginate(&db, page_size);
637
638        assert_eq!(paginator.num_pages().await?, num_pages);
639
640        let sub_query = SelectStatement::new()
641            .exprs([
642                Expr::col((fruit::Entity, fruit::Column::Id)),
643                Expr::col((fruit::Entity, fruit::Column::Name)),
644                Expr::col((fruit::Entity, fruit::Column::CakeId)),
645            ])
646            .from(fruit::Entity)
647            .to_owned();
648
649        let select = SelectStatement::new()
650            .expr(Expr::cust("COUNT(*) AS num_items"))
651            .from_subquery(sub_query, "sub_query")
652            .to_owned();
653
654        let query_builder = db.get_database_backend();
655        let stmts = [query_builder.build(&select)];
656
657        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
658        Ok(())
659    }
660
661    #[smol_potat::test]
662    async fn next_and_cur_page() -> Result<(), DbErr> {
663        let (db, _) = setup();
664
665        let mut paginator = fruit::Entity::find().paginate(&db, 2);
666
667        assert_eq!(paginator.cur_page(), 0);
668        paginator.next();
669
670        assert_eq!(paginator.cur_page(), 1);
671        paginator.next();
672
673        assert_eq!(paginator.cur_page(), 2);
674        Ok(())
675    }
676
677    #[smol_potat::test]
678    async fn next_and_cur_page_raw() -> Result<(), DbErr> {
679        let (db, _) = setup();
680
681        let mut paginator = fruit::Entity::find()
682            .from_raw_sql(RAW_STMT.clone())
683            .paginate(&db, 2);
684
685        assert_eq!(paginator.cur_page(), 0);
686        paginator.next();
687
688        assert_eq!(paginator.cur_page(), 1);
689        paginator.next();
690
691        assert_eq!(paginator.cur_page(), 2);
692        Ok(())
693    }
694
695    #[smol_potat::test]
696    async fn fetch_and_next() -> Result<(), DbErr> {
697        let (db, pages) = setup();
698
699        let mut paginator = fruit::Entity::find().paginate(&db, 2);
700
701        assert_eq!(paginator.cur_page(), 0);
702        assert_eq!(paginator.fetch_and_next().await?, Some(pages[0].clone()));
703
704        assert_eq!(paginator.cur_page(), 1);
705        assert_eq!(paginator.fetch_and_next().await?, Some(pages[1].clone()));
706
707        assert_eq!(paginator.cur_page(), 2);
708        assert_eq!(paginator.fetch_and_next().await?, None);
709
710        let mut select = SelectStatement::new()
711            .exprs([
712                Expr::col((fruit::Entity, fruit::Column::Id)),
713                Expr::col((fruit::Entity, fruit::Column::Name)),
714                Expr::col((fruit::Entity, fruit::Column::CakeId)),
715            ])
716            .from(fruit::Entity)
717            .to_owned();
718
719        let query_builder = db.get_database_backend();
720        let stmts = [
721            query_builder.build(select.clone().offset(0).limit(2)),
722            query_builder.build(select.clone().offset(2).limit(2)),
723            query_builder.build(select.offset(4).limit(2)),
724        ];
725
726        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
727        Ok(())
728    }
729
730    #[smol_potat::test]
731    async fn fetch_and_next_raw() -> Result<(), DbErr> {
732        let (db, pages) = setup();
733
734        let mut paginator = fruit::Entity::find()
735            .from_raw_sql(RAW_STMT.clone())
736            .paginate(&db, 2);
737
738        assert_eq!(paginator.cur_page(), 0);
739        assert_eq!(paginator.fetch_and_next().await?, Some(pages[0].clone()));
740
741        assert_eq!(paginator.cur_page(), 1);
742        assert_eq!(paginator.fetch_and_next().await?, Some(pages[1].clone()));
743
744        assert_eq!(paginator.cur_page(), 2);
745        assert_eq!(paginator.fetch_and_next().await?, None);
746
747        let mut select = SelectStatement::new()
748            .exprs([
749                Expr::col((fruit::Entity, fruit::Column::Id)),
750                Expr::col((fruit::Entity, fruit::Column::Name)),
751                Expr::col((fruit::Entity, fruit::Column::CakeId)),
752            ])
753            .from(fruit::Entity)
754            .to_owned();
755
756        let query_builder = db.get_database_backend();
757        let stmts = [
758            query_builder.build(select.clone().offset(0).limit(2)),
759            query_builder.build(select.clone().offset(2).limit(2)),
760            query_builder.build(select.offset(4).limit(2)),
761        ];
762
763        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
764        Ok(())
765    }
766
767    #[smol_potat::test]
768    async fn into_stream() -> Result<(), DbErr> {
769        let (db, pages) = setup();
770
771        let mut fruit_stream = fruit::Entity::find().paginate(&db, 2).into_stream();
772
773        assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
774        assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
775        assert_eq!(fruit_stream.try_next().await?, None);
776
777        drop(fruit_stream);
778
779        let mut select = SelectStatement::new()
780            .exprs([
781                Expr::col((fruit::Entity, fruit::Column::Id)),
782                Expr::col((fruit::Entity, fruit::Column::Name)),
783                Expr::col((fruit::Entity, fruit::Column::CakeId)),
784            ])
785            .from(fruit::Entity)
786            .to_owned();
787
788        let query_builder = db.get_database_backend();
789        let stmts = [
790            query_builder.build(select.clone().offset(0).limit(2)),
791            query_builder.build(select.clone().offset(2).limit(2)),
792            query_builder.build(select.offset(4).limit(2)),
793        ];
794
795        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
796        Ok(())
797    }
798
799    #[smol_potat::test]
800    async fn into_stream_raw() -> Result<(), DbErr> {
801        let (db, pages) = setup();
802
803        let mut fruit_stream = fruit::Entity::find()
804            .from_raw_sql(RAW_STMT.clone())
805            .paginate(&db, 2)
806            .into_stream();
807
808        assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
809        assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
810        assert_eq!(fruit_stream.try_next().await?, None);
811
812        drop(fruit_stream);
813
814        let mut select = SelectStatement::new()
815            .exprs([
816                Expr::col((fruit::Entity, fruit::Column::Id)),
817                Expr::col((fruit::Entity, fruit::Column::Name)),
818                Expr::col((fruit::Entity, fruit::Column::CakeId)),
819            ])
820            .from(fruit::Entity)
821            .to_owned();
822
823        let query_builder = db.get_database_backend();
824        let stmts = [
825            query_builder.build(select.clone().offset(0).limit(2)),
826            query_builder.build(select.clone().offset(2).limit(2)),
827            query_builder.build(select.offset(4).limit(2)),
828        ];
829
830        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
831        Ok(())
832    }
833
834    #[smol_potat::test]
835    async fn into_stream_raw_leading_spaces() -> Result<(), DbErr> {
836        let (db, pages) = setup();
837
838        let raw_stmt = Statement::from_sql_and_values(
839            DbBackend::Postgres,
840            r#"  SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id" FROM "fruit"  "#,
841            [],
842        );
843
844        let mut fruit_stream = fruit::Entity::find()
845            .from_raw_sql(raw_stmt.clone())
846            .paginate(&db, 2)
847            .into_stream();
848
849        assert_eq!(fruit_stream.try_next().await?, Some(pages[0].clone()));
850        assert_eq!(fruit_stream.try_next().await?, Some(pages[1].clone()));
851        assert_eq!(fruit_stream.try_next().await?, None);
852
853        drop(fruit_stream);
854
855        let mut select = SelectStatement::new()
856            .exprs([
857                Expr::col((fruit::Entity, fruit::Column::Id)),
858                Expr::col((fruit::Entity, fruit::Column::Name)),
859                Expr::col((fruit::Entity, fruit::Column::CakeId)),
860            ])
861            .from(fruit::Entity)
862            .to_owned();
863
864        let query_builder = db.get_database_backend();
865        let stmts = [
866            query_builder.build(select.clone().offset(0).limit(2)),
867            query_builder.build(select.clone().offset(2).limit(2)),
868            query_builder.build(select.offset(4).limit(2)),
869        ];
870
871        assert_eq!(db.into_transaction_log(), Transaction::wrap(stmts));
872        Ok(())
873    }
874
875    #[smol_potat::test]
876    #[should_panic]
877    async fn error() {
878        let (db, _pages) = setup();
879
880        fruit::Entity::find().paginate(&db, 0);
881    }
882}