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