sea_orm/executor/
cursor.rs

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