sea_orm/executor/
paginator.rs

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