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