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