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