1use crate::{
2 ConnectionTrait, DbErr, EntityTrait, FromQueryResult, Identity, IdentityOf, IntoIdentity,
3 PartialModelTrait, PrimaryKeyToColumn, QueryOrder, QuerySelect, Select, SelectModel,
4 SelectThree, SelectThreeModel, SelectTwo, 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#[derive(Debug, Clone)]
18pub struct Cursor<S>
19where
20 S: SelectorTrait,
21{
22 query: SelectStatement,
23 table: DynIden,
24 order_columns: Identity,
25 secondary_order_by: Vec<(DynIden, Identity)>,
26 first: Option<u64>,
27 last: Option<u64>,
28 before: Option<ValueTuple>,
29 after: Option<ValueTuple>,
30 sort_asc: bool,
31 is_result_reversed: bool,
32 phantom: PhantomData<S>,
33}
34
35impl<S> Cursor<S>
36where
37 S: SelectorTrait,
38{
39 pub 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 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 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 (1..=col_vec.len())
164 .rev()
165 .fold(Condition::any(), |cond_any, n| {
166 let inner_cond_all =
168 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 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 inner_cond_all.add(expr)
183 },
184 );
185 cond_any.add(inner_cond_all)
187 })
188 }
189 _ => panic!("column arity mismatch"),
190 }
191 }
192
193 pub fn asc(&mut self) -> &mut Self {
195 self.sort_asc = true;
196 self
197 }
198
199 pub fn desc(&mut self) -> &mut Self {
201 self.sort_asc = false;
202 self
203 }
204
205 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 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 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 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 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 #[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 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
373pub trait CursorTrait {
375 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 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, G, M, N, O> CursorTrait for SelectThree<E, F, G>
412where
413 E: EntityTrait<Model = M>,
414 F: EntityTrait<Model = N>,
415 G: EntityTrait<Model = O>,
416 M: FromQueryResult + Sized + Send + Sync,
417 N: FromQueryResult + Sized + Send + Sync,
418 O: FromQueryResult + Sized + Send + Sync,
419{
420 type Selector = SelectThreeModel<M, N, O>;
421}
422
423impl<E, F, M, N> SelectTwo<E, F>
424where
425 E: EntityTrait<Model = M>,
426 F: EntityTrait<Model = N>,
427 M: FromQueryResult + Sized + Send + Sync,
428 N: FromQueryResult + Sized + Send + Sync,
429{
430 pub fn cursor_by<C>(self, order_columns: C) -> Cursor<SelectTwoModel<M, N>>
432 where
433 C: IdentityOf<E>,
434 {
435 let primary_keys: Vec<(DynIden, Identity)> = <F::PrimaryKey as Iterable>::iter()
436 .map(|pk| {
437 (
438 SeaRc::new(F::default()),
439 Identity::Unary(SeaRc::new(pk.into_column())),
440 )
441 })
442 .collect();
443 let mut cursor = Cursor::new(
444 self.query,
445 SeaRc::new(E::default()),
446 order_columns.identity_of(),
447 );
448 cursor.set_secondary_order_by(primary_keys);
449 cursor
450 }
451
452 pub fn cursor_by_other<C>(self, order_columns: C) -> Cursor<SelectTwoModel<M, N>>
454 where
455 C: IdentityOf<F>,
456 {
457 let primary_keys: Vec<(DynIden, Identity)> = <E::PrimaryKey as Iterable>::iter()
458 .map(|pk| {
459 (
460 SeaRc::new(E::default()),
461 Identity::Unary(SeaRc::new(pk.into_column())),
462 )
463 })
464 .collect();
465 let mut cursor = Cursor::new(
466 self.query,
467 SeaRc::new(F::default()),
468 order_columns.identity_of(),
469 );
470 cursor.set_secondary_order_by(primary_keys);
471 cursor
472 }
473}
474
475impl<E, F, G, M, N, O> SelectThree<E, F, G>
476where
477 E: EntityTrait<Model = M>,
478 F: EntityTrait<Model = N>,
479 G: EntityTrait<Model = O>,
480 M: FromQueryResult + Sized + Send + Sync,
481 N: FromQueryResult + Sized + Send + Sync,
482 O: FromQueryResult + Sized + Send + Sync,
483{
484 pub fn cursor_by<C>(self, order_columns: C) -> Cursor<SelectThreeModel<M, N, O>>
486 where
487 C: IdentityOf<E>,
488 {
489 let mut cursor = Cursor::new(
490 self.query,
491 SeaRc::new(E::default()),
492 order_columns.identity_of(),
493 );
494 {
495 let primary_keys: Vec<(DynIden, Identity)> = <F::PrimaryKey as Iterable>::iter()
496 .map(|pk| {
497 (
498 SeaRc::new(F::default()),
499 Identity::Unary(SeaRc::new(pk.into_column())),
500 )
501 })
502 .collect();
503 cursor.set_secondary_order_by(primary_keys);
504 }
505 {
506 let primary_keys: Vec<(DynIden, Identity)> = <G::PrimaryKey as Iterable>::iter()
507 .map(|pk| {
508 (
509 SeaRc::new(G::default()),
510 Identity::Unary(SeaRc::new(pk.into_column())),
511 )
512 })
513 .collect();
514 cursor.set_secondary_order_by(primary_keys);
515 }
516 cursor
517 }
518}
519
520#[cfg(test)]
521#[cfg(feature = "mock")]
522mod tests {
523 use crate::entity::prelude::*;
524 use crate::tests_cfg::*;
525 use crate::{DbBackend, MockDatabase, Statement, Transaction};
526 use pretty_assertions::assert_eq;
527
528 #[smol_potat::test]
529 async fn first_2_before_10() -> Result<(), DbErr> {
530 use fruit::*;
531
532 let models = [
533 Model {
534 id: 1,
535 name: "Blueberry".into(),
536 cake_id: Some(1),
537 },
538 Model {
539 id: 2,
540 name: "Raspberry".into(),
541 cake_id: Some(1),
542 },
543 ];
544
545 let db = MockDatabase::new(DbBackend::Postgres)
546 .append_query_results([models.clone()])
547 .into_connection();
548
549 assert_eq!(
550 Entity::find()
551 .cursor_by(Column::Id)
552 .before(10)
553 .first(2)
554 .all(&db)
555 .await?,
556 models
557 );
558
559 assert_eq!(
560 db.into_transaction_log(),
561 [Transaction::many([Statement::from_sql_and_values(
562 DbBackend::Postgres,
563 [
564 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
565 r#"FROM "fruit""#,
566 r#"WHERE "fruit"."id" < $1"#,
567 r#"ORDER BY "fruit"."id" ASC"#,
568 r#"LIMIT $2"#,
569 ]
570 .join(" ")
571 .as_str(),
572 [10_i32.into(), 2_u64.into()]
573 ),])]
574 );
575
576 Ok(())
577 }
578
579 #[smol_potat::test]
580 async fn last_2_after_10_desc() -> Result<(), DbErr> {
581 use fruit::*;
582
583 let mut models = [
584 Model {
585 id: 1,
586 name: "Blueberry".into(),
587 cake_id: Some(1),
588 },
589 Model {
590 id: 2,
591 name: "Raspberry".into(),
592 cake_id: Some(1),
593 },
594 ];
595
596 let db = MockDatabase::new(DbBackend::Postgres)
597 .append_query_results([models.clone()])
598 .into_connection();
599
600 models.reverse();
601
602 assert_eq!(
603 Entity::find()
604 .cursor_by(Column::Id)
605 .after(10)
606 .last(2)
607 .desc()
608 .all(&db)
609 .await?,
610 models
611 );
612
613 assert_eq!(
614 db.into_transaction_log(),
615 [Transaction::many([Statement::from_sql_and_values(
616 DbBackend::Postgres,
617 [
618 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
619 r#"FROM "fruit""#,
620 r#"WHERE "fruit"."id" < $1"#,
621 r#"ORDER BY "fruit"."id" ASC"#,
622 r#"LIMIT $2"#,
623 ]
624 .join(" ")
625 .as_str(),
626 [10_i32.into(), 2_u64.into()]
627 ),])]
628 );
629
630 Ok(())
631 }
632
633 #[smol_potat::test]
634 async fn first_2_before_10_also_related_select() -> Result<(), DbErr> {
635 let models = [
636 (
637 cake::Model {
638 id: 1,
639 name: "Blueberry Cheese Cake".into(),
640 },
641 Some(fruit::Model {
642 id: 9,
643 name: "Blueberry".into(),
644 cake_id: Some(1),
645 }),
646 ),
647 (
648 cake::Model {
649 id: 2,
650 name: "Raspberry Cheese Cake".into(),
651 },
652 Some(fruit::Model {
653 id: 10,
654 name: "Raspberry".into(),
655 cake_id: Some(1),
656 }),
657 ),
658 ];
659
660 let db = MockDatabase::new(DbBackend::Postgres)
661 .append_query_results([models.clone()])
662 .into_connection();
663
664 assert_eq!(
665 cake::Entity::find()
666 .find_also_related(Fruit)
667 .cursor_by(cake::Column::Id)
668 .before(10)
669 .first(2)
670 .all(&db)
671 .await?,
672 models
673 );
674
675 assert_eq!(
676 db.into_transaction_log(),
677 [Transaction::many([Statement::from_sql_and_values(
678 DbBackend::Postgres,
679 [
680 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
681 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
682 r#"FROM "cake""#,
683 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
684 r#"WHERE "cake"."id" < $1"#,
685 r#"ORDER BY "cake"."id" ASC, "fruit"."id" ASC LIMIT $2"#,
686 ]
687 .join(" ")
688 .as_str(),
689 [10_i32.into(), 2_u64.into()]
690 ),])]
691 );
692
693 Ok(())
694 }
695
696 #[smol_potat::test]
697 async fn last_2_after_10_also_related_select_desc() -> Result<(), DbErr> {
698 let mut models = [
699 (
700 cake::Model {
701 id: 2,
702 name: "Raspberry Cheese Cake".into(),
703 },
704 Some(fruit::Model {
705 id: 10,
706 name: "Raspberry".into(),
707 cake_id: Some(1),
708 }),
709 ),
710 (
711 cake::Model {
712 id: 1,
713 name: "Blueberry Cheese Cake".into(),
714 },
715 Some(fruit::Model {
716 id: 9,
717 name: "Blueberry".into(),
718 cake_id: Some(1),
719 }),
720 ),
721 ];
722
723 let db = MockDatabase::new(DbBackend::Postgres)
724 .append_query_results([models.clone()])
725 .into_connection();
726
727 models.reverse();
728
729 assert_eq!(
730 cake::Entity::find()
731 .find_also_related(Fruit)
732 .cursor_by(cake::Column::Id)
733 .after(10)
734 .last(2)
735 .desc()
736 .all(&db)
737 .await?,
738 models
739 );
740
741 assert_eq!(
742 db.into_transaction_log(),
743 [Transaction::many([Statement::from_sql_and_values(
744 DbBackend::Postgres,
745 [
746 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
747 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
748 r#"FROM "cake""#,
749 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
750 r#"WHERE "cake"."id" < $1"#,
751 r#"ORDER BY "cake"."id" ASC, "fruit"."id" ASC LIMIT $2"#,
752 ]
753 .join(" ")
754 .as_str(),
755 [10_i32.into(), 2_u64.into()]
756 ),])]
757 );
758
759 Ok(())
760 }
761
762 #[smol_potat::test]
763 async fn first_2_before_10_also_related_select_cursor_other() -> Result<(), DbErr> {
764 let models = [(
765 cake::Model {
766 id: 1,
767 name: "Blueberry Cheese Cake".into(),
768 },
769 Some(fruit::Model {
770 id: 9,
771 name: "Blueberry".into(),
772 cake_id: Some(1),
773 }),
774 )];
775
776 let db = MockDatabase::new(DbBackend::Postgres)
777 .append_query_results([models.clone()])
778 .into_connection();
779
780 assert_eq!(
781 cake::Entity::find()
782 .find_also_related(Fruit)
783 .cursor_by_other(fruit::Column::Id)
784 .before(10)
785 .first(2)
786 .all(&db)
787 .await?,
788 models
789 );
790
791 assert_eq!(
792 db.into_transaction_log(),
793 [Transaction::many([Statement::from_sql_and_values(
794 DbBackend::Postgres,
795 [
796 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
797 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
798 r#"FROM "cake""#,
799 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
800 r#"WHERE "fruit"."id" < $1"#,
801 r#"ORDER BY "fruit"."id" ASC, "cake"."id" ASC LIMIT $2"#,
802 ]
803 .join(" ")
804 .as_str(),
805 [10_i32.into(), 2_u64.into()]
806 ),])]
807 );
808
809 Ok(())
810 }
811
812 #[smol_potat::test]
813 async fn last_2_after_10_also_related_select_cursor_other_desc() -> Result<(), DbErr> {
814 let models = [(
815 cake::Model {
816 id: 1,
817 name: "Blueberry Cheese Cake".into(),
818 },
819 Some(fruit::Model {
820 id: 9,
821 name: "Blueberry".into(),
822 cake_id: Some(1),
823 }),
824 )];
825
826 let db = MockDatabase::new(DbBackend::Postgres)
827 .append_query_results([models.clone()])
828 .into_connection();
829
830 assert_eq!(
831 cake::Entity::find()
832 .find_also_related(Fruit)
833 .cursor_by_other(fruit::Column::Id)
834 .after(10)
835 .last(2)
836 .desc()
837 .all(&db)
838 .await?,
839 models
840 );
841
842 assert_eq!(
843 db.into_transaction_log(),
844 [Transaction::many([Statement::from_sql_and_values(
845 DbBackend::Postgres,
846 [
847 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
848 r#""fruit"."id" AS "B_id", "fruit"."name" AS "B_name", "fruit"."cake_id" AS "B_cake_id""#,
849 r#"FROM "cake""#,
850 r#"LEFT JOIN "fruit" ON "cake"."id" = "fruit"."cake_id""#,
851 r#"WHERE "fruit"."id" < $1"#,
852 r#"ORDER BY "fruit"."id" ASC, "cake"."id" ASC LIMIT $2"#,
853 ]
854 .join(" ")
855 .as_str(),
856 [10_i32.into(), 2_u64.into()]
857 ),])]
858 );
859
860 Ok(())
861 }
862
863 #[smol_potat::test]
864 async fn first_2_before_10_also_linked_select() -> Result<(), DbErr> {
865 let models = [
866 (
867 cake::Model {
868 id: 1,
869 name: "Blueberry Cheese Cake".into(),
870 },
871 Some(vendor::Model {
872 id: 9,
873 name: "Blueberry".into(),
874 }),
875 ),
876 (
877 cake::Model {
878 id: 2,
879 name: "Raspberry Cheese Cake".into(),
880 },
881 Some(vendor::Model {
882 id: 10,
883 name: "Raspberry".into(),
884 }),
885 ),
886 ];
887
888 let db = MockDatabase::new(DbBackend::Postgres)
889 .append_query_results([models.clone()])
890 .into_connection();
891
892 assert_eq!(
893 cake::Entity::find()
894 .find_also_linked(entity_linked::CakeToFillingVendor)
895 .cursor_by(cake::Column::Id)
896 .before(10)
897 .first(2)
898 .all(&db)
899 .await?,
900 models
901 );
902
903 assert_eq!(
904 db.into_transaction_log(),
905 [Transaction::many([Statement::from_sql_and_values(
906 DbBackend::Postgres,
907 [
908 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
909 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
910 r#"FROM "cake""#,
911 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
912 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
913 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
914 r#"WHERE "cake"."id" < $1 ORDER BY "cake"."id" ASC, "vendor"."id" ASC LIMIT $2"#,
915 ]
916 .join(" ")
917 .as_str(),
918 [10_i32.into(), 2_u64.into()]
919 ),])]
920 );
921
922 Ok(())
923 }
924
925 #[smol_potat::test]
926 async fn last_2_after_10_also_linked_select_desc() -> Result<(), DbErr> {
927 let mut models = [
928 (
929 cake::Model {
930 id: 2,
931 name: "Raspberry Cheese Cake".into(),
932 },
933 Some(vendor::Model {
934 id: 10,
935 name: "Raspberry".into(),
936 }),
937 ),
938 (
939 cake::Model {
940 id: 1,
941 name: "Blueberry Cheese Cake".into(),
942 },
943 Some(vendor::Model {
944 id: 9,
945 name: "Blueberry".into(),
946 }),
947 ),
948 ];
949
950 let db = MockDatabase::new(DbBackend::Postgres)
951 .append_query_results([models.clone()])
952 .into_connection();
953
954 models.reverse();
955
956 assert_eq!(
957 cake::Entity::find()
958 .find_also_linked(entity_linked::CakeToFillingVendor)
959 .cursor_by(cake::Column::Id)
960 .after(10)
961 .last(2)
962 .desc()
963 .all(&db)
964 .await?,
965 models
966 );
967
968 assert_eq!(
969 db.into_transaction_log(),
970 [Transaction::many([Statement::from_sql_and_values(
971 DbBackend::Postgres,
972 [
973 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
974 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
975 r#"FROM "cake""#,
976 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
977 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
978 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
979 r#"WHERE "cake"."id" < $1 ORDER BY "cake"."id" ASC, "vendor"."id" ASC LIMIT $2"#,
980 ]
981 .join(" ")
982 .as_str(),
983 [10_i32.into(), 2_u64.into()]
984 ),])]
985 );
986
987 Ok(())
988 }
989
990 #[smol_potat::test]
991 async fn first_2_before_10_also_linked_select_cursor_other() -> Result<(), DbErr> {
992 let models = [(
993 cake::Model {
994 id: 1,
995 name: "Blueberry Cheese Cake".into(),
996 },
997 Some(vendor::Model {
998 id: 9,
999 name: "Blueberry".into(),
1000 }),
1001 )];
1002
1003 let db = MockDatabase::new(DbBackend::Postgres)
1004 .append_query_results([models.clone()])
1005 .into_connection();
1006
1007 assert_eq!(
1008 cake::Entity::find()
1009 .find_also_linked(entity_linked::CakeToFillingVendor)
1010 .cursor_by_other(vendor::Column::Id)
1011 .before(10)
1012 .first(2)
1013 .all(&db)
1014 .await?,
1015 models
1016 );
1017
1018 assert_eq!(
1019 db.into_transaction_log(),
1020 [Transaction::many([Statement::from_sql_and_values(
1021 DbBackend::Postgres,
1022 [
1023 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1024 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
1025 r#"FROM "cake""#,
1026 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1027 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1028 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1029 r#"WHERE "vendor"."id" < $1 ORDER BY "vendor"."id" ASC, "cake"."id" ASC LIMIT $2"#,
1030 ]
1031 .join(" ")
1032 .as_str(),
1033 [10_i32.into(), 2_u64.into()]
1034 ),])]
1035 );
1036
1037 Ok(())
1038 }
1039
1040 #[smol_potat::test]
1041 async fn last_2_after_10_also_linked_select_cursor_other_desc() -> Result<(), DbErr> {
1042 let mut models = [(
1043 cake::Model {
1044 id: 1,
1045 name: "Blueberry Cheese Cake".into(),
1046 },
1047 Some(vendor::Model {
1048 id: 9,
1049 name: "Blueberry".into(),
1050 }),
1051 )];
1052
1053 let db = MockDatabase::new(DbBackend::Postgres)
1054 .append_query_results([models.clone()])
1055 .into_connection();
1056
1057 models.reverse();
1058
1059 assert_eq!(
1060 cake::Entity::find()
1061 .find_also_linked(entity_linked::CakeToFillingVendor)
1062 .cursor_by_other(vendor::Column::Id)
1063 .after(10)
1064 .last(2)
1065 .desc()
1066 .all(&db)
1067 .await?,
1068 models
1069 );
1070
1071 assert_eq!(
1072 db.into_transaction_log(),
1073 [Transaction::many([Statement::from_sql_and_values(
1074 DbBackend::Postgres,
1075 [
1076 r#"SELECT "cake"."id" AS "A_id", "cake"."name" AS "A_name","#,
1077 r#""r2"."id" AS "B_id", "r2"."name" AS "B_name""#,
1078 r#"FROM "cake""#,
1079 r#"LEFT JOIN "cake_filling" AS "r0" ON "cake"."id" = "r0"."cake_id""#,
1080 r#"LEFT JOIN "filling" AS "r1" ON "r0"."filling_id" = "r1"."id""#,
1081 r#"LEFT JOIN "vendor" AS "r2" ON "r1"."vendor_id" = "r2"."id""#,
1082 r#"WHERE "vendor"."id" < $1 ORDER BY "vendor"."id" ASC, "cake"."id" ASC LIMIT $2"#,
1083 ]
1084 .join(" ")
1085 .as_str(),
1086 [10_i32.into(), 2_u64.into()]
1087 ),])]
1088 );
1089
1090 Ok(())
1091 }
1092
1093 #[smol_potat::test]
1094 async fn last_2_after_10() -> Result<(), DbErr> {
1095 use fruit::*;
1096
1097 let db = MockDatabase::new(DbBackend::Postgres)
1098 .append_query_results([[
1099 Model {
1100 id: 22,
1101 name: "Raspberry".into(),
1102 cake_id: Some(1),
1103 },
1104 Model {
1105 id: 21,
1106 name: "Blueberry".into(),
1107 cake_id: Some(1),
1108 },
1109 ]])
1110 .into_connection();
1111
1112 assert_eq!(
1113 Entity::find()
1114 .cursor_by(Column::Id)
1115 .after(10)
1116 .last(2)
1117 .all(&db)
1118 .await?,
1119 [
1120 Model {
1121 id: 21,
1122 name: "Blueberry".into(),
1123 cake_id: Some(1),
1124 },
1125 Model {
1126 id: 22,
1127 name: "Raspberry".into(),
1128 cake_id: Some(1),
1129 },
1130 ]
1131 );
1132
1133 assert_eq!(
1134 db.into_transaction_log(),
1135 [Transaction::many([Statement::from_sql_and_values(
1136 DbBackend::Postgres,
1137 [
1138 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1139 r#"FROM "fruit""#,
1140 r#"WHERE "fruit"."id" > $1"#,
1141 r#"ORDER BY "fruit"."id" DESC"#,
1142 r#"LIMIT $2"#,
1143 ]
1144 .join(" ")
1145 .as_str(),
1146 [10_i32.into(), 2_u64.into()]
1147 ),])]
1148 );
1149
1150 Ok(())
1151 }
1152
1153 #[smol_potat::test]
1154 async fn first_2_before_10_desc() -> Result<(), DbErr> {
1155 use fruit::*;
1156
1157 let models = [
1158 Model {
1159 id: 22,
1160 name: "Raspberry".into(),
1161 cake_id: Some(1),
1162 },
1163 Model {
1164 id: 21,
1165 name: "Blueberry".into(),
1166 cake_id: Some(1),
1167 },
1168 ];
1169
1170 let db = MockDatabase::new(DbBackend::Postgres)
1171 .append_query_results([models.clone()])
1172 .into_connection();
1173
1174 assert_eq!(
1175 Entity::find()
1176 .cursor_by(Column::Id)
1177 .before(10)
1178 .first(2)
1179 .desc()
1180 .all(&db)
1181 .await?,
1182 models
1183 );
1184
1185 assert_eq!(
1186 db.into_transaction_log(),
1187 [Transaction::many([Statement::from_sql_and_values(
1188 DbBackend::Postgres,
1189 [
1190 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1191 r#"FROM "fruit""#,
1192 r#"WHERE "fruit"."id" > $1"#,
1193 r#"ORDER BY "fruit"."id" DESC"#,
1194 r#"LIMIT $2"#,
1195 ]
1196 .join(" ")
1197 .as_str(),
1198 [10_i32.into(), 2_u64.into()]
1199 ),])]
1200 );
1201
1202 Ok(())
1203 }
1204
1205 #[smol_potat::test]
1206 async fn last_2_after_25_before_30() -> Result<(), DbErr> {
1207 use fruit::*;
1208
1209 let db = MockDatabase::new(DbBackend::Postgres)
1210 .append_query_results([[
1211 Model {
1212 id: 27,
1213 name: "Raspberry".into(),
1214 cake_id: Some(1),
1215 },
1216 Model {
1217 id: 26,
1218 name: "Blueberry".into(),
1219 cake_id: Some(1),
1220 },
1221 ]])
1222 .into_connection();
1223
1224 assert_eq!(
1225 Entity::find()
1226 .cursor_by(Column::Id)
1227 .after(25)
1228 .before(30)
1229 .last(2)
1230 .all(&db)
1231 .await?,
1232 [
1233 Model {
1234 id: 26,
1235 name: "Blueberry".into(),
1236 cake_id: Some(1),
1237 },
1238 Model {
1239 id: 27,
1240 name: "Raspberry".into(),
1241 cake_id: Some(1),
1242 },
1243 ]
1244 );
1245
1246 assert_eq!(
1247 db.into_transaction_log(),
1248 [Transaction::many([Statement::from_sql_and_values(
1249 DbBackend::Postgres,
1250 [
1251 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1252 r#"FROM "fruit""#,
1253 r#"WHERE "fruit"."id" > $1"#,
1254 r#"AND "fruit"."id" < $2"#,
1255 r#"ORDER BY "fruit"."id" DESC"#,
1256 r#"LIMIT $3"#,
1257 ]
1258 .join(" ")
1259 .as_str(),
1260 [25_i32.into(), 30_i32.into(), 2_u64.into()]
1261 ),])]
1262 );
1263
1264 Ok(())
1265 }
1266
1267 #[smol_potat::test]
1268 async fn first_2_after_30_before_25_desc() -> Result<(), DbErr> {
1269 use fruit::*;
1270
1271 let models = [
1272 Model {
1273 id: 27,
1274 name: "Raspberry".into(),
1275 cake_id: Some(1),
1276 },
1277 Model {
1278 id: 26,
1279 name: "Blueberry".into(),
1280 cake_id: Some(1),
1281 },
1282 ];
1283
1284 let db = MockDatabase::new(DbBackend::Postgres)
1285 .append_query_results([models.clone()])
1286 .into_connection();
1287
1288 assert_eq!(
1289 Entity::find()
1290 .cursor_by(Column::Id)
1291 .after(30)
1292 .before(25)
1293 .first(2)
1294 .desc()
1295 .all(&db)
1296 .await?,
1297 models
1298 );
1299
1300 assert_eq!(
1301 db.into_transaction_log(),
1302 [Transaction::many([Statement::from_sql_and_values(
1303 DbBackend::Postgres,
1304 [
1305 r#"SELECT "fruit"."id", "fruit"."name", "fruit"."cake_id""#,
1306 r#"FROM "fruit""#,
1307 r#"WHERE "fruit"."id" < $1"#,
1308 r#"AND "fruit"."id" > $2"#,
1309 r#"ORDER BY "fruit"."id" DESC"#,
1310 r#"LIMIT $3"#,
1311 ]
1312 .join(" ")
1313 .as_str(),
1314 [30_i32.into(), 25_i32.into(), 2_u64.into()]
1315 ),])]
1316 );
1317
1318 Ok(())
1319 }
1320
1321 mod test_entity {
1322 use crate as sea_orm;
1323 use crate::entity::prelude::*;
1324
1325 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1326 #[sea_orm(table_name = "example")]
1327 pub struct Model {
1328 #[sea_orm(primary_key)]
1329 pub id: i32,
1330 #[sea_orm(primary_key)]
1331 pub category: String,
1332 }
1333
1334 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1335 pub enum Relation {}
1336
1337 impl ActiveModelBehavior for ActiveModel {}
1338 }
1339
1340 mod xyz_entity {
1341 use crate as sea_orm;
1342 use crate::entity::prelude::*;
1343
1344 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1345 #[sea_orm(table_name = "m")]
1346 pub struct Model {
1347 #[sea_orm(primary_key)]
1348 pub x: i32,
1349 #[sea_orm(primary_key)]
1350 pub y: String,
1351 #[sea_orm(primary_key)]
1352 pub z: i64,
1353 }
1354
1355 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1356 pub enum Relation {}
1357
1358 impl ActiveModelBehavior for ActiveModel {}
1359 }
1360
1361 #[smol_potat::test]
1362 async fn composite_keys_1() -> Result<(), DbErr> {
1363 use test_entity::*;
1364
1365 let db = MockDatabase::new(DbBackend::Postgres)
1366 .append_query_results([[Model {
1367 id: 1,
1368 category: "CAT".into(),
1369 }]])
1370 .into_connection();
1371
1372 assert!(!Entity::find()
1373 .cursor_by((Column::Category, Column::Id))
1374 .first(3)
1375 .all(&db)
1376 .await?
1377 .is_empty());
1378
1379 assert_eq!(
1380 db.into_transaction_log(),
1381 [Transaction::many([Statement::from_sql_and_values(
1382 DbBackend::Postgres,
1383 [
1384 r#"SELECT "example"."id", "example"."category""#,
1385 r#"FROM "example""#,
1386 r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1387 r#"LIMIT $1"#,
1388 ]
1389 .join(" ")
1390 .as_str(),
1391 [3_u64.into()]
1392 ),])]
1393 );
1394
1395 Ok(())
1396 }
1397
1398 #[smol_potat::test]
1399 async fn composite_keys_1_desc() -> Result<(), DbErr> {
1400 use test_entity::*;
1401
1402 let db = MockDatabase::new(DbBackend::Postgres)
1403 .append_query_results([[Model {
1404 id: 1,
1405 category: "CAT".into(),
1406 }]])
1407 .into_connection();
1408
1409 assert!(!Entity::find()
1410 .cursor_by((Column::Category, Column::Id))
1411 .last(3)
1412 .desc()
1413 .all(&db)
1414 .await?
1415 .is_empty());
1416
1417 assert_eq!(
1418 db.into_transaction_log(),
1419 [Transaction::many([Statement::from_sql_and_values(
1420 DbBackend::Postgres,
1421 [
1422 r#"SELECT "example"."id", "example"."category""#,
1423 r#"FROM "example""#,
1424 r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1425 r#"LIMIT $1"#,
1426 ]
1427 .join(" ")
1428 .as_str(),
1429 [3_u64.into()]
1430 ),])]
1431 );
1432
1433 Ok(())
1434 }
1435
1436 #[smol_potat::test]
1437 async fn composite_keys_2() -> Result<(), DbErr> {
1438 use test_entity::*;
1439
1440 let db = MockDatabase::new(DbBackend::Postgres)
1441 .append_query_results([[Model {
1442 id: 1,
1443 category: "CAT".into(),
1444 }]])
1445 .into_connection();
1446
1447 assert!(!Entity::find()
1448 .cursor_by((Column::Category, Column::Id))
1449 .after(("A".to_owned(), 2))
1450 .first(3)
1451 .all(&db)
1452 .await?
1453 .is_empty());
1454
1455 assert_eq!(
1456 db.into_transaction_log(),
1457 [Transaction::many([Statement::from_sql_and_values(
1458 DbBackend::Postgres,
1459 [
1460 r#"SELECT "example"."id", "example"."category""#,
1461 r#"FROM "example""#,
1462 r#"WHERE ("example"."category" = $1 AND "example"."id" > $2)"#,
1463 r#"OR "example"."category" > $3"#,
1464 r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1465 r#"LIMIT $4"#,
1466 ]
1467 .join(" ")
1468 .as_str(),
1469 [
1470 "A".to_string().into(),
1471 2i32.into(),
1472 "A".to_string().into(),
1473 3_u64.into(),
1474 ]
1475 )])]
1476 );
1477
1478 Ok(())
1479 }
1480
1481 #[smol_potat::test]
1482 async fn composite_keys_2_desc() -> Result<(), DbErr> {
1483 use test_entity::*;
1484
1485 let db = MockDatabase::new(DbBackend::Postgres)
1486 .append_query_results([[Model {
1487 id: 1,
1488 category: "CAT".into(),
1489 }]])
1490 .into_connection();
1491
1492 assert!(!Entity::find()
1493 .cursor_by((Column::Category, Column::Id))
1494 .before(("A".to_owned(), 2))
1495 .last(3)
1496 .desc()
1497 .all(&db)
1498 .await?
1499 .is_empty());
1500
1501 assert_eq!(
1502 db.into_transaction_log(),
1503 [Transaction::many([Statement::from_sql_and_values(
1504 DbBackend::Postgres,
1505 [
1506 r#"SELECT "example"."id", "example"."category""#,
1507 r#"FROM "example""#,
1508 r#"WHERE ("example"."category" = $1 AND "example"."id" > $2)"#,
1509 r#"OR "example"."category" > $3"#,
1510 r#"ORDER BY "example"."category" ASC, "example"."id" ASC"#,
1511 r#"LIMIT $4"#,
1512 ]
1513 .join(" ")
1514 .as_str(),
1515 [
1516 "A".to_string().into(),
1517 2i32.into(),
1518 "A".to_string().into(),
1519 3_u64.into(),
1520 ]
1521 )])]
1522 );
1523
1524 Ok(())
1525 }
1526
1527 #[smol_potat::test]
1528 async fn composite_keys_3() -> Result<(), DbErr> {
1529 use test_entity::*;
1530
1531 let db = MockDatabase::new(DbBackend::Postgres)
1532 .append_query_results([[Model {
1533 id: 1,
1534 category: "CAT".into(),
1535 }]])
1536 .into_connection();
1537
1538 assert!(!Entity::find()
1539 .cursor_by((Column::Category, Column::Id))
1540 .before(("A".to_owned(), 2))
1541 .last(3)
1542 .all(&db)
1543 .await?
1544 .is_empty());
1545
1546 assert_eq!(
1547 db.into_transaction_log(),
1548 [Transaction::many([Statement::from_sql_and_values(
1549 DbBackend::Postgres,
1550 [
1551 r#"SELECT "example"."id", "example"."category""#,
1552 r#"FROM "example""#,
1553 r#"WHERE ("example"."category" = $1 AND "example"."id" < $2)"#,
1554 r#"OR "example"."category" < $3"#,
1555 r#"ORDER BY "example"."category" DESC, "example"."id" DESC"#,
1556 r#"LIMIT $4"#,
1557 ]
1558 .join(" ")
1559 .as_str(),
1560 [
1561 "A".to_string().into(),
1562 2i32.into(),
1563 "A".to_string().into(),
1564 3_u64.into(),
1565 ]
1566 )])]
1567 );
1568
1569 Ok(())
1570 }
1571
1572 #[smol_potat::test]
1573 async fn composite_keys_3_desc() -> Result<(), DbErr> {
1574 use test_entity::*;
1575
1576 let db = MockDatabase::new(DbBackend::Postgres)
1577 .append_query_results([[Model {
1578 id: 1,
1579 category: "CAT".into(),
1580 }]])
1581 .into_connection();
1582
1583 assert!(!Entity::find()
1584 .cursor_by((Column::Category, Column::Id))
1585 .after(("A".to_owned(), 2))
1586 .first(3)
1587 .desc()
1588 .all(&db)
1589 .await?
1590 .is_empty());
1591
1592 assert_eq!(
1593 db.into_transaction_log(),
1594 [Transaction::many([Statement::from_sql_and_values(
1595 DbBackend::Postgres,
1596 [
1597 r#"SELECT "example"."id", "example"."category""#,
1598 r#"FROM "example""#,
1599 r#"WHERE ("example"."category" = $1 AND "example"."id" < $2)"#,
1600 r#"OR "example"."category" < $3"#,
1601 r#"ORDER BY "example"."category" DESC, "example"."id" DESC"#,
1602 r#"LIMIT $4"#,
1603 ]
1604 .join(" ")
1605 .as_str(),
1606 [
1607 "A".to_string().into(),
1608 2i32.into(),
1609 "A".to_string().into(),
1610 3_u64.into(),
1611 ]
1612 )])]
1613 );
1614
1615 Ok(())
1616 }
1617
1618 #[smol_potat::test]
1619 async fn composite_keys_4() -> Result<(), DbErr> {
1620 use xyz_entity::*;
1621
1622 let db = MockDatabase::new(DbBackend::Postgres)
1623 .append_query_results([[Model {
1624 x: 'x' as i32,
1625 y: "y".into(),
1626 z: 'z' as i64,
1627 }]])
1628 .into_connection();
1629
1630 assert!(!Entity::find()
1631 .cursor_by((Column::X, Column::Y, Column::Z))
1632 .first(4)
1633 .all(&db)
1634 .await?
1635 .is_empty());
1636
1637 assert_eq!(
1638 db.into_transaction_log(),
1639 [Transaction::many([Statement::from_sql_and_values(
1640 DbBackend::Postgres,
1641 [
1642 r#"SELECT "m"."x", "m"."y", "m"."z""#,
1643 r#"FROM "m""#,
1644 r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1645 r#"LIMIT $1"#,
1646 ]
1647 .join(" ")
1648 .as_str(),
1649 [4_u64.into()]
1650 ),])]
1651 );
1652
1653 Ok(())
1654 }
1655
1656 #[smol_potat::test]
1657 async fn composite_keys_4_desc() -> 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!(!Entity::find()
1669 .cursor_by((Column::X, Column::Y, Column::Z))
1670 .last(4)
1671 .desc()
1672 .all(&db)
1673 .await?
1674 .is_empty());
1675
1676 assert_eq!(
1677 db.into_transaction_log(),
1678 [Transaction::many([Statement::from_sql_and_values(
1679 DbBackend::Postgres,
1680 [
1681 r#"SELECT "m"."x", "m"."y", "m"."z""#,
1682 r#"FROM "m""#,
1683 r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1684 r#"LIMIT $1"#,
1685 ]
1686 .join(" ")
1687 .as_str(),
1688 [4_u64.into()]
1689 ),])]
1690 );
1691
1692 Ok(())
1693 }
1694
1695 #[smol_potat::test]
1696 async fn composite_keys_5() -> Result<(), DbErr> {
1697 use xyz_entity::*;
1698
1699 let db = MockDatabase::new(DbBackend::Postgres)
1700 .append_query_results([[Model {
1701 x: 'x' as i32,
1702 y: "y".into(),
1703 z: 'z' as i64,
1704 }]])
1705 .into_connection();
1706
1707 assert!(!Entity::find()
1708 .cursor_by((Column::X, Column::Y, Column::Z))
1709 .after(('x' as i32, "y".to_owned(), 'z' as i64))
1710 .first(4)
1711 .all(&db)
1712 .await?
1713 .is_empty());
1714
1715 assert_eq!(
1716 db.into_transaction_log(),
1717 [Transaction::many([Statement::from_sql_and_values(
1718 DbBackend::Postgres,
1719 [
1720 r#"SELECT "m"."x", "m"."y", "m"."z""#,
1721 r#"FROM "m""#,
1722 r#"WHERE ("m"."x" = $1 AND "m"."y" = $2 AND "m"."z" > $3)"#,
1723 r#"OR ("m"."x" = $4 AND "m"."y" > $5)"#,
1724 r#"OR "m"."x" > $6"#,
1725 r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1726 r#"LIMIT $7"#,
1727 ]
1728 .join(" ")
1729 .as_str(),
1730 [
1731 ('x' as i32).into(),
1732 "y".into(),
1733 ('z' as i64).into(),
1734 ('x' as i32).into(),
1735 "y".into(),
1736 ('x' as i32).into(),
1737 4_u64.into(),
1738 ]
1739 ),])]
1740 );
1741
1742 Ok(())
1743 }
1744
1745 #[smol_potat::test]
1746 async fn composite_keys_5_desc() -> Result<(), DbErr> {
1747 use xyz_entity::*;
1748
1749 let db = MockDatabase::new(DbBackend::Postgres)
1750 .append_query_results([[Model {
1751 x: 'x' as i32,
1752 y: "y".into(),
1753 z: 'z' as i64,
1754 }]])
1755 .into_connection();
1756
1757 assert!(!Entity::find()
1758 .cursor_by((Column::X, Column::Y, Column::Z))
1759 .before(('x' as i32, "y".to_owned(), 'z' as i64))
1760 .last(4)
1761 .desc()
1762 .all(&db)
1763 .await?
1764 .is_empty());
1765
1766 assert_eq!(
1767 db.into_transaction_log(),
1768 [Transaction::many([Statement::from_sql_and_values(
1769 DbBackend::Postgres,
1770 [
1771 r#"SELECT "m"."x", "m"."y", "m"."z""#,
1772 r#"FROM "m""#,
1773 r#"WHERE ("m"."x" = $1 AND "m"."y" = $2 AND "m"."z" > $3)"#,
1774 r#"OR ("m"."x" = $4 AND "m"."y" > $5)"#,
1775 r#"OR "m"."x" > $6"#,
1776 r#"ORDER BY "m"."x" ASC, "m"."y" ASC, "m"."z" ASC"#,
1777 r#"LIMIT $7"#,
1778 ]
1779 .join(" ")
1780 .as_str(),
1781 [
1782 ('x' as i32).into(),
1783 "y".into(),
1784 ('z' as i64).into(),
1785 ('x' as i32).into(),
1786 "y".into(),
1787 ('x' as i32).into(),
1788 4_u64.into(),
1789 ]
1790 ),])]
1791 );
1792
1793 Ok(())
1794 }
1795
1796 mod composite_entity {
1797 use crate as sea_orm;
1798 use crate::entity::prelude::*;
1799
1800 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
1801 #[sea_orm(table_name = "t")]
1802 pub struct Model {
1803 #[sea_orm(primary_key)]
1804 pub col_1: String,
1805 #[sea_orm(primary_key)]
1806 pub col_2: String,
1807 #[sea_orm(primary_key)]
1808 pub col_3: String,
1809 #[sea_orm(primary_key)]
1810 pub col_4: String,
1811 #[sea_orm(primary_key)]
1812 pub col_5: String,
1813 #[sea_orm(primary_key)]
1814 pub col_6: String,
1815 #[sea_orm(primary_key)]
1816 pub col_7: String,
1817 #[sea_orm(primary_key)]
1818 pub col_8: String,
1819 #[sea_orm(primary_key)]
1820 pub col_9: String,
1821 #[sea_orm(primary_key)]
1822 pub col_10: String,
1823 #[sea_orm(primary_key)]
1824 pub col_11: String,
1825 #[sea_orm(primary_key)]
1826 pub col_12: String,
1827 }
1828
1829 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1830 pub enum Relation {}
1831
1832 impl ActiveModelBehavior for ActiveModel {}
1833 }
1834
1835 #[smol_potat::test]
1836 async fn cursor_by_many() -> Result<(), DbErr> {
1837 use composite_entity::*;
1838
1839 let base_sql = [
1840 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""#,
1841 r#"FROM "t" WHERE"#,
1842 ].join(" ");
1843
1844 assert_eq!(
1845 DbBackend::Postgres.build(&
1846 Entity::find()
1847 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4))
1848 .after(("val_1", "val_2", "val_3", "val_4")).apply_limit().apply_order_by().apply_filters().query
1849 ).to_string(),
1850 format!("{base_sql} {}", [
1851 r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1852 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1853 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1854 r#"OR "t"."col_1" > 'val_1'"#,
1855 r#"ORDER BY "t"."col_1" ASC, "t"."col_2" ASC, "t"."col_3" ASC, "t"."col_4" ASC"#,
1856 ].join(" "))
1857 );
1858
1859 assert_eq!(
1860 DbBackend::Postgres.build(&
1861 Entity::find()
1862 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5))
1863 .after(("val_1", "val_2", "val_3", "val_4", "val_5")).apply_limit().apply_order_by().apply_filters()
1864 .query
1865 ).to_string(),
1866 format!("{base_sql} {}", [
1867 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')"#,
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"#,
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))
1880 .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")).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')"#,
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')"#,
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')"#,
1887 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1888 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1889 r#"OR "t"."col_1" > 'val_1'"#,
1890 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"#,
1891 ].join(" "))
1892 );
1893
1894 assert_eq!(
1895 DbBackend::Postgres.build(&
1896 Entity::find()
1897 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7))
1898 .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")).apply_limit().apply_order_by().apply_filters()
1899 .query
1900 ).to_string(),
1901 format!("{base_sql} {}", [
1902 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')"#,
1903 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')"#,
1904 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')"#,
1905 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')"#,
1906 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
1907 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
1908 r#"OR "t"."col_1" < 'val_1'"#,
1909 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"#,
1910 ].join(" "))
1911 );
1912
1913 assert_eq!(
1914 DbBackend::Postgres.build(&
1915 Entity::find()
1916 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8))
1917 .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7", "val_8")).apply_limit().apply_order_by().apply_filters()
1918 .query
1919 ).to_string(),
1920 format!("{base_sql} {}", [
1921 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')"#,
1922 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')"#,
1923 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')"#,
1924 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')"#,
1925 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')"#,
1926 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
1927 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
1928 r#"OR "t"."col_1" < 'val_1'"#,
1929 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"#,
1930 ].join(" "))
1931 );
1932
1933 assert_eq!(
1934 DbBackend::Postgres.build(&
1935 Entity::find()
1936 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8, Column::Col9))
1937 .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()
1938 .query
1939 ).to_string(),
1940 format!("{base_sql} {}", [
1941 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')"#,
1942 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')"#,
1943 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')"#,
1944 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')"#,
1945 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
1946 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
1947 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
1948 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
1949 r#"OR "t"."col_1" < 'val_1'"#,
1950 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"#,
1951 ].join(" "))
1952 );
1953
1954 Ok(())
1955 }
1956
1957 #[smol_potat::test]
1958 async fn cursor_by_many_desc() -> Result<(), DbErr> {
1959 use composite_entity::*;
1960
1961 let base_sql = [
1962 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""#,
1963 r#"FROM "t" WHERE"#,
1964 ].join(" ");
1965
1966 assert_eq!(
1967 DbBackend::Postgres.build(&
1968 Entity::find()
1969 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4))
1970 .before(("val_1", "val_2", "val_3", "val_4")).desc().apply_limit().apply_order_by().apply_filters().query
1971 ).to_string(),
1972 format!("{base_sql} {}", [
1973 r#"("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" > 'val_4')"#,
1974 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
1975 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
1976 r#"OR "t"."col_1" > 'val_1'"#,
1977 r#"ORDER BY "t"."col_1" DESC, "t"."col_2" DESC, "t"."col_3" DESC, "t"."col_4" DESC"#,
1978 ].join(" "))
1979 );
1980
1981 assert_eq!(
1982 DbBackend::Postgres.build(&
1983 Entity::find()
1984 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5))
1985 .before(("val_1", "val_2", "val_3", "val_4", "val_5")).desc().apply_limit().apply_order_by().apply_filters()
1986 .query
1987 ).to_string(),
1988 format!("{base_sql} {}", [
1989 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')"#,
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"#,
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))
2002 .before(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6")).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')"#,
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')"#,
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')"#,
2009 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" > 'val_3')"#,
2010 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" > 'val_2')"#,
2011 r#"OR "t"."col_1" > 'val_1'"#,
2012 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"#,
2013 ].join(" "))
2014 );
2015
2016 assert_eq!(
2017 DbBackend::Postgres.build(&
2018 Entity::find()
2019 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7))
2020 .after(("val_1", "val_2", "val_3", "val_4", "val_5", "val_6", "val_7")).desc().apply_limit().apply_order_by().apply_filters()
2021 .query
2022 ).to_string(),
2023 format!("{base_sql} {}", [
2024 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')"#,
2025 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')"#,
2026 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')"#,
2027 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')"#,
2028 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
2029 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
2030 r#"OR "t"."col_1" < 'val_1'"#,
2031 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"#,
2032 ].join(" "))
2033 );
2034
2035 assert_eq!(
2036 DbBackend::Postgres.build(&
2037 Entity::find()
2038 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8))
2039 .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()
2040 .query
2041 ).to_string(),
2042 format!("{base_sql} {}", [
2043 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')"#,
2044 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')"#,
2045 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')"#,
2046 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')"#,
2047 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')"#,
2048 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
2049 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
2050 r#"OR "t"."col_1" < 'val_1'"#,
2051 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"#,
2052 ].join(" "))
2053 );
2054
2055 assert_eq!(
2056 DbBackend::Postgres.build(&
2057 Entity::find()
2058 .cursor_by((Column::Col1, Column::Col2, Column::Col3, Column::Col4, Column::Col5, Column::Col6, Column::Col7, Column::Col8, Column::Col9))
2059 .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()
2060 .query
2061 ).to_string(),
2062 format!("{base_sql} {}", [
2063 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')"#,
2064 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')"#,
2065 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')"#,
2066 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')"#,
2067 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" = 'val_4' AND "t"."col_5" < 'val_5')"#,
2068 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" = 'val_3' AND "t"."col_4" < 'val_4')"#,
2069 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" = 'val_2' AND "t"."col_3" < 'val_3')"#,
2070 r#"OR ("t"."col_1" = 'val_1' AND "t"."col_2" < 'val_2')"#,
2071 r#"OR "t"."col_1" < 'val_1'"#,
2072 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"#,
2073 ].join(" "))
2074 );
2075
2076 Ok(())
2077 }
2078
2079 mod test_base_entity {
2080 use crate as sea_orm;
2081 use crate::entity::prelude::*;
2082
2083 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
2084 #[sea_orm(table_name = "base")]
2085 pub struct Model {
2086 #[sea_orm(primary_key)]
2087 pub id: i32,
2088 #[sea_orm(primary_key)]
2089 pub name: String,
2090 }
2091
2092 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
2093 pub enum Relation {
2094 #[sea_orm(has_many = "super::test_related_entity::Entity")]
2095 TestRelatedEntity,
2096 }
2097
2098 impl Related<super::test_related_entity::Entity> for Entity {
2099 fn to() -> RelationDef {
2100 Relation::TestRelatedEntity.def()
2101 }
2102 }
2103
2104 impl ActiveModelBehavior for ActiveModel {}
2105 }
2106
2107 mod test_related_entity {
2108 use super::test_base_entity;
2109 use crate as sea_orm;
2110 use crate::entity::prelude::*;
2111
2112 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
2113 #[sea_orm(table_name = "related")]
2114 pub struct Model {
2115 #[sea_orm(primary_key)]
2116 pub id: i32,
2117 #[sea_orm(primary_key)]
2118 pub name: String,
2119 pub test_id: i32,
2120 }
2121
2122 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
2123 pub enum Relation {
2124 #[sea_orm(
2125 belongs_to = "test_base_entity::Entity",
2126 from = "Column::TestId",
2127 to = "super::test_base_entity::Column::Id"
2128 )]
2129 TestBaseEntity,
2130 }
2131
2132 impl Related<super::test_base_entity::Entity> for Entity {
2133 fn to() -> RelationDef {
2134 Relation::TestBaseEntity.def()
2135 }
2136 }
2137
2138 impl ActiveModelBehavior for ActiveModel {}
2139 }
2140
2141 #[smol_potat::test]
2142 async fn related_composite_keys_1() -> Result<(), DbErr> {
2143 let db = MockDatabase::new(DbBackend::Postgres)
2144 .append_query_results([[(
2145 test_base_entity::Model {
2146 id: 1,
2147 name: "CAT".into(),
2148 },
2149 test_related_entity::Model {
2150 id: 1,
2151 name: "CATE".into(),
2152 test_id: 1,
2153 },
2154 )]])
2155 .into_connection();
2156
2157 assert!(!test_base_entity::Entity::find()
2158 .find_also_related(test_related_entity::Entity)
2159 .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2160 .first(1)
2161 .all(&db)
2162 .await?
2163 .is_empty());
2164
2165 assert_eq!(
2166 db.into_transaction_log(),
2167 [Transaction::many([Statement::from_sql_and_values(
2168 DbBackend::Postgres,
2169 [
2170 r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2171 r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2172 r#"FROM "base""#,
2173 r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2174 r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $1"#,
2175 ]
2176 .join(" ")
2177 .as_str(),
2178 [1_u64.into()]
2179 ),])]
2180 );
2181
2182 Ok(())
2183 }
2184
2185 #[smol_potat::test]
2186 async fn related_composite_keys_1_desc() -> Result<(), DbErr> {
2187 let db = MockDatabase::new(DbBackend::Postgres)
2188 .append_query_results([[(
2189 test_base_entity::Model {
2190 id: 1,
2191 name: "CAT".into(),
2192 },
2193 test_related_entity::Model {
2194 id: 1,
2195 name: "CATE".into(),
2196 test_id: 1,
2197 },
2198 )]])
2199 .into_connection();
2200
2201 assert!(!test_base_entity::Entity::find()
2202 .find_also_related(test_related_entity::Entity)
2203 .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2204 .last(1)
2205 .desc()
2206 .all(&db)
2207 .await?
2208 .is_empty());
2209
2210 assert_eq!(
2211 db.into_transaction_log(),
2212 [Transaction::many([Statement::from_sql_and_values(
2213 DbBackend::Postgres,
2214 [
2215 r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2216 r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2217 r#"FROM "base""#,
2218 r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2219 r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $1"#,
2220 ]
2221 .join(" ")
2222 .as_str(),
2223 [1_u64.into()]
2224 ),])]
2225 );
2226
2227 Ok(())
2228 }
2229
2230 #[smol_potat::test]
2231 async fn related_composite_keys_2() -> Result<(), DbErr> {
2232 let db = MockDatabase::new(DbBackend::Postgres)
2233 .append_query_results([[(
2234 test_base_entity::Model {
2235 id: 1,
2236 name: "CAT".into(),
2237 },
2238 test_related_entity::Model {
2239 id: 1,
2240 name: "CATE".into(),
2241 test_id: 1,
2242 },
2243 )]])
2244 .into_connection();
2245
2246 assert!(!test_base_entity::Entity::find()
2247 .find_also_related(test_related_entity::Entity)
2248 .cursor_by((test_base_entity::Column::Id, test_base_entity::Column::Name))
2249 .after((1, "C".to_string()))
2250 .first(2)
2251 .all(&db)
2252 .await?
2253 .is_empty());
2254
2255 assert_eq!(
2256 db.into_transaction_log(),
2257 [Transaction::many([Statement::from_sql_and_values(
2258 DbBackend::Postgres,
2259 [
2260 r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2261 r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2262 r#"FROM "base""#,
2263 r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2264 r#"WHERE ("base"."id" = $1 AND "base"."name" > $2) OR "base"."id" > $3"#,
2265 r#"ORDER BY "base"."id" ASC, "base"."name" ASC, "related"."id" ASC, "related"."name" ASC LIMIT $4"#,
2266 ]
2267 .join(" ")
2268 .as_str(),
2269 [
2270 1_i32.into(),
2271 "C".into(),
2272 1_i32.into(),
2273 2_u64.into(),
2274 ]
2275 ),])]
2276 );
2277
2278 Ok(())
2279 }
2280
2281 #[smol_potat::test]
2282 async fn related_composite_keys_2_desc() -> Result<(), DbErr> {
2283 let db = MockDatabase::new(DbBackend::Postgres)
2284 .append_query_results([[(
2285 test_base_entity::Model {
2286 id: 1,
2287 name: "CAT".into(),
2288 },
2289 test_related_entity::Model {
2290 id: 1,
2291 name: "CATE".into(),
2292 test_id: 1,
2293 },
2294 )]])
2295 .into_connection();
2296
2297 assert!(!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 .before((1, "C".to_string()))
2301 .last(2)
2302 .desc()
2303 .all(&db)
2304 .await?
2305 .is_empty());
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_3() -> 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!(!test_base_entity::Entity::find()
2350 .find_also_related(test_related_entity::Entity)
2351 .cursor_by_other((
2352 test_related_entity::Column::Id,
2353 test_related_entity::Column::Name
2354 ))
2355 .after((1, "CAT".to_string()))
2356 .first(2)
2357 .all(&db)
2358 .await?
2359 .is_empty());
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 ("related"."id" = $1 AND "related"."name" > $2) OR "related"."id" > $3"#,
2371 r#"ORDER BY "related"."id" ASC, "related"."name" ASC, "base"."id" ASC, "base"."name" ASC LIMIT $4"#,
2372 ]
2373 .join(" ")
2374 .as_str(),
2375 [
2376 1_i32.into(),
2377 "CAT".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_desc() -> 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!(!test_base_entity::Entity::find()
2404 .find_also_related(test_related_entity::Entity)
2405 .cursor_by_other((
2406 test_related_entity::Column::Id,
2407 test_related_entity::Column::Name
2408 ))
2409 .before((1, "CAT".to_string()))
2410 .last(2)
2411 .desc()
2412 .all(&db)
2413 .await?
2414 .is_empty());
2415
2416 assert_eq!(
2417 db.into_transaction_log(),
2418 [Transaction::many([Statement::from_sql_and_values(
2419 DbBackend::Postgres,
2420 [
2421 r#"SELECT "base"."id" AS "A_id", "base"."name" AS "A_name","#,
2422 r#""related"."id" AS "B_id", "related"."name" AS "B_name", "related"."test_id" AS "B_test_id""#,
2423 r#"FROM "base""#,
2424 r#"LEFT JOIN "related" ON "base"."id" = "related"."test_id""#,
2425 r#"WHERE ("related"."id" = $1 AND "related"."name" > $2) OR "related"."id" > $3"#,
2426 r#"ORDER BY "related"."id" ASC, "related"."name" ASC, "base"."id" ASC, "base"."name" ASC LIMIT $4"#,
2427 ]
2428 .join(" ")
2429 .as_str(),
2430 [
2431 1_i32.into(),
2432 "CAT".into(),
2433 1_i32.into(),
2434 2_u64.into(),
2435 ]
2436 ),])]
2437 );
2438
2439 Ok(())
2440 }
2441}