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