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