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