1use crate::{
2 ColumnDef, ColumnType, DbBackend, EntityName, Iden, IdenStatic, IntoSimpleExpr, Iterable,
3};
4use sea_query::{
5 BinOper, DynIden, Expr, ExprTrait, IntoIden, IntoLikeExpr, SeaRc, SelectStatement, SimpleExpr,
6 Value,
7};
8use std::{borrow::Cow, str::FromStr};
9
10pub(crate) mod methods {
11 macro_rules! bind_oper {
12 ($vis:vis $op:ident, $bin_op:ident) => {
13 #[allow(missing_docs)]
14 $vis fn $op<V>(&self, v: V) -> SimpleExpr
15 where
16 V: Into<Value>,
17 {
18 let expr = self.save_as(Expr::val(v));
19 Expr::col(self.as_column_ref()).binary(BinOper::$bin_op, expr)
20 }
21 };
22 }
23
24 macro_rules! bind_func_no_params {
25 ($vis:vis $func:ident) => {
26 $vis fn $func(&self) -> SimpleExpr {
28 Expr::col(self.as_column_ref()).$func()
29 }
30 };
31 }
32
33 macro_rules! bind_vec_func {
34 ($vis:vis $func:ident) => {
35 #[allow(missing_docs)]
36 #[allow(clippy::wrong_self_convention)]
37 $vis fn $func<V, I>(&self, v: I) -> SimpleExpr
38 where
39 V: Into<Value>,
40 I: IntoIterator<Item = V>,
41 {
42 let v_with_enum_cast = v.into_iter().map(|v| self.save_as(Expr::val(v)));
43 Expr::col(self.as_column_ref()).$func(v_with_enum_cast)
44 }
45 };
46 }
47
48 macro_rules! bind_subquery_func {
49 ($vis:vis $func:ident) => {
50 #[allow(clippy::wrong_self_convention)]
51 #[allow(missing_docs)]
52 $vis fn $func(&self, s: SelectStatement) -> SimpleExpr {
53 Expr::col(self.as_column_ref()).$func(s)
54 }
55 };
56 }
57
58 pub(crate) use bind_func_no_params;
59 pub(crate) use bind_oper;
60 pub(crate) use bind_subquery_func;
61 pub(crate) use bind_vec_func;
62}
63
64use methods::*;
65
66pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
69 #[allow(missing_docs)]
70 type EntityName: EntityName;
71
72 fn def(&self) -> ColumnDef;
74
75 fn enum_type_name(&self) -> Option<&'static str> {
77 None
78 }
79
80 fn entity_name(&self) -> DynIden {
82 SeaRc::new(Self::EntityName::default())
83 }
84
85 fn as_column_ref(&self) -> (DynIden, DynIden) {
87 (self.entity_name(), SeaRc::new(*self))
88 }
89
90 bind_oper!(eq, Equal);
91 bind_oper!(ne, NotEqual);
92 bind_oper!(gt, GreaterThan);
93 bind_oper!(gte, GreaterThanOrEqual);
94 bind_oper!(lt, SmallerThan);
95 bind_oper!(lte, SmallerThanOrEqual);
96
97 fn between<V>(&self, a: V, b: V) -> SimpleExpr
109 where
110 V: Into<Value>,
111 {
112 Expr::col(self.as_column_ref()).between(a, b)
113 }
114
115 fn not_between<V>(&self, a: V, b: V) -> SimpleExpr
127 where
128 V: Into<Value>,
129 {
130 Expr::col(self.as_column_ref()).not_between(a, b)
131 }
132
133 fn like<T>(&self, s: T) -> SimpleExpr
145 where
146 T: IntoLikeExpr,
147 {
148 Expr::col(self.as_column_ref()).like(s)
149 }
150
151 fn not_like<T>(&self, s: T) -> SimpleExpr
163 where
164 T: IntoLikeExpr,
165 {
166 Expr::col(self.as_column_ref()).not_like(s)
167 }
168
169 fn starts_with<T>(&self, s: T) -> SimpleExpr
186 where
187 T: Into<String>,
188 {
189 let pattern = format!("{}%", s.into());
190 Expr::col(self.as_column_ref()).like(pattern)
191 }
192
193 fn ends_with<T>(&self, s: T) -> SimpleExpr
210 where
211 T: Into<String>,
212 {
213 let pattern = format!("%{}", s.into());
214 Expr::col(self.as_column_ref()).like(pattern)
215 }
216
217 fn contains<T>(&self, s: T) -> SimpleExpr
234 where
235 T: Into<String>,
236 {
237 let pattern = format!("%{}%", s.into());
238 Expr::col(self.as_column_ref()).like(pattern)
239 }
240
241 bind_func_no_params!(max);
242 bind_func_no_params!(min);
243 bind_func_no_params!(sum);
244 bind_func_no_params!(count);
245 bind_func_no_params!(is_null);
246 bind_func_no_params!(is_not_null);
247
248 fn if_null<V>(&self, v: V) -> SimpleExpr
250 where
251 V: Into<Value>,
252 {
253 Expr::col(self.as_column_ref()).if_null(v)
254 }
255
256 bind_vec_func!(is_in);
257 bind_vec_func!(is_not_in);
258
259 #[cfg(feature = "postgres-array")]
272 fn eq_any<V, I>(&self, v: I) -> SimpleExpr
273 where
274 V: Into<Value> + sea_query::ValueType + sea_query::with_array::NotU8,
275 I: IntoIterator<Item = V>,
276 {
277 use sea_query::extension::postgres::PgFunc;
278
279 let vec: Vec<_> = v.into_iter().collect();
280 Expr::col(self.as_column_ref()).eq(PgFunc::any(vec))
281 }
282
283 bind_subquery_func!(in_subquery);
284 bind_subquery_func!(not_in_subquery);
285
286 fn into_expr(self) -> Expr {
288 self.into_simple_expr()
289 }
290
291 #[allow(clippy::match_single_binding)]
293 fn into_returning_expr(self, db_backend: DbBackend) -> Expr {
294 match db_backend {
295 _ => Expr::col(self),
296 }
297 }
298
299 fn select_as(&self, expr: Expr) -> SimpleExpr {
302 self.select_enum_as(expr)
303 }
304
305 fn select_enum_as(&self, expr: Expr) -> SimpleExpr {
307 cast_enum_as(expr, &self.def(), select_enum_as)
308 }
309
310 fn save_as(&self, val: Expr) -> SimpleExpr {
313 self.save_enum_as(val)
314 }
315
316 fn save_enum_as(&self, val: Expr) -> SimpleExpr {
318 cast_enum_as(val, &self.def(), save_enum_as)
319 }
320}
321
322pub trait ColumnTypeTrait {
324 fn def(self) -> ColumnDef;
326
327 fn get_enum_name(&self) -> Option<&DynIden>;
329}
330
331impl ColumnTypeTrait for ColumnType {
332 fn def(self) -> ColumnDef {
333 ColumnDef {
334 col_type: self,
335 null: false,
336 unique: false,
337 indexed: false,
338 default: None,
339 comment: None,
340 unique_key: None,
341 seaography: Default::default(),
342 }
343 }
344
345 fn get_enum_name(&self) -> Option<&DynIden> {
346 enum_name(self)
347 }
348}
349
350impl ColumnTypeTrait for ColumnDef {
351 fn def(self) -> ColumnDef {
352 self
353 }
354
355 fn get_enum_name(&self) -> Option<&DynIden> {
356 enum_name(&self.col_type)
357 }
358}
359
360fn enum_name(col_type: &ColumnType) -> Option<&DynIden> {
361 match col_type {
362 ColumnType::Enum { name, .. } => Some(name),
363 ColumnType::Array(col_type) => enum_name(col_type),
364 _ => None,
365 }
366}
367
368struct Text;
369struct TextArray;
370
371impl Iden for Text {
372 fn quoted(&self) -> Cow<'static, str> {
373 Cow::Borrowed("text")
374 }
375
376 fn unquoted(&self) -> &str {
377 match self.quoted() {
378 Cow::Borrowed(s) => s,
379 _ => unreachable!(),
380 }
381 }
382}
383
384impl Iden for TextArray {
385 fn quoted(&self) -> Cow<'static, str> {
386 Cow::Borrowed("text[]")
388 }
389
390 fn unquoted(&self) -> &str {
391 match self.quoted() {
392 Cow::Borrowed(s) => s,
393 _ => unreachable!(),
394 }
395 }
396}
397
398pub(crate) fn select_enum_as(col: Expr, _: DynIden, col_type: &ColumnType) -> SimpleExpr {
399 let type_name = match col_type {
400 ColumnType::Array(_) => TextArray.into_iden(),
401 _ => Text.into_iden(),
402 };
403 col.as_enum(type_name)
404}
405
406pub(crate) fn save_enum_as(col: Expr, enum_name: DynIden, col_type: &ColumnType) -> SimpleExpr {
407 let type_name = match col_type {
408 ColumnType::Array(_) => format!("{enum_name}[]").into_iden(),
409 _ => enum_name,
410 };
411 col.as_enum(type_name)
412}
413
414pub(crate) fn cast_enum_as<F>(expr: Expr, col_def: &ColumnDef, f: F) -> SimpleExpr
415where
416 F: Fn(Expr, DynIden, &ColumnType) -> SimpleExpr,
417{
418 let col_type = col_def.get_column_type();
419
420 match col_type {
421 #[cfg(all(feature = "with-json", feature = "postgres-array"))]
422 ColumnType::Json | ColumnType::JsonBinary => {
423 use sea_query::ArrayType;
424 use serde_json::Value as Json;
425
426 match expr {
427 SimpleExpr::Value(Value::Array(ArrayType::Json, Some(json_vec))) => {
428 let json_vec: Vec<Json> = json_vec
430 .into_iter()
431 .filter_map(|val| match val {
432 Value::Json(Some(json)) => Some(json),
433 _ => None,
434 })
435 .collect();
436 SimpleExpr::Value(Value::Json(Some(json_vec.into())))
437 }
438 SimpleExpr::Value(Value::Array(ArrayType::Json, None)) => {
439 SimpleExpr::Value(Value::Json(None))
440 }
441 _ => expr,
442 }
443 }
444 _ => match col_type.get_enum_name() {
445 Some(enum_name) => f(expr, enum_name.clone(), col_type),
446 None => expr,
447 },
448 }
449}
450
451#[cfg(test)]
452mod tests {
453 use crate::{
454 ColumnTrait, Condition, DbBackend, EntityTrait, QueryFilter, QueryTrait, tests_cfg::*,
455 };
456 use sea_query::Query;
457
458 #[test]
459 fn test_in_subquery_1() {
460 assert_eq!(
461 cake::Entity::find()
462 .filter(
463 Condition::any().add(
464 cake::Column::Id.in_subquery(
465 Query::select()
466 .expr(cake::Column::Id.max())
467 .from(cake::Entity)
468 .to_owned()
469 )
470 )
471 )
472 .build(DbBackend::MySql)
473 .to_string(),
474 [
475 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
476 "WHERE `cake`.`id` IN (SELECT MAX(`cake`.`id`) FROM `cake`)",
477 ]
478 .join(" ")
479 );
480 }
481
482 #[test]
483 fn test_in_subquery_2() {
484 assert_eq!(
485 cake::Entity::find()
486 .filter(
487 Condition::any().add(
488 cake::Column::Id.in_subquery(
489 Query::select()
490 .column(cake_filling::Column::CakeId)
491 .from(cake_filling::Entity)
492 .to_owned()
493 )
494 )
495 )
496 .build(DbBackend::MySql)
497 .to_string(),
498 [
499 "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
500 "WHERE `cake`.`id` IN (SELECT `cake_id` FROM `cake_filling`)",
501 ]
502 .join(" ")
503 );
504 }
505
506 #[test]
507 #[cfg(feature = "macros")]
508 fn select_as_1() {
509 use crate::{ActiveModelTrait, ActiveValue, Update};
510
511 mod hello_expanded {
512 use crate as sea_orm;
513 use crate::entity::prelude::*;
514 use crate::sea_query::{Expr, ExprTrait, SimpleExpr};
515
516 #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
517 pub struct Entity;
518
519 impl EntityName for Entity {
520 fn table_name(&self) -> &'static str {
521 "hello"
522 }
523 }
524
525 #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
526 pub struct Model {
527 pub id: i32,
528 #[sea_orm(enum_name = "One1")]
529 pub one: i32,
530 pub two: i32,
531 #[sea_orm(enum_name = "Three3")]
532 pub three: i32,
533 }
534
535 #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
536 pub enum Column {
537 Id,
538 One1,
539 Two,
540 Three3,
541 }
542
543 impl ColumnTrait for Column {
544 type EntityName = Entity;
545
546 fn def(&self) -> ColumnDef {
547 match self {
548 Column::Id => ColumnType::Integer.def(),
549 Column::One1 => ColumnType::Integer.def(),
550 Column::Two => ColumnType::Integer.def(),
551 Column::Three3 => ColumnType::Integer.def(),
552 }
553 }
554
555 fn select_as(&self, expr: Expr) -> SimpleExpr {
556 match self {
557 Self::Two => expr.cast_as("integer"),
558 _ => self.select_enum_as(expr),
559 }
560 }
561 }
562
563 #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
564 pub enum PrimaryKey {
565 Id,
566 }
567
568 impl PrimaryKeyTrait for PrimaryKey {
569 type ValueType = i32;
570
571 fn auto_increment() -> bool {
572 true
573 }
574 }
575
576 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
577 pub enum Relation {}
578
579 impl ActiveModelBehavior for ActiveModel {}
580 }
581
582 #[allow(clippy::enum_variant_names)]
583 mod hello_compact {
584 use crate as sea_orm;
585 use crate::entity::prelude::*;
586
587 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
588 #[sea_orm(table_name = "hello")]
589 pub struct Model {
590 #[sea_orm(primary_key)]
591 pub id: i32,
592 #[sea_orm(enum_name = "One1")]
593 pub one: i32,
594 #[sea_orm(select_as = "integer")]
595 pub two: i32,
596 #[sea_orm(enum_name = "Three3")]
597 pub three: i32,
598 }
599
600 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
601 pub enum Relation {}
602
603 impl ActiveModelBehavior for ActiveModel {}
604 }
605
606 fn assert_it<E, A>(active_model: A)
607 where
608 E: EntityTrait,
609 A: ActiveModelTrait<Entity = E>,
610 {
611 assert_eq!(
612 E::find().build(DbBackend::Postgres).to_string(),
613 r#"SELECT "hello"."id", "hello"."one1", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello""#,
614 );
615 assert_eq!(
616 Update::one(active_model)
617 .validate()
618 .unwrap()
619 .build(DbBackend::Postgres)
620 .to_string(),
621 r#"UPDATE "hello" SET "one1" = 1, "two" = 2, "three3" = 3 WHERE "hello"."id" = 1"#,
622 );
623 }
624
625 assert_it(hello_expanded::ActiveModel {
626 id: ActiveValue::set(1),
627 one: ActiveValue::set(1),
628 two: ActiveValue::set(2),
629 three: ActiveValue::set(3),
630 });
631 assert_it(hello_compact::ActiveModel {
632 id: ActiveValue::set(1),
633 one: ActiveValue::set(1),
634 two: ActiveValue::set(2),
635 three: ActiveValue::set(3),
636 });
637 }
638
639 #[test]
640 #[cfg(feature = "macros")]
641 fn save_as_1() {
642 use crate::{ActiveModelTrait, ActiveValue, Update};
643
644 mod hello_expanded {
645 use crate as sea_orm;
646 use crate::entity::prelude::*;
647 use crate::sea_query::{Expr, ExprTrait, SimpleExpr};
648
649 #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
650 pub struct Entity;
651
652 impl EntityName for Entity {
653 fn table_name(&self) -> &'static str {
654 "hello"
655 }
656 }
657
658 #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
659 pub struct Model {
660 pub id: i32,
661 #[sea_orm(enum_name = "One1")]
662 pub one: i32,
663 pub two: i32,
664 #[sea_orm(enum_name = "Three3")]
665 pub three: i32,
666 }
667
668 #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
669 pub enum Column {
670 Id,
671 One1,
672 Two,
673 Three3,
674 }
675
676 impl ColumnTrait for Column {
677 type EntityName = Entity;
678
679 fn def(&self) -> ColumnDef {
680 match self {
681 Column::Id => ColumnType::Integer.def(),
682 Column::One1 => ColumnType::Integer.def(),
683 Column::Two => ColumnType::Integer.def(),
684 Column::Three3 => ColumnType::Integer.def(),
685 }
686 }
687
688 fn save_as(&self, val: Expr) -> SimpleExpr {
689 match self {
690 Self::Two => val.cast_as("text"),
691 _ => self.save_enum_as(val),
692 }
693 }
694 }
695
696 #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
697 pub enum PrimaryKey {
698 Id,
699 }
700
701 impl PrimaryKeyTrait for PrimaryKey {
702 type ValueType = i32;
703
704 fn auto_increment() -> bool {
705 true
706 }
707 }
708
709 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
710 pub enum Relation {}
711
712 impl ActiveModelBehavior for ActiveModel {}
713 }
714
715 #[allow(clippy::enum_variant_names)]
716 mod hello_compact {
717 use crate as sea_orm;
718 use crate::entity::prelude::*;
719
720 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
721 #[sea_orm(table_name = "hello")]
722 pub struct Model {
723 #[sea_orm(primary_key)]
724 pub id: i32,
725 #[sea_orm(enum_name = "One1")]
726 pub one: i32,
727 #[sea_orm(save_as = "text")]
728 pub two: i32,
729 #[sea_orm(enum_name = "Three3")]
730 pub three: i32,
731 }
732
733 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
734 pub enum Relation {}
735
736 impl ActiveModelBehavior for ActiveModel {}
737 }
738
739 fn assert_it<E, A>(active_model: A)
740 where
741 E: EntityTrait,
742 A: ActiveModelTrait<Entity = E>,
743 {
744 assert_eq!(
745 E::find().build(DbBackend::Postgres).to_string(),
746 r#"SELECT "hello"."id", "hello"."one1", "hello"."two", "hello"."three3" FROM "hello""#,
747 );
748 assert_eq!(
749 Update::one(active_model)
750 .validate()
751 .unwrap()
752 .build(DbBackend::Postgres)
753 .to_string(),
754 r#"UPDATE "hello" SET "one1" = 1, "two" = CAST(2 AS text), "three3" = 3 WHERE "hello"."id" = 1"#,
755 );
756 }
757
758 assert_it(hello_expanded::ActiveModel {
759 id: ActiveValue::set(1),
760 one: ActiveValue::set(1),
761 two: ActiveValue::set(2),
762 three: ActiveValue::set(3),
763 });
764 assert_it(hello_compact::ActiveModel {
765 id: ActiveValue::set(1),
766 one: ActiveValue::set(1),
767 two: ActiveValue::set(2),
768 three: ActiveValue::set(3),
769 });
770 }
771
772 #[test]
773 #[cfg(feature = "macros")]
774 fn select_as_and_value_1() {
775 use crate::{ActiveModelTrait, ActiveValue, Update};
776
777 mod hello_expanded {
778 use crate as sea_orm;
779 use crate::entity::prelude::*;
780 use crate::sea_query::{Expr, ExprTrait, SimpleExpr};
781
782 #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
783 pub struct Entity;
784
785 impl EntityName for Entity {
786 fn table_name(&self) -> &'static str {
787 "hello"
788 }
789 }
790
791 #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
792 pub struct Model {
793 pub id: i32,
794 #[sea_orm(enum_name = "One1")]
795 pub one: i32,
796 pub two: i32,
797 #[sea_orm(enum_name = "Three3")]
798 pub three: i32,
799 }
800
801 #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
802 pub enum Column {
803 Id,
804 One1,
805 Two,
806 Three3,
807 }
808
809 impl ColumnTrait for Column {
810 type EntityName = Entity;
811
812 fn def(&self) -> ColumnDef {
813 match self {
814 Column::Id => ColumnType::Integer.def(),
815 Column::One1 => ColumnType::Integer.def(),
816 Column::Two => ColumnType::Integer.def(),
817 Column::Three3 => ColumnType::Integer.def(),
818 }
819 }
820
821 fn select_as(&self, expr: Expr) -> SimpleExpr {
822 match self {
823 Self::Two => expr.cast_as("integer"),
824 _ => self.select_enum_as(expr),
825 }
826 }
827
828 fn save_as(&self, val: Expr) -> SimpleExpr {
829 match self {
830 Self::Two => val.cast_as("text"),
831 _ => self.save_enum_as(val),
832 }
833 }
834 }
835
836 #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
837 pub enum PrimaryKey {
838 Id,
839 }
840
841 impl PrimaryKeyTrait for PrimaryKey {
842 type ValueType = i32;
843
844 fn auto_increment() -> bool {
845 true
846 }
847 }
848
849 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
850 pub enum Relation {}
851
852 impl ActiveModelBehavior for ActiveModel {}
853 }
854
855 #[allow(clippy::enum_variant_names)]
856 mod hello_compact {
857 use crate as sea_orm;
858 use crate::entity::prelude::*;
859
860 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
861 #[sea_orm(table_name = "hello")]
862 pub struct Model {
863 #[sea_orm(primary_key)]
864 pub id: i32,
865 #[sea_orm(enum_name = "One1")]
866 pub one: i32,
867 #[sea_orm(select_as = "integer", save_as = "text")]
868 pub two: i32,
869 #[sea_orm(enum_name = "Three3")]
870 pub three: i32,
871 }
872
873 #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
874 pub enum Relation {}
875
876 impl ActiveModelBehavior for ActiveModel {}
877 }
878
879 fn assert_it<E, A>(active_model: A)
880 where
881 E: EntityTrait,
882 A: ActiveModelTrait<Entity = E>,
883 {
884 assert_eq!(
885 E::find().build(DbBackend::Postgres).to_string(),
886 r#"SELECT "hello"."id", "hello"."one1", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello""#,
887 );
888 assert_eq!(
889 Update::one(active_model)
890 .validate()
891 .unwrap()
892 .build(DbBackend::Postgres)
893 .to_string(),
894 r#"UPDATE "hello" SET "one1" = 1, "two" = CAST(2 AS text), "three3" = 3 WHERE "hello"."id" = 1"#,
895 );
896 }
897
898 assert_it(hello_expanded::ActiveModel {
899 id: ActiveValue::set(1),
900 one: ActiveValue::set(1),
901 two: ActiveValue::set(2),
902 three: ActiveValue::set(3),
903 });
904 assert_it(hello_compact::ActiveModel {
905 id: ActiveValue::set(1),
906 one: ActiveValue::set(1),
907 two: ActiveValue::set(2),
908 three: ActiveValue::set(3),
909 });
910 }
911}