sea_orm/executor/
cursor.rs

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