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