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