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