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