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