sea_orm/executor/
cursor.rs

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