sea_orm/executor/
cursor.rs

1use crate::{
2    ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IdentityOf, IntoIdentity,
3    PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel,
4    SelectThree, SelectThreeModel, SelectTwo, SelectTwoModel, SelectorTrait,
5};
6use sea_query::{
7    Condition, DynIden, Expr, IntoValueTuple, Order, SeaRc, SelectStatement, SimpleExpr, Value,
8    ValueTuple,
9};
10use std::marker::PhantomData;
11use strum::IntoEnumIterator as Iterable;
12
13#[cfg(feature = "with-json")]
14use crate::JsonValue;
15
16/// Cursor pagination
17#[derive(Debug, Clone)]
18pub struct Cursor<S>
19where
20    S: SelectorTrait,
21{
22    query: SelectStatement,
23    table: DynIden,
24    order_columns: Identity,
25    secondary_order_by: Vec<(DynIden, Identity)>,
26    first: Option<u64>,
27    last: Option<u64>,
28    before: Option<ValueTuple>,
29    after: Option<ValueTuple>,
30    sort_asc: bool,
31    is_result_reversed: bool,
32    phantom: PhantomData<S>,
33}
34
35impl<S> Cursor<S>
36where
37    S: SelectorTrait,
38{
39    /// Create a new cursor
40    pub fn new<C>(query: SelectStatement, table: DynIden, order_columns: C) -> Self
41    where
42        C: IntoIdentity,
43    {
44        Self {
45            query,
46            table,
47            order_columns: order_columns.into_identity(),
48            last: None,
49            first: None,
50            after: None,
51            before: None,
52            sort_asc: true,
53            is_result_reversed: false,
54            phantom: PhantomData,
55            secondary_order_by: Default::default(),
56        }
57    }
58
59    /// Filter paginated result with corresponding column less than the input value
60    pub fn before<V>(&mut self, values: V) -> &mut Self
61    where
62        V: IntoValueTuple,
63    {
64        self.before = Some(values.into_value_tuple());
65        self
66    }
67
68    /// Filter paginated result with corresponding column greater than the input value
69    pub fn after<V>(&mut self, values: V) -> &mut Self
70    where
71        V: IntoValueTuple,
72    {
73        self.after = Some(values.into_value_tuple());
74        self
75    }
76
77    fn apply_filters(&mut self) -> &mut Self {
78        if let Some(values) = self.after.clone() {
79            let condition = self.apply_filter(values, |c, v| {
80                let exp = Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c)));
81                if self.sort_asc {
82                    exp.gt(v)
83                } else {
84                    exp.lt(v)
85                }
86            });
87            self.query.cond_where(condition);
88        }
89
90        if let Some(values) = self.before.clone() {
91            let condition = self.apply_filter(values, |c, v| {
92                let exp = Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c)));
93                if self.sort_asc {
94                    exp.lt(v)
95                } else {
96                    exp.gt(v)
97                }
98            });
99            self.query.cond_where(condition);
100        }
101
102        self
103    }
104
105    fn apply_filter<F>(&self, values: ValueTuple, f: F) -> Condition
106    where
107        F: Fn(&DynIden, Value) -> SimpleExpr,
108    {
109        match (&self.order_columns, values) {
110            (Identity::Unary(c1), ValueTuple::One(v1)) => Condition::all().add(f(c1, v1)),
111            (Identity::Binary(c1, c2), ValueTuple::Two(v1, v2)) => Condition::any()
112                .add(
113                    Condition::all()
114                        .add(
115                            Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c1))).eq(v1.clone()),
116                        )
117                        .add(f(c2, v2)),
118                )
119                .add(f(c1, v1)),
120            (Identity::Ternary(c1, c2, c3), ValueTuple::Three(v1, v2, v3)) => Condition::any()
121                .add(
122                    Condition::all()
123                        .add(
124                            Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c1))).eq(v1.clone()),
125                        )
126                        .add(
127                            Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c2))).eq(v2.clone()),
128                        )
129                        .add(f(c3, v3)),
130                )
131                .add(
132                    Condition::all()
133                        .add(
134                            Expr::col((SeaRc::clone(&self.table), SeaRc::clone(c1))).eq(v1.clone()),
135                        )
136                        .add(f(c2, v2)),
137                )
138                .add(f(c1, v1)),
139            (Identity::Many(col_vec), ValueTuple::Many(val_vec))
140                if col_vec.len() == val_vec.len() =>
141            {
142                // The length of `col_vec` and `val_vec` should be equal and is denoted by "n".
143                //
144                // The elements of `col_vec` and `val_vec` are denoted by:
145                //   - `col_vec`: "col_1", "col_2", ..., "col_n-1", "col_n"
146                //   - `val_vec`: "val_1", "val_2", ..., "val_n-1", "val_n"
147                //
148                // The general form of the where condition should have "n" number of inner-AND-condition chained by an outer-OR-condition.
149                // The "n"-th inner-AND-condition should have exactly "n" number of column value expressions,
150                // to construct the expression we take the first "n" number of column and value from the respected vector.
151                //   - if it's not the last element, then we construct a "col_1 = val_1" equal expression
152                //   - otherwise, for the last element, we should construct a "col_n > val_n" greater than or "col_n < val_n" less than expression.
153                // i.e.
154                // WHERE
155                //   (col_1 = val_1 AND col_2 = val_2 AND ... AND col_n > val_n)
156                //   OR (col_1 = val_1 AND col_2 = val_2 AND ... AND col_n-1 > val_n-1)
157                //   OR (col_1 = val_1 AND col_2 = val_2 AND ... AND col_n-2 > val_n-2)
158                //   OR ...
159                //   OR (col_1 = val_1 AND col_2 > val_2)
160                //   OR (col_1 > val_1)
161
162                // Counting from 1 to "n" (inclusive) but in reverse, i.e. n, n-1, ..., 2, 1
163                (1..=col_vec.len())
164                    .rev()
165                    .fold(Condition::any(), |cond_any, n| {
166                        // Construct the inner-AND-condition
167                        let inner_cond_all =
168                            // Take the first "n" elements from the column and value vector respectively
169                            col_vec.iter().zip(val_vec.iter()).enumerate().take(n).fold(
170                                Condition::all(),
171                                |inner_cond_all, (i, (col, val))| {
172                                    let val = val.clone();
173                                    // Construct a equal expression,
174                                    // except for the last one being greater than or less than expression
175                                    let expr = if i != (n - 1) {
176                                        Expr::col((SeaRc::clone(&self.table), SeaRc::clone(col)))
177                                            .eq(val)
178                                    } else {
179                                        f(col, val)
180                                    };
181                                    // Chain it with AND operator
182                                    inner_cond_all.add(expr)
183                                },
184                            );
185                        // Chain inner-AND-condition with OR operator
186                        cond_any.add(inner_cond_all)
187                    })
188            }
189            _ => panic!("column arity mismatch"),
190        }
191    }
192
193    /// Use ascending sort order
194    pub fn asc(&mut self) -> &mut Self {
195        self.sort_asc = true;
196        self
197    }
198
199    /// Use descending sort order
200    pub fn desc(&mut self) -> &mut Self {
201        self.sort_asc = false;
202        self
203    }
204
205    /// Limit result set to only first N rows in ascending order of the order by column
206    pub fn first(&mut self, num_rows: u64) -> &mut Self {
207        self.last = None;
208        self.first = Some(num_rows);
209        self
210    }
211
212    /// Limit result set to only last N rows in ascending order of the order by column
213    pub fn last(&mut self, num_rows: u64) -> &mut Self {
214        self.first = None;
215        self.last = Some(num_rows);
216        self
217    }
218
219    fn resolve_sort_order(&mut self) -> Order {
220        let should_reverse_order = self.last.is_some();
221        self.is_result_reversed = should_reverse_order;
222
223        if (self.sort_asc && !should_reverse_order) || (!self.sort_asc && should_reverse_order) {
224            Order::Asc
225        } else {
226            Order::Desc
227        }
228    }
229
230    fn apply_limit(&mut self) -> &mut Self {
231        if let Some(num_rows) = self.first {
232            self.query.limit(num_rows);
233        } else if let Some(num_rows) = self.last {
234            self.query.limit(num_rows);
235        }
236
237        self
238    }
239
240    fn apply_order_by(&mut self) -> &mut Self {
241        self.query.clear_order_by();
242        let ord = self.resolve_sort_order();
243
244        let query = &mut self.query;
245        let order = |query: &mut SelectStatement, col| {
246            query.order_by((SeaRc::clone(&self.table), SeaRc::clone(col)), ord.clone());
247        };
248        match &self.order_columns {
249            Identity::Unary(c1) => {
250                order(query, c1);
251            }
252            Identity::Binary(c1, c2) => {
253                order(query, c1);
254                order(query, c2);
255            }
256            Identity::Ternary(c1, c2, c3) => {
257                order(query, c1);
258                order(query, c2);
259                order(query, c3);
260            }
261            Identity::Many(vec) => {
262                for col in vec.iter() {
263                    order(query, col);
264                }
265            }
266        }
267
268        for (tbl, col) in self.secondary_order_by.iter().cloned() {
269            if let Identity::Unary(c1) = col {
270                query.order_by((tbl, c1), ord.clone());
271            }
272        }
273
274        self
275    }
276
277    /// Fetch the paginated result
278    pub async fn all<C>(&mut self, db: &C) -> Result<Vec<S::Item>, DbErr>
279    where
280        C: ConnectionTrait,
281    {
282        self.apply_limit();
283        self.apply_order_by();
284        self.apply_filters();
285
286        let stmt = db.get_database_backend().build(&self.query);
287        let rows = db.query_all(stmt).await?;
288        let mut buffer = Vec::with_capacity(rows.len());
289        for row in rows.into_iter() {
290            buffer.push(S::from_raw_query_result(row)?);
291        }
292        if self.is_result_reversed {
293            buffer.reverse()
294        }
295        Ok(buffer)
296    }
297
298    /// Construct a [Cursor] that fetch any custom struct
299    pub fn into_model<M>(self) -> Cursor<SelectModel<M>>
300    where
301        M: FromQueryResult,
302    {
303        Cursor {
304            query: self.query,
305            table: self.table,
306            order_columns: self.order_columns,
307            last: self.last,
308            first: self.first,
309            after: self.after,
310            before: self.before,
311            sort_asc: self.sort_asc,
312            is_result_reversed: self.is_result_reversed,
313            phantom: PhantomData,
314            secondary_order_by: self.secondary_order_by,
315        }
316    }
317
318    /// Return a [Selector] from `Self` that wraps a [SelectModel] with a [PartialModel](PartialModelTrait)
319    pub fn into_partial_model<M>(self) -> Cursor<SelectModel<M>>
320    where
321        M: PartialModelTrait,
322    {
323        M::select_cols(QuerySelect::select_only(self)).into_model::<M>()
324    }
325
326    /// Construct a [Cursor] that fetch JSON value
327    #[cfg(feature = "with-json")]
328    pub fn into_json(self) -> Cursor<SelectModel<JsonValue>> {
329        Cursor {
330            query: self.query,
331            table: self.table,
332            order_columns: self.order_columns,
333            last: self.last,
334            first: self.first,
335            after: self.after,
336            before: self.before,
337            sort_asc: self.sort_asc,
338            is_result_reversed: self.is_result_reversed,
339            phantom: PhantomData,
340            secondary_order_by: self.secondary_order_by,
341        }
342    }
343
344    /// Set the cursor ordering for another table when dealing with SelectTwo
345    pub fn set_secondary_order_by(&mut self, tbl_col: Vec<(DynIden, Identity)>) -> &mut Self {
346        self.secondary_order_by = tbl_col;
347        self
348    }
349}
350
351impl<S> QuerySelect for Cursor<S>
352where
353    S: SelectorTrait,
354{
355    type QueryStatement = SelectStatement;
356
357    fn query(&mut self) -> &mut SelectStatement {
358        &mut self.query
359    }
360}
361
362impl<S> QueryOrder for Cursor<S>
363where
364    S: SelectorTrait,
365{
366    type QueryStatement = SelectStatement;
367
368    fn query(&mut self) -> &mut SelectStatement {
369        &mut self.query
370    }
371}
372
373/// A trait for any type that can be turn into a cursor
374pub trait CursorTrait {
375    /// Select operation
376    type Selector: SelectorTrait + Send + Sync;
377}
378
379impl<E, M> CursorTrait for Select<E>
380where
381    E: EntityTrait<Model = M>,
382    M: FromQueryResult + Sized + Send + Sync,
383{
384    type Selector = SelectModel<M>;
385}
386
387impl<E, M> Select<E>
388where
389    E: EntityTrait<Model = M>,
390    M: FromQueryResult + Sized + Send + Sync,
391{
392    /// Convert into a cursor
393    pub fn cursor_by<C>(self, order_columns: C) -> Cursor<SelectModel<M>>
394    where
395        C: IntoIdentity,
396    {
397        Cursor::new(self.query, SeaRc::new(E::default()), order_columns)
398    }
399}
400
401impl<E, F, M, N> CursorTrait for SelectTwo<E, F>
402where
403    E: EntityTrait<Model = M>,
404    F: EntityTrait<Model = N>,
405    M: FromQueryResult + Sized + Send + Sync,
406    N: FromQueryResult + Sized + Send + Sync,
407{
408    type Selector = SelectTwoModel<M, N>;
409}
410
411impl<E, F, G, M, N, O> CursorTrait for SelectThree<E, F, G>
412where
413    E: EntityTrait<Model = M>,
414    F: EntityTrait<Model = N>,
415    G: EntityTrait<Model = O>,
416    M: FromQueryResult + Sized + Send + Sync,
417    N: FromQueryResult + Sized + Send + Sync,
418    O: FromQueryResult + Sized + Send + Sync,
419{
420    type Selector = SelectThreeModel<M, N, O>;
421}
422
423impl<E, F, M, N> SelectTwo<E, F>
424where
425    E: EntityTrait<Model = M>,
426    F: EntityTrait<Model = N>,
427    M: FromQueryResult + Sized + Send + Sync,
428    N: FromQueryResult + Sized + Send + Sync,
429{
430    /// Convert into a cursor using column of first entity
431    pub fn cursor_by<C>(self, order_columns: C) -> Cursor<SelectTwoModel<M, N>>
432    where
433        C: IdentityOf<E>,
434    {
435        let primary_keys: Vec<(DynIden, Identity)> = <F::PrimaryKey as Iterable>::iter()
436            .map(|pk| {
437                (
438                    SeaRc::new(F::default()),
439                    Identity::Unary(SeaRc::new(pk.into_column())),
440                )
441            })
442            .collect();
443        let mut cursor = Cursor::new(
444            self.query,
445            SeaRc::new(E::default()),
446            order_columns.identity_of(),
447        );
448        cursor.set_secondary_order_by(primary_keys);
449        cursor
450    }
451
452    /// Convert into a cursor using column of second entity
453    pub fn cursor_by_other<C>(self, order_columns: C) -> Cursor<SelectTwoModel<M, N>>
454    where
455        C: IdentityOf<F>,
456    {
457        let primary_keys: Vec<(DynIden, Identity)> = <E::PrimaryKey as Iterable>::iter()
458            .map(|pk| {
459                (
460                    SeaRc::new(E::default()),
461                    Identity::Unary(SeaRc::new(pk.into_column())),
462                )
463            })
464            .collect();
465        let mut cursor = Cursor::new(
466            self.query,
467            SeaRc::new(F::default()),
468            order_columns.identity_of(),
469        );
470        cursor.set_secondary_order_by(primary_keys);
471        cursor
472    }
473}
474
475impl<E, F, G, M, N, O> SelectThree<E, F, G>
476where
477    E: EntityTrait<Model = M>,
478    F: EntityTrait<Model = N>,
479    G: EntityTrait<Model = O>,
480    M: FromQueryResult + Sized + Send + Sync,
481    N: FromQueryResult + Sized + Send + Sync,
482    O: FromQueryResult + Sized + Send + Sync,
483{
484    /// Convert into a cursor using column of first entity
485    pub fn cursor_by<C>(self, order_columns: C) -> Cursor<SelectThreeModel<M, N, O>>
486    where
487        C: IdentityOf<E>,
488    {
489        let mut cursor = Cursor::new(
490            self.query,
491            SeaRc::new(E::default()),
492            order_columns.identity_of(),
493        );
494        {
495            let primary_keys: Vec<(DynIden, Identity)> = <F::PrimaryKey as Iterable>::iter()
496                .map(|pk| {
497                    (
498                        SeaRc::new(F::default()),
499                        Identity::Unary(SeaRc::new(pk.into_column())),
500                    )
501                })
502                .collect();
503            cursor.set_secondary_order_by(primary_keys);
504        }
505        {
506            let primary_keys: Vec<(DynIden, Identity)> = <G::PrimaryKey as Iterable>::iter()
507                .map(|pk| {
508                    (
509                        SeaRc::new(G::default()),
510                        Identity::Unary(SeaRc::new(pk.into_column())),
511                    )
512                })
513                .collect();
514            cursor.set_secondary_order_by(primary_keys);
515        }
516        cursor
517    }
518}
519
520#[cfg(test)]
521#[cfg(feature = "mock")]
522mod tests {
523    use crate::entity::prelude::*;
524    use crate::tests_cfg::*;
525    use crate::{DbBackend, MockDatabase, Statement, Transaction};
526    use pretty_assertions::assert_eq;
527
528    #[smol_potat::test]
529    async fn first_2_before_10() -> Result<(), DbErr> {
530        use fruit::*;
531
532        let models = [
533            Model {
534                id: 1,
535                name: "Blueberry".into(),
536                cake_id: Some(1),
537            },
538            Model {
539                id: 2,
540                name: "Raspberry".into(),
541                cake_id: Some(1),
542            },
543        ];
544
545        let db = MockDatabase::new(DbBackend::Postgres)
546            .append_query_results([models.clone()])
547            .into_connection();
548
549        assert_eq!(
550            Entity::find()
551                .cursor_by(Column::Id)
552                .before(10)
553                .first(2)
554                .all(&db)
555                .await?,
556            models
557        );
558
559        assert_eq!(
560            db.into_transaction_log(),
561            [Transaction::many([Statement::from_sql_and_values(
562                DbBackend::Postgres,
563                [
564                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
565                    r#"FROM "fruit""#,
566                    r#"WHERE "fruit"."id" < $1"#,
567                    r#"ORDER BY "fruit"."id" ASC"#,
568                    r#"LIMIT $2"#,
569                ]
570                .join(" ")
571                .as_str(),
572                [10_i32.into(), 2_u64.into()]
573            ),])]
574        );
575
576        Ok(())
577    }
578
579    #[smol_potat::test]
580    async fn last_2_after_10_desc() -> Result<(), DbErr> {
581        use fruit::*;
582
583        let mut models = [
584            Model {
585                id: 1,
586                name: "Blueberry".into(),
587                cake_id: Some(1),
588            },
589            Model {
590                id: 2,
591                name: "Raspberry".into(),
592                cake_id: Some(1),
593            },
594        ];
595
596        let db = MockDatabase::new(DbBackend::Postgres)
597            .append_query_results([models.clone()])
598            .into_connection();
599
600        models.reverse();
601
602        assert_eq!(
603            Entity::find()
604                .cursor_by(Column::Id)
605                .after(10)
606                .last(2)
607                .desc()
608                .all(&db)
609                .await?,
610            models
611        );
612
613        assert_eq!(
614            db.into_transaction_log(),
615            [Transaction::many([Statement::from_sql_and_values(
616                DbBackend::Postgres,
617                [
618                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
619                    r#"FROM "fruit""#,
620                    r#"WHERE "fruit"."id" < $1"#,
621                    r#"ORDER BY "fruit"."id" ASC"#,
622                    r#"LIMIT $2"#,
623                ]
624                .join(" ")
625                .as_str(),
626                [10_i32.into(), 2_u64.into()]
627            ),])]
628        );
629
630        Ok(())
631    }
632
633    #[smol_potat::test]
634    async fn first_2_before_10_also_related_select() -> Result<(), DbErr> {
635        let models = [
636            (
637                cake::Model {
638                    id: 1,
639                    name: "Blueberry Cheese Cake".into(),
640                },
641                Some(fruit::Model {
642                    id: 9,
643                    name: "Blueberry".into(),
644                    cake_id: Some(1),
645                }),
646            ),
647            (
648                cake::Model {
649                    id: 2,
650                    name: "Raspberry Cheese Cake".into(),
651                },
652                Some(fruit::Model {
653                    id: 10,
654                    name: "Raspberry".into(),
655                    cake_id: Some(1),
656                }),
657            ),
658        ];
659
660        let db = MockDatabase::new(DbBackend::Postgres)
661            .append_query_results([models.clone()])
662            .into_connection();
663
664        assert_eq!(
665            cake::Entity::find()
666                .find_also_related(Fruit)
667                .cursor_by(cake::Column::Id)
668                .before(10)
669                .first(2)
670                .all(&db)
671                .await?,
672            models
673        );
674
675        assert_eq!(
676            db.into_transaction_log(),
677            [Transaction::many([Statement::from_sql_and_values(
678                DbBackend::Postgres,
679                [
680                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
681                    r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
682                    r#"FROM "cake""#,
683                    r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
684                    r#"WHERE "cake"."id" < $1"#,
685                    r#"ORDER BY "cake"."id" ASC, "fruit"."id" ASC LIMIT $2"#,
686                ]
687                .join(" ")
688                .as_str(),
689                [10_i32.into(), 2_u64.into()]
690            ),])]
691        );
692
693        Ok(())
694    }
695
696    #[smol_potat::test]
697    async fn last_2_after_10_also_related_select_desc() -> Result<(), DbErr> {
698        let mut models = [
699            (
700                cake::Model {
701                    id: 2,
702                    name: "Raspberry Cheese Cake".into(),
703                },
704                Some(fruit::Model {
705                    id: 10,
706                    name: "Raspberry".into(),
707                    cake_id: Some(1),
708                }),
709            ),
710            (
711                cake::Model {
712                    id: 1,
713                    name: "Blueberry Cheese Cake".into(),
714                },
715                Some(fruit::Model {
716                    id: 9,
717                    name: "Blueberry".into(),
718                    cake_id: Some(1),
719                }),
720            ),
721        ];
722
723        let db = MockDatabase::new(DbBackend::Postgres)
724            .append_query_results([models.clone()])
725            .into_connection();
726
727        models.reverse();
728
729        assert_eq!(
730            cake::Entity::find()
731                .find_also_related(Fruit)
732                .cursor_by(cake::Column::Id)
733                .after(10)
734                .last(2)
735                .desc()
736                .all(&db)
737                .await?,
738            models
739        );
740
741        assert_eq!(
742            db.into_transaction_log(),
743            [Transaction::many([Statement::from_sql_and_values(
744                DbBackend::Postgres,
745                [
746                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
747                    r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
748                    r#"FROM "cake""#,
749                    r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
750                    r#"WHERE "cake"."id" < $1"#,
751                    r#"ORDER BY "cake"."id" ASC, "fruit"."id" ASC LIMIT $2"#,
752                ]
753                .join(" ")
754                .as_str(),
755                [10_i32.into(), 2_u64.into()]
756            ),])]
757        );
758
759        Ok(())
760    }
761
762    #[smol_potat::test]
763    async fn first_2_before_10_also_related_select_cursor_other() -> Result<(), DbErr> {
764        let models = [(
765            cake::Model {
766                id: 1,
767                name: "Blueberry Cheese Cake".into(),
768            },
769            Some(fruit::Model {
770                id: 9,
771                name: "Blueberry".into(),
772                cake_id: Some(1),
773            }),
774        )];
775
776        let db = MockDatabase::new(DbBackend::Postgres)
777            .append_query_results([models.clone()])
778            .into_connection();
779
780        assert_eq!(
781            cake::Entity::find()
782                .find_also_related(Fruit)
783                .cursor_by_other(fruit::Column::Id)
784                .before(10)
785                .first(2)
786                .all(&db)
787                .await?,
788            models
789        );
790
791        assert_eq!(
792            db.into_transaction_log(),
793            [Transaction::many([Statement::from_sql_and_values(
794                DbBackend::Postgres,
795                [
796                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
797                    r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
798                    r#"FROM "cake""#,
799                    r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
800                    r#"WHERE "fruit"."id" < $1"#,
801                    r#"ORDER BY "fruit"."id" ASC, "cake"."id" ASC LIMIT $2"#,
802                ]
803                .join(" ")
804                .as_str(),
805                [10_i32.into(), 2_u64.into()]
806            ),])]
807        );
808
809        Ok(())
810    }
811
812    #[smol_potat::test]
813    async fn last_2_after_10_also_related_select_cursor_other_desc() -> Result<(), DbErr> {
814        let models = [(
815            cake::Model {
816                id: 1,
817                name: "Blueberry Cheese Cake".into(),
818            },
819            Some(fruit::Model {
820                id: 9,
821                name: "Blueberry".into(),
822                cake_id: Some(1),
823            }),
824        )];
825
826        let db = MockDatabase::new(DbBackend::Postgres)
827            .append_query_results([models.clone()])
828            .into_connection();
829
830        assert_eq!(
831            cake::Entity::find()
832                .find_also_related(Fruit)
833                .cursor_by_other(fruit::Column::Id)
834                .after(10)
835                .last(2)
836                .desc()
837                .all(&db)
838                .await?,
839            models
840        );
841
842        assert_eq!(
843            db.into_transaction_log(),
844            [Transaction::many([Statement::from_sql_and_values(
845                DbBackend::Postgres,
846                [
847                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
848                    r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
849                    r#"FROM "cake""#,
850                    r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
851                    r#"WHERE "fruit"."id" < $1"#,
852                    r#"ORDER BY "fruit"."id" ASC, "cake"."id" ASC LIMIT $2"#,
853                ]
854                .join(" ")
855                .as_str(),
856                [10_i32.into(), 2_u64.into()]
857            ),])]
858        );
859
860        Ok(())
861    }
862
863    #[smol_potat::test]
864    async fn first_2_before_10_also_linked_select() -> Result<(), DbErr> {
865        let models = [
866            (
867                cake::Model {
868                    id: 1,
869                    name: "Blueberry Cheese Cake".into(),
870                },
871                Some(vendor::Model {
872                    id: 9,
873                    name: "Blueberry".into(),
874                }),
875            ),
876            (
877                cake::Model {
878                    id: 2,
879                    name: "Raspberry Cheese Cake".into(),
880                },
881                Some(vendor::Model {
882                    id: 10,
883                    name: "Raspberry".into(),
884                }),
885            ),
886        ];
887
888        let db = MockDatabase::new(DbBackend::Postgres)
889            .append_query_results([models.clone()])
890            .into_connection();
891
892        assert_eq!(
893            cake::Entity::find()
894                .find_also_linked(entity_linked::CakeToFillingVendor)
895                .cursor_by(cake::Column::Id)
896                .before(10)
897                .first(2)
898                .all(&db)
899                .await?,
900            models
901        );
902
903        assert_eq!(
904            db.into_transaction_log(),
905            [Transaction::many([Statement::from_sql_and_values(
906                DbBackend::Postgres,
907                [
908                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
909                    r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
910                    r#"FROM "cake""#,
911                    r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
912                    r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
913                    r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
914                    r#"WHERE "cake"."id" < $1 ORDER BY "cake"."id" ASC, "vendor"."id" ASC LIMIT $2"#,
915                ]
916                .join(" ")
917                .as_str(),
918                [10_i32.into(), 2_u64.into()]
919            ),])]
920        );
921
922        Ok(())
923    }
924
925    #[smol_potat::test]
926    async fn last_2_after_10_also_linked_select_desc() -> Result<(), DbErr> {
927        let mut models = [
928            (
929                cake::Model {
930                    id: 2,
931                    name: "Raspberry Cheese Cake".into(),
932                },
933                Some(vendor::Model {
934                    id: 10,
935                    name: "Raspberry".into(),
936                }),
937            ),
938            (
939                cake::Model {
940                    id: 1,
941                    name: "Blueberry Cheese Cake".into(),
942                },
943                Some(vendor::Model {
944                    id: 9,
945                    name: "Blueberry".into(),
946                }),
947            ),
948        ];
949
950        let db = MockDatabase::new(DbBackend::Postgres)
951            .append_query_results([models.clone()])
952            .into_connection();
953
954        models.reverse();
955
956        assert_eq!(
957            cake::Entity::find()
958                .find_also_linked(entity_linked::CakeToFillingVendor)
959                .cursor_by(cake::Column::Id)
960                .after(10)
961                .last(2)
962                .desc()
963                .all(&db)
964                .await?,
965            models
966        );
967
968        assert_eq!(
969            db.into_transaction_log(),
970            [Transaction::many([Statement::from_sql_and_values(
971                DbBackend::Postgres,
972                [
973                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
974                    r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
975                    r#"FROM "cake""#,
976                    r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
977                    r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
978                    r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
979                    r#"WHERE "cake"."id" < $1 ORDER BY "cake"."id" ASC, "vendor"."id" ASC LIMIT $2"#,
980                ]
981                .join(" ")
982                .as_str(),
983                [10_i32.into(), 2_u64.into()]
984            ),])]
985        );
986
987        Ok(())
988    }
989
990    #[smol_potat::test]
991    async fn first_2_before_10_also_linked_select_cursor_other() -> Result<(), DbErr> {
992        let models = [(
993            cake::Model {
994                id: 1,
995                name: "Blueberry Cheese Cake".into(),
996            },
997            Some(vendor::Model {
998                id: 9,
999                name: "Blueberry".into(),
1000            }),
1001        )];
1002
1003        let db = MockDatabase::new(DbBackend::Postgres)
1004            .append_query_results([models.clone()])
1005            .into_connection();
1006
1007        assert_eq!(
1008            cake::Entity::find()
1009                .find_also_linked(entity_linked::CakeToFillingVendor)
1010                .cursor_by_other(vendor::Column::Id)
1011                .before(10)
1012                .first(2)
1013                .all(&db)
1014                .await?,
1015            models
1016        );
1017
1018        assert_eq!(
1019            db.into_transaction_log(),
1020            [Transaction::many([Statement::from_sql_and_values(
1021                DbBackend::Postgres,
1022                [
1023                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1024                    r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
1025                    r#"FROM "cake""#,
1026                    r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1027                    r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1028                    r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1029                    r#"WHERE "vendor"."id" < $1 ORDER BY "vendor"."id" ASC, "cake"."id" ASC LIMIT $2"#,
1030                ]
1031                .join(" ")
1032                .as_str(),
1033                [10_i32.into(), 2_u64.into()]
1034            ),])]
1035        );
1036
1037        Ok(())
1038    }
1039
1040    #[smol_potat::test]
1041    async fn last_2_after_10_also_linked_select_cursor_other_desc() -> Result<(), DbErr> {
1042        let mut models = [(
1043            cake::Model {
1044                id: 1,
1045                name: "Blueberry Cheese Cake".into(),
1046            },
1047            Some(vendor::Model {
1048                id: 9,
1049                name: "Blueberry".into(),
1050            }),
1051        )];
1052
1053        let db = MockDatabase::new(DbBackend::Postgres)
1054            .append_query_results([models.clone()])
1055            .into_connection();
1056
1057        models.reverse();
1058
1059        assert_eq!(
1060            cake::Entity::find()
1061                .find_also_linked(entity_linked::CakeToFillingVendor)
1062                .cursor_by_other(vendor::Column::Id)
1063                .after(10)
1064                .last(2)
1065                .desc()
1066                .all(&db)
1067                .await?,
1068            models
1069        );
1070
1071        assert_eq!(
1072            db.into_transaction_log(),
1073            [Transaction::many([Statement::from_sql_and_values(
1074                DbBackend::Postgres,
1075                [
1076                    r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1077                    r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
1078                    r#"FROM "cake""#,
1079                    r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1080                    r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1081                    r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1082                    r#"WHERE "vendor"."id" < $1 ORDER BY "vendor"."id" ASC, "cake"."id" ASC LIMIT $2"#,
1083                ]
1084                .join(" ")
1085                .as_str(),
1086                [10_i32.into(), 2_u64.into()]
1087            ),])]
1088        );
1089
1090        Ok(())
1091    }
1092
1093    #[smol_potat::test]
1094    async fn last_2_after_10() -> Result<(), DbErr> {
1095        use fruit::*;
1096
1097        let db = MockDatabase::new(DbBackend::Postgres)
1098            .append_query_results([[
1099                Model {
1100                    id: 22,
1101                    name: "Raspberry".into(),
1102                    cake_id: Some(1),
1103                },
1104                Model {
1105                    id: 21,
1106                    name: "Blueberry".into(),
1107                    cake_id: Some(1),
1108                },
1109            ]])
1110            .into_connection();
1111
1112        assert_eq!(
1113            Entity::find()
1114                .cursor_by(Column::Id)
1115                .after(10)
1116                .last(2)
1117                .all(&db)
1118                .await?,
1119            [
1120                Model {
1121                    id: 21,
1122                    name: "Blueberry".into(),
1123                    cake_id: Some(1),
1124                },
1125                Model {
1126                    id: 22,
1127                    name: "Raspberry".into(),
1128                    cake_id: Some(1),
1129                },
1130            ]
1131        );
1132
1133        assert_eq!(
1134            db.into_transaction_log(),
1135            [Transaction::many([Statement::from_sql_and_values(
1136                DbBackend::Postgres,
1137                [
1138                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1139                    r#"FROM "fruit""#,
1140                    r#"WHERE "fruit"."id" > $1"#,
1141                    r#"ORDER BY "fruit"."id" DESC"#,
1142                    r#"LIMIT $2"#,
1143                ]
1144                .join(" ")
1145                .as_str(),
1146                [10_i32.into(), 2_u64.into()]
1147            ),])]
1148        );
1149
1150        Ok(())
1151    }
1152
1153    #[smol_potat::test]
1154    async fn first_2_before_10_desc() -> Result<(), DbErr> {
1155        use fruit::*;
1156
1157        let models = [
1158            Model {
1159                id: 22,
1160                name: "Raspberry".into(),
1161                cake_id: Some(1),
1162            },
1163            Model {
1164                id: 21,
1165                name: "Blueberry".into(),
1166                cake_id: Some(1),
1167            },
1168        ];
1169
1170        let db = MockDatabase::new(DbBackend::Postgres)
1171            .append_query_results([models.clone()])
1172            .into_connection();
1173
1174        assert_eq!(
1175            Entity::find()
1176                .cursor_by(Column::Id)
1177                .before(10)
1178                .first(2)
1179                .desc()
1180                .all(&db)
1181                .await?,
1182            models
1183        );
1184
1185        assert_eq!(
1186            db.into_transaction_log(),
1187            [Transaction::many([Statement::from_sql_and_values(
1188                DbBackend::Postgres,
1189                [
1190                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1191                    r#"FROM "fruit""#,
1192                    r#"WHERE "fruit"."id" > $1"#,
1193                    r#"ORDER BY "fruit"."id" DESC"#,
1194                    r#"LIMIT $2"#,
1195                ]
1196                .join(" ")
1197                .as_str(),
1198                [10_i32.into(), 2_u64.into()]
1199            ),])]
1200        );
1201
1202        Ok(())
1203    }
1204
1205    #[smol_potat::test]
1206    async fn last_2_after_25_before_30() -> Result<(), DbErr> {
1207        use fruit::*;
1208
1209        let db = MockDatabase::new(DbBackend::Postgres)
1210            .append_query_results([[
1211                Model {
1212                    id: 27,
1213                    name: "Raspberry".into(),
1214                    cake_id: Some(1),
1215                },
1216                Model {
1217                    id: 26,
1218                    name: "Blueberry".into(),
1219                    cake_id: Some(1),
1220                },
1221            ]])
1222            .into_connection();
1223
1224        assert_eq!(
1225            Entity::find()
1226                .cursor_by(Column::Id)
1227                .after(25)
1228                .before(30)
1229                .last(2)
1230                .all(&db)
1231                .await?,
1232            [
1233                Model {
1234                    id: 26,
1235                    name: "Blueberry".into(),
1236                    cake_id: Some(1),
1237                },
1238                Model {
1239                    id: 27,
1240                    name: "Raspberry".into(),
1241                    cake_id: Some(1),
1242                },
1243            ]
1244        );
1245
1246        assert_eq!(
1247            db.into_transaction_log(),
1248            [Transaction::many([Statement::from_sql_and_values(
1249                DbBackend::Postgres,
1250                [
1251                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1252                    r#"FROM "fruit""#,
1253                    r#"WHERE "fruit"."id" > $1"#,
1254                    r#"AND "fruit"."id" < $2"#,
1255                    r#"ORDER BY "fruit"."id" DESC"#,
1256                    r#"LIMIT $3"#,
1257                ]
1258                .join(" ")
1259                .as_str(),
1260                [25_i32.into(), 30_i32.into(), 2_u64.into()]
1261            ),])]
1262        );
1263
1264        Ok(())
1265    }
1266
1267    #[smol_potat::test]
1268    async fn first_2_after_30_before_25_desc() -> Result<(), DbErr> {
1269        use fruit::*;
1270
1271        let models = [
1272            Model {
1273                id: 27,
1274                name: "Raspberry".into(),
1275                cake_id: Some(1),
1276            },
1277            Model {
1278                id: 26,
1279                name: "Blueberry".into(),
1280                cake_id: Some(1),
1281            },
1282        ];
1283
1284        let db = MockDatabase::new(DbBackend::Postgres)
1285            .append_query_results([models.clone()])
1286            .into_connection();
1287
1288        assert_eq!(
1289            Entity::find()
1290                .cursor_by(Column::Id)
1291                .after(30)
1292                .before(25)
1293                .first(2)
1294                .desc()
1295                .all(&db)
1296                .await?,
1297            models
1298        );
1299
1300        assert_eq!(
1301            db.into_transaction_log(),
1302            [Transaction::many([Statement::from_sql_and_values(
1303                DbBackend::Postgres,
1304                [
1305                    r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1306                    r#"FROM "fruit""#,
1307                    r#"WHERE "fruit"."id" < $1"#,
1308                    r#"AND "fruit"."id" > $2"#,
1309                    r#"ORDER BY "fruit"."id" DESC"#,
1310                    r#"LIMIT $3"#,
1311                ]
1312                .join(" ")
1313                .as_str(),
1314                [30_i32.into(), 25_i32.into(), 2_u64.into()]
1315            ),])]
1316        );
1317
1318        Ok(())
1319    }
1320
1321    mod test_entity {
1322        use crate as sea_orm;
1323        use crate::entity::prelude::*;
1324
1325        #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1326        #[sea_orm(table_name = "example")]
1327        pub struct Model {
1328            #[sea_orm(primary_key)]
1329            pub id: i32,
1330            #[sea_orm(primary_key)]
1331            pub category: String,
1332        }
1333
1334        #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1335        pub enum Relation {}
1336
1337        impl ActiveModelBehavior for ActiveModel {}
1338    }
1339
1340    mod xyz_entity {
1341        use crate as sea_orm;
1342        use crate::entity::prelude::*;
1343
1344        #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1345        #[sea_orm(table_name = "m")]
1346        pub struct Model {
1347            #[sea_orm(primary_key)]
1348            pub x: i32,
1349            #[sea_orm(primary_key)]
1350            pub y: String,
1351            #[sea_orm(primary_key)]
1352            pub z: i64,
1353        }
1354
1355        #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1356        pub enum Relation {}
1357
1358        impl ActiveModelBehavior for ActiveModel {}
1359    }
1360
1361    #[smol_potat::test]
1362    async fn composite_keys_1() -> Result<(), DbErr> {
1363        use test_entity::*;
1364
1365        let db = MockDatabase::new(DbBackend::Postgres)
1366            .append_query_results([[Model {
1367                id: 1,
1368                category: "CAT".into(),
1369            }]])
1370            .into_connection();
1371
1372        assert!(!Entity::find()
1373            .cursor_by((Column::Category, Column::Id))
1374            .first(3)
1375            .all(&db)
1376            .await?
1377            .is_empty());
1378
1379        assert_eq!(
1380            db.into_transaction_log(),
1381            [Transaction::many([Statement::from_sql_and_values(
1382                DbBackend::Postgres,
1383                [
1384                    r#"SELECT "example"."id", "example"."category""#,
1385                    r#"FROM "example""#,
1386                    r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1387                    r#"LIMIT $1"#,
1388                ]
1389                .join(" ")
1390                .as_str(),
1391                [3_u64.into()]
1392            ),])]
1393        );
1394
1395        Ok(())
1396    }
1397
1398    #[smol_potat::test]
1399    async fn composite_keys_1_desc() -> Result<(), DbErr> {
1400        use test_entity::*;
1401
1402        let db = MockDatabase::new(DbBackend::Postgres)
1403            .append_query_results([[Model {
1404                id: 1,
1405                category: "CAT".into(),
1406            }]])
1407            .into_connection();
1408
1409        assert!(!Entity::find()
1410            .cursor_by((Column::Category, Column::Id))
1411            .last(3)
1412            .desc()
1413            .all(&db)
1414            .await?
1415            .is_empty());
1416
1417        assert_eq!(
1418            db.into_transaction_log(),
1419            [Transaction::many([Statement::from_sql_and_values(
1420                DbBackend::Postgres,
1421                [
1422                    r#"SELECT "example"."id", "example"."category""#,
1423                    r#"FROM "example""#,
1424                    r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1425                    r#"LIMIT $1"#,
1426                ]
1427                .join(" ")
1428                .as_str(),
1429                [3_u64.into()]
1430            ),])]
1431        );
1432
1433        Ok(())
1434    }
1435
1436    #[smol_potat::test]
1437    async fn composite_keys_2() -> Result<(), DbErr> {
1438        use test_entity::*;
1439
1440        let db = MockDatabase::new(DbBackend::Postgres)
1441            .append_query_results([[Model {
1442                id: 1,
1443                category: "CAT".into(),
1444            }]])
1445            .into_connection();
1446
1447        assert!(!Entity::find()
1448            .cursor_by((Column::Category, Column::Id))
1449            .after(("A".to_owned(), 2))
1450            .first(3)
1451            .all(&db)
1452            .await?
1453            .is_empty());
1454
1455        assert_eq!(
1456            db.into_transaction_log(),
1457            [Transaction::many([Statement::from_sql_and_values(
1458                DbBackend::Postgres,
1459                [
1460                    r#"SELECT "example"."id", "example"."category""#,
1461                    r#"FROM "example""#,
1462                    r#"WHERE ("example"."category" = $1 AND "example"."id" > $2)"#,
1463                    r#"OR "example"."category" > $3"#,
1464                    r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1465                    r#"LIMIT $4"#,
1466                ]
1467                .join(" ")
1468                .as_str(),
1469                [
1470                    "A".to_string().into(),
1471                    2i32.into(),
1472                    "A".to_string().into(),
1473                    3_u64.into(),
1474                ]
1475            )])]
1476        );
1477
1478        Ok(())
1479    }
1480
1481    #[smol_potat::test]
1482    async fn composite_keys_2_desc() -> Result<(), DbErr> {
1483        use test_entity::*;
1484
1485        let db = MockDatabase::new(DbBackend::Postgres)
1486            .append_query_results([[Model {
1487                id: 1,
1488                category: "CAT".into(),
1489            }]])
1490            .into_connection();
1491
1492        assert!(!Entity::find()
1493            .cursor_by((Column::Category, Column::Id))
1494            .before(("A".to_owned(), 2))
1495            .last(3)
1496            .desc()
1497            .all(&db)
1498            .await?
1499            .is_empty());
1500
1501        assert_eq!(
1502            db.into_transaction_log(),
1503            [Transaction::many([Statement::from_sql_and_values(
1504                DbBackend::Postgres,
1505                [
1506                    r#"SELECT "example"."id", "example"."category""#,
1507                    r#"FROM "example""#,
1508                    r#"WHERE ("example"."category" = $1 AND "example"."id" > $2)"#,
1509                    r#"OR "example"."category" > $3"#,
1510                    r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1511                    r#"LIMIT $4"#,
1512                ]
1513                .join(" ")
1514                .as_str(),
1515                [
1516                    "A".to_string().into(),
1517                    2i32.into(),
1518                    "A".to_string().into(),
1519                    3_u64.into(),
1520                ]
1521            )])]
1522        );
1523
1524        Ok(())
1525    }
1526
1527    #[smol_potat::test]
1528    async fn composite_keys_3() -> Result<(), DbErr> {
1529        use test_entity::*;
1530
1531        let db = MockDatabase::new(DbBackend::Postgres)
1532            .append_query_results([[Model {
1533                id: 1,
1534                category: "CAT".into(),
1535            }]])
1536            .into_connection();
1537
1538        assert!(!Entity::find()
1539            .cursor_by((Column::Category, Column::Id))
1540            .before(("A".to_owned(), 2))
1541            .last(3)
1542            .all(&db)
1543            .await?
1544            .is_empty());
1545
1546        assert_eq!(
1547            db.into_transaction_log(),
1548            [Transaction::many([Statement::from_sql_and_values(
1549                DbBackend::Postgres,
1550                [
1551                    r#"SELECT "example"."id", "example"."category""#,
1552                    r#"FROM "example""#,
1553                    r#"WHERE ("example"."category" = $1 AND "example"."id" < $2)"#,
1554                    r#"OR "example"."category" < $3"#,
1555                    r#"ORDER BY "example"."category" DESC, "example"."id" DESC"#,
1556                    r#"LIMIT $4"#,
1557                ]
1558                .join(" ")
1559                .as_str(),
1560                [
1561                    "A".to_string().into(),
1562                    2i32.into(),
1563                    "A".to_string().into(),
1564                    3_u64.into(),
1565                ]
1566            )])]
1567        );
1568
1569        Ok(())
1570    }
1571
1572    #[smol_potat::test]
1573    async fn composite_keys_3_desc() -> Result<(), DbErr> {
1574        use test_entity::*;
1575
1576        let db = MockDatabase::new(DbBackend::Postgres)
1577            .append_query_results([[Model {
1578                id: 1,
1579                category: "CAT".into(),
1580            }]])
1581            .into_connection();
1582
1583        assert!(!Entity::find()
1584            .cursor_by((Column::Category, Column::Id))
1585            .after(("A".to_owned(), 2))
1586            .first(3)
1587            .desc()
1588            .all(&db)
1589            .await?
1590            .is_empty());
1591
1592        assert_eq!(
1593            db.into_transaction_log(),
1594            [Transaction::many([Statement::from_sql_and_values(
1595                DbBackend::Postgres,
1596                [
1597                    r#"SELECT "example"."id", "example"."category""#,
1598                    r#"FROM "example""#,
1599                    r#"WHERE ("example"."category" = $1 AND "example"."id" < $2)"#,
1600                    r#"OR "example"."category" < $3"#,
1601                    r#"ORDER BY "example"."category" DESC, "example"."id" DESC"#,
1602                    r#"LIMIT $4"#,
1603                ]
1604                .join(" ")
1605                .as_str(),
1606                [
1607                    "A".to_string().into(),
1608                    2i32.into(),
1609                    "A".to_string().into(),
1610                    3_u64.into(),
1611                ]
1612            )])]
1613        );
1614
1615        Ok(())
1616    }
1617
1618    #[smol_potat::test]
1619    async fn composite_keys_4() -> Result<(), DbErr> {
1620        use xyz_entity::*;
1621
1622        let db = MockDatabase::new(DbBackend::Postgres)
1623            .append_query_results([[Model {
1624                x: 'x' as i32,
1625                y: "y".into(),
1626                z: 'z' as i64,
1627            }]])
1628            .into_connection();
1629
1630        assert!(!Entity::find()
1631            .cursor_by((Column::X, Column::Y, Column::Z))
1632            .first(4)
1633            .all(&db)
1634            .await?
1635            .is_empty());
1636
1637        assert_eq!(
1638            db.into_transaction_log(),
1639            [Transaction::many([Statement::from_sql_and_values(
1640                DbBackend::Postgres,
1641                [
1642                    r#"SELECT "m"."x", "m"."y", "m"."z""#,
1643                    r#"FROM "m""#,
1644                    r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1645                    r#"LIMIT $1"#,
1646                ]
1647                .join(" ")
1648                .as_str(),
1649                [4_u64.into()]
1650            ),])]
1651        );
1652
1653        Ok(())
1654    }
1655
1656    #[smol_potat::test]
1657    async fn composite_keys_4_desc() -> Result<(), DbErr> {
1658        use xyz_entity::*;
1659
1660        let db = MockDatabase::new(DbBackend::Postgres)
1661            .append_query_results([[Model {
1662                x: 'x' as i32,
1663                y: "y".into(),
1664                z: 'z' as i64,
1665            }]])
1666            .into_connection();
1667
1668        assert!(!Entity::find()
1669            .cursor_by((Column::X, Column::Y, Column::Z))
1670            .last(4)
1671            .desc()
1672            .all(&db)
1673            .await?
1674            .is_empty());
1675
1676        assert_eq!(
1677            db.into_transaction_log(),
1678            [Transaction::many([Statement::from_sql_and_values(
1679                DbBackend::Postgres,
1680                [
1681                    r#"SELECT "m"."x", "m"."y", "m"."z""#,
1682                    r#"FROM "m""#,
1683                    r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1684                    r#"LIMIT $1"#,
1685                ]
1686                .join(" ")
1687                .as_str(),
1688                [4_u64.into()]
1689            ),])]
1690        );
1691
1692        Ok(())
1693    }
1694
1695    #[smol_potat::test]
1696    async fn composite_keys_5() -> Result<(), DbErr> {
1697        use xyz_entity::*;
1698
1699        let db = MockDatabase::new(DbBackend::Postgres)
1700            .append_query_results([[Model {
1701                x: 'x' as i32,
1702                y: "y".into(),
1703                z: 'z' as i64,
1704            }]])
1705            .into_connection();
1706
1707        assert!(!Entity::find()
1708            .cursor_by((Column::X, Column::Y, Column::Z))
1709            .after(('x' as i32, "y".to_owned(), 'z' as i64))
1710            .first(4)
1711            .all(&db)
1712            .await?
1713            .is_empty());
1714
1715        assert_eq!(
1716            db.into_transaction_log(),
1717            [Transaction::many([Statement::from_sql_and_values(
1718                DbBackend::Postgres,
1719                [
1720                    r#"SELECT "m"."x", "m"."y", "m"."z""#,
1721                    r#"FROM "m""#,
1722                    r#"WHERE ("m"."x" = $1 AND "m"."y" = $2 AND "m"."z" > $3)"#,
1723                    r#"OR ("m"."x" = $4 AND "m"."y" > $5)"#,
1724                    r#"OR "m"."x" > $6"#,
1725                    r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1726                    r#"LIMIT $7"#,
1727                ]
1728                .join(" ")
1729                .as_str(),
1730                [
1731                    ('x' as i32).into(),
1732                    "y".into(),
1733                    ('z' as i64).into(),
1734                    ('x' as i32).into(),
1735                    "y".into(),
1736                    ('x' as i32).into(),
1737                    4_u64.into(),
1738                ]
1739            ),])]
1740        );
1741
1742        Ok(())
1743    }
1744
1745    #[smol_potat::test]
1746    async fn composite_keys_5_desc() -> Result<(), DbErr> {
1747        use xyz_entity::*;
1748
1749        let db = MockDatabase::new(DbBackend::Postgres)
1750            .append_query_results([[Model {
1751                x: 'x' as i32,
1752                y: "y".into(),
1753                z: 'z' as i64,
1754            }]])
1755            .into_connection();
1756
1757        assert!(!Entity::find()
1758            .cursor_by((Column::X, Column::Y, Column::Z))
1759            .before(('x' as i32, "y".to_owned(), 'z' as i64))
1760            .last(4)
1761            .desc()
1762            .all(&db)
1763            .await?
1764            .is_empty());
1765
1766        assert_eq!(
1767            db.into_transaction_log(),
1768            [Transaction::many([Statement::from_sql_and_values(
1769                DbBackend::Postgres,
1770                [
1771                    r#"SELECT "m"."x", "m"."y", "m"."z""#,
1772                    r#"FROM "m""#,
1773                    r#"WHERE ("m"."x" = $1 AND "m"."y" = $2 AND "m"."z" > $3)"#,
1774                    r#"OR ("m"."x" = $4 AND "m"."y" > $5)"#,
1775                    r#"OR "m"."x" > $6"#,
1776                    r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1777                    r#"LIMIT $7"#,
1778                ]
1779                .join(" ")
1780                .as_str(),
1781                [
1782                    ('x' as i32).into(),
1783                    "y".into(),
1784                    ('z' as i64).into(),
1785                    ('x' as i32).into(),
1786                    "y".into(),
1787                    ('x' as i32).into(),
1788                    4_u64.into(),
1789                ]
1790            ),])]
1791        );
1792
1793        Ok(())
1794    }
1795
1796    mod composite_entity {
1797        use crate as sea_orm;
1798        use crate::entity::prelude::*;
1799
1800        #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1801        #[sea_orm(table_name = "t")]
1802        pub struct Model {
1803            #[sea_orm(primary_key)]
1804            pub col_1: String,
1805            #[sea_orm(primary_key)]
1806            pub col_2: String,
1807            #[sea_orm(primary_key)]
1808            pub col_3: String,
1809            #[sea_orm(primary_key)]
1810            pub col_4: String,
1811            #[sea_orm(primary_key)]
1812            pub col_5: String,
1813            #[sea_orm(primary_key)]
1814            pub col_6: String,
1815            #[sea_orm(primary_key)]
1816            pub col_7: String,
1817            #[sea_orm(primary_key)]
1818            pub col_8: String,
1819            #[sea_orm(primary_key)]
1820            pub col_9: String,
1821            #[sea_orm(primary_key)]
1822            pub col_10: String,
1823            #[sea_orm(primary_key)]
1824            pub col_11: String,
1825            #[sea_orm(primary_key)]
1826            pub col_12: String,
1827        }
1828
1829        #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1830        pub enum Relation {}
1831
1832        impl ActiveModelBehavior for ActiveModel {}
1833    }
1834
1835    #[smol_potat::test]
1836    async fn cursor_by_many() -> Result<(), DbErr> {
1837        use composite_entity::*;
1838
1839        let base_sql = [
1840            r#"SELECT "t"."col_1", "t"."col_2", "t"."col_3", "t"."col_4", "t"."col_5", "t"."col_6", "t"."col_7", "t"."col_8", "t"."col_9", "t"."col_10", "t"."col_11", "t"."col_12""#,
1841            r#"FROM "t" WHERE"#,
1842        ].join(" ");
1843
1844        assert_eq!(
1845            DbBackend::Postgres.build(&
1846                Entity::find()
1847                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4))
1848                .after(("val_1", "val_2", "val_3", "val_4")).apply_limit().apply_order_by().apply_filters().query
1849            ).to_string(),
1850            format!("{base_sql} {}", [
1851                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1852                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1853                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1854                r#"OR "t"."col_1" > 'val_1'"#,
1855                r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC"#,
1856            ].join(" "))
1857        );
1858
1859        assert_eq!(
1860            DbBackend::Postgres.build(&
1861                Entity::find()
1862                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5))
1863                .after(("val_1", "val_2", "val_3", "val_4", "val_5")).apply_limit().apply_order_by().apply_filters()
1864                .query
1865            ).to_string(),
1866            format!("{base_sql} {}", [
1867                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" > 'val_5')"#,
1868                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1869                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1870                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1871                r#"OR "t"."col_1" > 'val_1'"#,
1872                r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC"#,
1873            ].join(" "))
1874        );
1875
1876        assert_eq!(
1877            DbBackend::Postgres.build(&
1878                Entity::find()
1879                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6))
1880                .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")).apply_limit().apply_order_by().apply_filters()
1881                .query
1882            ).to_string(),
1883            format!("{base_sql} {}", [
1884                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" > 'val_6')"#,
1885                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" > 'val_5')"#,
1886                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1887                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1888                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1889                r#"OR "t"."col_1" > 'val_1'"#,
1890                r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC"#,
1891            ].join(" "))
1892        );
1893
1894        assert_eq!(
1895            DbBackend::Postgres.build(&
1896                Entity::find()
1897                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7))
1898                .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")).apply_limit().apply_order_by().apply_filters()
1899                .query
1900            ).to_string(),
1901            format!("{base_sql} {}", [
1902                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#,
1903                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#,
1904                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
1905                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
1906                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
1907                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
1908                r#"OR "t"."col_1" < 'val_1'"#,
1909                r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC, "t"."col_7" ASC"#,
1910            ].join(" "))
1911        );
1912
1913        assert_eq!(
1914            DbBackend::Postgres.build(&
1915                Entity::find()
1916                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8))
1917                .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8")).apply_limit().apply_order_by().apply_filters()
1918                .query
1919            ).to_string(),
1920            format!("{base_sql} {}", [
1921                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" < 'val_8')"#,
1922                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#,
1923                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#,
1924                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
1925                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
1926                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
1927                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
1928                r#"OR "t"."col_1" < 'val_1'"#,
1929                r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC, "t"."col_7" ASC, "t"."col_8" ASC"#,
1930            ].join(" "))
1931        );
1932
1933        assert_eq!(
1934            DbBackend::Postgres.build(&
1935                Entity::find()
1936                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8, Column::Col9))
1937                .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8", "val_9")).apply_limit().apply_order_by().apply_filters()
1938                .query
1939            ).to_string(),
1940            format!("{base_sql} {}", [
1941                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" = 'val_8' AND "t"."col_9" < 'val_9')"#,
1942                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" < 'val_8')"#,
1943                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#,
1944                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#,
1945                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
1946                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
1947                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
1948                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
1949                r#"OR "t"."col_1" < 'val_1'"#,
1950                r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC, "t"."col_5" ASC, "t"."col_6" ASC, "t"."col_7" ASC, "t"."col_8" ASC, "t"."col_9" ASC"#,
1951            ].join(" "))
1952        );
1953
1954        Ok(())
1955    }
1956
1957    #[smol_potat::test]
1958    async fn cursor_by_many_desc() -> Result<(), DbErr> {
1959        use composite_entity::*;
1960
1961        let base_sql = [
1962            r#"SELECT "t"."col_1", "t"."col_2", "t"."col_3", "t"."col_4", "t"."col_5", "t"."col_6", "t"."col_7", "t"."col_8", "t"."col_9", "t"."col_10", "t"."col_11", "t"."col_12""#,
1963            r#"FROM "t" WHERE"#,
1964        ].join(" ");
1965
1966        assert_eq!(
1967            DbBackend::Postgres.build(&
1968                Entity::find()
1969                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4))
1970                .before(("val_1", "val_2", "val_3", "val_4")).desc().apply_limit().apply_order_by().apply_filters().query
1971            ).to_string(),
1972            format!("{base_sql} {}", [
1973                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1974                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1975                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1976                r#"OR "t"."col_1" > 'val_1'"#,
1977                r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC"#,
1978            ].join(" "))
1979        );
1980
1981        assert_eq!(
1982            DbBackend::Postgres.build(&
1983                Entity::find()
1984                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5))
1985                .before(("val_1", "val_2", "val_3", "val_4", "val_5")).desc().apply_limit().apply_order_by().apply_filters()
1986                .query
1987            ).to_string(),
1988            format!("{base_sql} {}", [
1989                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" > 'val_5')"#,
1990                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1991                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1992                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1993                r#"OR "t"."col_1" > 'val_1'"#,
1994                r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC"#,
1995            ].join(" "))
1996        );
1997
1998        assert_eq!(
1999            DbBackend::Postgres.build(&
2000                Entity::find()
2001                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6))
2002                .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")).desc().apply_limit().apply_order_by().apply_filters()
2003                .query
2004            ).to_string(),
2005            format!("{base_sql} {}", [
2006                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" > 'val_6')"#,
2007                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" > 'val_5')"#,
2008                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
2009                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
2010                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
2011                r#"OR "t"."col_1" > 'val_1'"#,
2012                r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC"#,
2013            ].join(" "))
2014        );
2015
2016        assert_eq!(
2017            DbBackend::Postgres.build(&
2018                Entity::find()
2019                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7))
2020                .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")).desc().apply_limit().apply_order_by().apply_filters()
2021                .query
2022            ).to_string(),
2023            format!("{base_sql} {}", [
2024                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#,
2025                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#,
2026                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
2027                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
2028                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
2029                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
2030                r#"OR "t"."col_1" < 'val_1'"#,
2031                r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC, "t"."col_7" DESC"#,
2032            ].join(" "))
2033        );
2034
2035        assert_eq!(
2036            DbBackend::Postgres.build(&
2037                Entity::find()
2038                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8))
2039                .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8")).desc().apply_limit().apply_order_by().apply_filters()
2040                .query
2041            ).to_string(),
2042            format!("{base_sql} {}", [
2043                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" < 'val_8')"#,
2044                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#,
2045                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#,
2046                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
2047                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
2048                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
2049                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
2050                r#"OR "t"."col_1" < 'val_1'"#,
2051                r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC, "t"."col_7" DESC, "t"."col_8" DESC"#,
2052            ].join(" "))
2053        );
2054
2055        assert_eq!(
2056            DbBackend::Postgres.build(&
2057                Entity::find()
2058                .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8, Column::Col9))
2059                .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8", "val_9")).desc().apply_limit().apply_order_by().apply_filters()
2060                .query
2061            ).to_string(),
2062            format!("{base_sql} {}", [
2063                r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" = 'val_8' AND "t"."col_9" < 'val_9')"#,
2064                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" = 'val_7' AND "t"."col_8" < 'val_8')"#,
2065                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" = 'val_6' AND "t"."col_7" < 'val_7')"#,
2066                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" = 'val_5' AND "t"."col_6" < 'val_6')"#,
2067                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
2068                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
2069                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
2070                r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
2071                r#"OR "t"."col_1" < 'val_1'"#,
2072                r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC, "t"."col_5" DESC, "t"."col_6" DESC, "t"."col_7" DESC, "t"."col_8" DESC, "t"."col_9" DESC"#,
2073            ].join(" "))
2074        );
2075
2076        Ok(())
2077    }
2078
2079    mod test_base_entity {
2080        use crate as sea_orm;
2081        use crate::entity::prelude::*;
2082
2083        #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
2084        #[sea_orm(table_name = "base")]
2085        pub struct Model {
2086            #[sea_orm(primary_key)]
2087            pub id: i32,
2088            #[sea_orm(primary_key)]
2089            pub name: String,
2090        }
2091
2092        #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
2093        pub enum Relation {
2094            #[sea_orm(has_many = "super::test_related_entity::Entity")]
2095            TestRelatedEntity,
2096        }
2097
2098        impl Related<super::test_related_entity::Entity> for Entity {
2099            fn to() -> RelationDef {
2100                Relation::TestRelatedEntity.def()
2101            }
2102        }
2103
2104        impl ActiveModelBehavior for ActiveModel {}
2105    }
2106
2107    mod test_related_entity {
2108        use super::test_base_entity;
2109        use crate as sea_orm;
2110        use crate::entity::prelude::*;
2111
2112        #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
2113        #[sea_orm(table_name = "related")]
2114        pub struct Model {
2115            #[sea_orm(primary_key)]
2116            pub id: i32,
2117            #[sea_orm(primary_key)]
2118            pub name: String,
2119            pub test_id: i32,
2120        }
2121
2122        #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
2123        pub enum Relation {
2124            #[sea_orm(
2125                belongs_to = "test_base_entity::Entity",
2126                from = "Column::TestId",
2127                to = "super::test_base_entity::Column::Id"
2128            )]
2129            TestBaseEntity,
2130        }
2131
2132        impl Related<super::test_base_entity::Entity> for Entity {
2133            fn to() -> RelationDef {
2134                Relation::TestBaseEntity.def()
2135            }
2136        }
2137
2138        impl ActiveModelBehavior for ActiveModel {}
2139    }
2140
2141    #[smol_potat::test]
2142    async fn related_composite_keys_1() -> Result<(), DbErr> {
2143        let db = MockDatabase::new(DbBackend::Postgres)
2144            .append_query_results([[(
2145                test_base_entity::Model {
2146                    id: 1,
2147                    name: "CAT".into(),
2148                },
2149                test_related_entity::Model {
2150                    id: 1,
2151                    name: "CATE".into(),
2152                    test_id: 1,
2153                },
2154            )]])
2155            .into_connection();
2156
2157        assert!(!test_base_entity::Entity::find()
2158            .find_also_related(test_related_entity::Entity)
2159            .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2160            .first(1)
2161            .all(&db)
2162            .await?
2163            .is_empty());
2164
2165        assert_eq!(
2166            db.into_transaction_log(),
2167            [Transaction::many([Statement::from_sql_and_values(
2168                DbBackend::Postgres,
2169                [
2170                    r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2171                    r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2172                    r#"FROM "base""#,
2173                    r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2174                    r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $1"#,
2175                ]
2176                .join(" ")
2177                .as_str(),
2178                [1_u64.into()]
2179            ),])]
2180        );
2181
2182        Ok(())
2183    }
2184
2185    #[smol_potat::test]
2186    async fn related_composite_keys_1_desc() -> Result<(), DbErr> {
2187        let db = MockDatabase::new(DbBackend::Postgres)
2188            .append_query_results([[(
2189                test_base_entity::Model {
2190                    id: 1,
2191                    name: "CAT".into(),
2192                },
2193                test_related_entity::Model {
2194                    id: 1,
2195                    name: "CATE".into(),
2196                    test_id: 1,
2197                },
2198            )]])
2199            .into_connection();
2200
2201        assert!(!test_base_entity::Entity::find()
2202            .find_also_related(test_related_entity::Entity)
2203            .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2204            .last(1)
2205            .desc()
2206            .all(&db)
2207            .await?
2208            .is_empty());
2209
2210        assert_eq!(
2211            db.into_transaction_log(),
2212            [Transaction::many([Statement::from_sql_and_values(
2213                DbBackend::Postgres,
2214                [
2215                    r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2216                    r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2217                    r#"FROM "base""#,
2218                    r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2219                    r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $1"#,
2220                ]
2221                .join(" ")
2222                .as_str(),
2223                [1_u64.into()]
2224            ),])]
2225        );
2226
2227        Ok(())
2228    }
2229
2230    #[smol_potat::test]
2231    async fn related_composite_keys_2() -> Result<(), DbErr> {
2232        let db = MockDatabase::new(DbBackend::Postgres)
2233            .append_query_results([[(
2234                test_base_entity::Model {
2235                    id: 1,
2236                    name: "CAT".into(),
2237                },
2238                test_related_entity::Model {
2239                    id: 1,
2240                    name: "CATE".into(),
2241                    test_id: 1,
2242                },
2243            )]])
2244            .into_connection();
2245
2246        assert!(!test_base_entity::Entity::find()
2247            .find_also_related(test_related_entity::Entity)
2248            .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2249            .after((1, "C".to_string()))
2250            .first(2)
2251            .all(&db)
2252            .await?
2253            .is_empty());
2254
2255        assert_eq!(
2256            db.into_transaction_log(),
2257            [Transaction::many([Statement::from_sql_and_values(
2258                DbBackend::Postgres,
2259                [
2260                    r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2261                    r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2262                    r#"FROM "base""#,
2263                    r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2264                    r#"WHERE ("base"."id" = $1 AND "base"."name" > $2) OR "base"."id" > $3"#,
2265                    r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $4"#,
2266                ]
2267                .join(" ")
2268                .as_str(),
2269                [
2270                    1_i32.into(),
2271                    "C".into(),
2272                    1_i32.into(),
2273                    2_u64.into(),
2274                ]
2275            ),])]
2276        );
2277
2278        Ok(())
2279    }
2280
2281    #[smol_potat::test]
2282    async fn related_composite_keys_2_desc() -> Result<(), DbErr> {
2283        let db = MockDatabase::new(DbBackend::Postgres)
2284            .append_query_results([[(
2285                test_base_entity::Model {
2286                    id: 1,
2287                    name: "CAT".into(),
2288                },
2289                test_related_entity::Model {
2290                    id: 1,
2291                    name: "CATE".into(),
2292                    test_id: 1,
2293                },
2294            )]])
2295            .into_connection();
2296
2297        assert!(!test_base_entity::Entity::find()
2298            .find_also_related(test_related_entity::Entity)
2299            .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2300            .before((1, "C".to_string()))
2301            .last(2)
2302            .desc()
2303            .all(&db)
2304            .await?
2305            .is_empty());
2306
2307        assert_eq!(
2308            db.into_transaction_log(),
2309            [Transaction::many([Statement::from_sql_and_values(
2310                DbBackend::Postgres,
2311                [
2312                    r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2313                    r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2314                    r#"FROM "base""#,
2315                    r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2316                    r#"WHERE ("base"."id" = $1 AND "base"."name" > $2) OR "base"."id" > $3"#,
2317                    r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $4"#,
2318                ]
2319                .join(" ")
2320                .as_str(),
2321                [
2322                    1_i32.into(),
2323                    "C".into(),
2324                    1_i32.into(),
2325                    2_u64.into(),
2326                ]
2327            ),])]
2328        );
2329
2330        Ok(())
2331    }
2332
2333    #[smol_potat::test]
2334    async fn related_composite_keys_3() -> Result<(), DbErr> {
2335        let db = MockDatabase::new(DbBackend::Postgres)
2336            .append_query_results([[(
2337                test_base_entity::Model {
2338                    id: 1,
2339                    name: "CAT".into(),
2340                },
2341                test_related_entity::Model {
2342                    id: 1,
2343                    name: "CATE".into(),
2344                    test_id: 1,
2345                },
2346            )]])
2347            .into_connection();
2348
2349        assert!(!test_base_entity::Entity::find()
2350            .find_also_related(test_related_entity::Entity)
2351            .cursor_by_other((
2352                test_related_entity::Column::Id,
2353                test_related_entity::Column::Name
2354            ))
2355            .after((1, "CAT".to_string()))
2356            .first(2)
2357            .all(&db)
2358            .await?
2359            .is_empty());
2360
2361        assert_eq!(
2362            db.into_transaction_log(),
2363            [Transaction::many([Statement::from_sql_and_values(
2364                DbBackend::Postgres,
2365                [
2366                    r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2367                    r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2368                    r#"FROM "base""#,
2369                    r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2370                    r#"WHERE ("related"."id" = $1 AND "related"."name" > $2) OR "related"."id" > $3"#,
2371                    r#"ORDER BY "related"."id" ASC, "related"."name" ASC, "base"."id" ASC, "base"."name" ASC LIMIT $4"#,
2372                ]
2373                .join(" ")
2374                .as_str(),
2375                [
2376                    1_i32.into(),
2377                    "CAT".into(),
2378                    1_i32.into(),
2379                    2_u64.into(),
2380                ]
2381            ),])]
2382        );
2383
2384        Ok(())
2385    }
2386
2387    #[smol_potat::test]
2388    async fn related_composite_keys_3_desc() -> Result<(), DbErr> {
2389        let db = MockDatabase::new(DbBackend::Postgres)
2390            .append_query_results([[(
2391                test_base_entity::Model {
2392                    id: 1,
2393                    name: "CAT".into(),
2394                },
2395                test_related_entity::Model {
2396                    id: 1,
2397                    name: "CATE".into(),
2398                    test_id: 1,
2399                },
2400            )]])
2401            .into_connection();
2402
2403        assert!(!test_base_entity::Entity::find()
2404            .find_also_related(test_related_entity::Entity)
2405            .cursor_by_other((
2406                test_related_entity::Column::Id,
2407                test_related_entity::Column::Name
2408            ))
2409            .before((1, "CAT".to_string()))
2410            .last(2)
2411            .desc()
2412            .all(&db)
2413            .await?
2414            .is_empty());
2415
2416        assert_eq!(
2417            db.into_transaction_log(),
2418            [Transaction::many([Statement::from_sql_and_values(
2419                DbBackend::Postgres,
2420                [
2421                    r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2422                    r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2423                    r#"FROM "base""#,
2424                    r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2425                    r#"WHERE ("related"."id" = $1 AND "related"."name" > $2) OR "related"."id" > $3"#,
2426                    r#"ORDER BY "related"."id" ASC, "related"."name" ASC, "base"."id" ASC, "base"."name" ASC LIMIT $4"#,
2427                ]
2428                .join(" ")
2429                .as_str(),
2430                [
2431                    1_i32.into(),
2432                    "CAT".into(),
2433                    1_i32.into(),
2434                    2_u64.into(),
2435                ]
2436            ),])]
2437        );
2438
2439        Ok(())
2440    }
2441}