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