sea_orm/entity/
column.rs

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            /// See also SeaQuery's method with same name.
27            $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
66// LINT: when the operand value does not match column type
67/// API for working with a `Column`. Mostly a wrapper of the identically named methods in [`sea_query::Expr`]
68pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
69    #[allow(missing_docs)]
70    type EntityName: EntityName;
71
72    /// Define a column for an Entity
73    fn def(&self) -> ColumnDef;
74
75    /// Get the enum name of the column type
76    fn enum_type_name(&self) -> Option<&'static str> {
77        None
78    }
79
80    /// Get the name of the entity the column belongs to
81    fn entity_name(&self) -> DynIden {
82        SeaRc::new(Self::EntityName::default())
83    }
84
85    /// get the name of the entity the column belongs to
86    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    /// ```
98    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
99    ///
100    /// assert_eq!(
101    ///     cake::Entity::find()
102    ///         .filter(cake::Column::Id.between(2, 3))
103    ///         .build(DbBackend::MySql)
104    ///         .to_string(),
105    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` BETWEEN 2 AND 3"
106    /// );
107    /// ```
108    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    /// ```
116    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
117    ///
118    /// assert_eq!(
119    ///     cake::Entity::find()
120    ///         .filter(cake::Column::Id.not_between(2, 3))
121    ///         .build(DbBackend::MySql)
122    ///         .to_string(),
123    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` NOT BETWEEN 2 AND 3"
124    /// );
125    /// ```
126    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    /// ```
134    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
135    ///
136    /// assert_eq!(
137    ///     cake::Entity::find()
138    ///         .filter(cake::Column::Name.like("cheese"))
139    ///         .build(DbBackend::MySql)
140    ///         .to_string(),
141    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese'"
142    /// );
143    /// ```
144    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    /// ```
152    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
153    ///
154    /// assert_eq!(
155    ///     cake::Entity::find()
156    ///         .filter(cake::Column::Name.not_like("cheese"))
157    ///         .build(DbBackend::MySql)
158    ///         .to_string(),
159    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` NOT LIKE 'cheese'"
160    /// );
161    /// ```
162    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    /// Postgres Only.
170    /// ```
171    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
172    ///
173    /// assert_eq!(
174    ///     cake::Entity::find()
175    ///         .filter(cake::Column::Name.ilike("cheese"))
176    ///         .build(DbBackend::Postgres)
177    ///         .to_string(),
178    ///     r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."name" ILIKE 'cheese'"#
179    /// );
180    /// ```
181    fn ilike<T>(&self, s: T) -> SimpleExpr
182    where
183        T: IntoLikeExpr,
184    {
185        use sea_query::extension::postgres::PgExpr;
186
187        Expr::col(self.as_column_ref()).ilike(s)
188    }
189
190    /// Postgres Only.
191    /// ```
192    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
193    ///
194    /// assert_eq!(
195    ///     cake::Entity::find()
196    ///         .filter(cake::Column::Name.not_ilike("cheese"))
197    ///         .build(DbBackend::Postgres)
198    ///         .to_string(),
199    ///     r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."name" NOT ILIKE 'cheese'"#
200    /// );
201    /// ```
202    fn not_ilike<T>(&self, s: T) -> SimpleExpr
203    where
204        T: IntoLikeExpr,
205    {
206        use sea_query::extension::postgres::PgExpr;
207
208        Expr::col(self.as_column_ref()).not_ilike(s)
209    }
210
211    /// This is a simplified shorthand for a more general `like` method.
212    /// Use `like` if you need something more complex, like specifying an escape character.
213    ///
214    /// ## Examples
215    ///
216    /// ```
217    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
218    ///
219    /// assert_eq!(
220    ///     cake::Entity::find()
221    ///         .filter(cake::Column::Name.starts_with("cheese"))
222    ///         .build(DbBackend::MySql)
223    ///         .to_string(),
224    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese%'"
225    /// );
226    /// ```
227    fn starts_with<T>(&self, s: T) -> SimpleExpr
228    where
229        T: Into<String>,
230    {
231        let pattern = format!("{}%", s.into());
232        Expr::col(self.as_column_ref()).like(pattern)
233    }
234
235    /// This is a simplified shorthand for a more general `like` method.
236    /// Use `like` if you need something more complex, like specifying an escape character.
237    ///
238    /// ## Examples
239    ///
240    /// ```
241    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
242    ///
243    /// assert_eq!(
244    ///     cake::Entity::find()
245    ///         .filter(cake::Column::Name.ends_with("cheese"))
246    ///         .build(DbBackend::MySql)
247    ///         .to_string(),
248    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese'"
249    /// );
250    /// ```
251    fn ends_with<T>(&self, s: T) -> SimpleExpr
252    where
253        T: Into<String>,
254    {
255        let pattern = format!("%{}", s.into());
256        Expr::col(self.as_column_ref()).like(pattern)
257    }
258
259    /// This is a simplified shorthand for a more general `like` method.
260    /// Use `like` if you need something more complex, like specifying an escape character.
261    ///
262    /// ## Examples
263    ///
264    /// ```
265    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
266    ///
267    /// assert_eq!(
268    ///     cake::Entity::find()
269    ///         .filter(cake::Column::Name.contains("cheese"))
270    ///         .build(DbBackend::MySql)
271    ///         .to_string(),
272    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese%'"
273    /// );
274    /// ```
275    fn contains<T>(&self, s: T) -> SimpleExpr
276    where
277        T: Into<String>,
278    {
279        let pattern = format!("%{}%", s.into());
280        Expr::col(self.as_column_ref()).like(pattern)
281    }
282
283    bind_func_no_params!(max);
284    bind_func_no_params!(min);
285    bind_func_no_params!(sum);
286    bind_func_no_params!(count);
287    bind_func_no_params!(is_null);
288    bind_func_no_params!(is_not_null);
289
290    /// Perform an operation if the column is null
291    fn if_null<V>(&self, v: V) -> SimpleExpr
292    where
293        V: Into<Value>,
294    {
295        Expr::col(self.as_column_ref()).if_null(v)
296    }
297
298    bind_vec_func!(is_in);
299    bind_vec_func!(is_not_in);
300
301    /// Postgres only.
302    /// ```
303    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
304    ///
305    /// assert_eq!(
306    ///     cake::Entity::find()
307    ///         .filter(cake::Column::Id.eq_any(vec![4, 5]))
308    ///         .build(DbBackend::Postgres)
309    ///         .to_string(),
310    ///     r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = ANY(ARRAY [4,5])"#
311    /// );
312    /// ```
313    #[cfg(feature = "postgres-array")]
314    fn eq_any<V, I>(&self, v: I) -> SimpleExpr
315    where
316        V: Into<Value> + sea_query::ValueType + sea_query::with_array::NotU8,
317        I: IntoIterator<Item = V>,
318    {
319        use sea_query::extension::postgres::PgFunc;
320
321        let vec: Vec<_> = v.into_iter().collect();
322        Expr::col(self.as_column_ref()).eq(PgFunc::any(vec))
323    }
324
325    bind_subquery_func!(in_subquery);
326    bind_subquery_func!(not_in_subquery);
327
328    /// Construct a [`SimpleExpr::Column`] wrapped in [`Expr`].
329    fn into_expr(self) -> Expr {
330        self.into_simple_expr()
331    }
332
333    /// Construct a returning [`Expr`].
334    #[allow(clippy::match_single_binding)]
335    fn into_returning_expr(self, db_backend: DbBackend) -> Expr {
336        match db_backend {
337            _ => Expr::col(self),
338        }
339    }
340
341    /// Cast column expression used in select statement.
342    /// It only cast database enum as text if it's an enum column.
343    fn select_as(&self, expr: Expr) -> SimpleExpr {
344        self.select_enum_as(expr)
345    }
346
347    /// Cast enum column as text; do nothing if `self` is not an enum.
348    fn select_enum_as(&self, expr: Expr) -> SimpleExpr {
349        cast_enum_as(expr, &self.def(), select_enum_as)
350    }
351
352    /// Cast value of a column into the correct type for database storage.
353    /// By default, it only cast text as enum type if it's an enum column.
354    fn save_as(&self, val: Expr) -> SimpleExpr {
355        self.save_enum_as(val)
356    }
357
358    /// Cast value of an enum column as enum type; do nothing if `self` is not an enum.
359    fn save_enum_as(&self, val: Expr) -> SimpleExpr {
360        cast_enum_as(val, &self.def(), save_enum_as)
361    }
362}
363
364/// SeaORM's utility methods that act on [ColumnType]
365pub trait ColumnTypeTrait {
366    /// Instantiate a new [ColumnDef]
367    fn def(self) -> ColumnDef;
368
369    /// Get the name of the enum if this is a enum column
370    fn get_enum_name(&self) -> Option<&DynIden>;
371}
372
373impl ColumnTypeTrait for ColumnType {
374    fn def(self) -> ColumnDef {
375        ColumnDef {
376            col_type: self,
377            null: false,
378            unique: false,
379            indexed: false,
380            default: None,
381            comment: None,
382            unique_key: None,
383            renamed_from: None,
384            seaography: Default::default(),
385        }
386    }
387
388    fn get_enum_name(&self) -> Option<&DynIden> {
389        enum_name(self)
390    }
391}
392
393impl ColumnTypeTrait for ColumnDef {
394    fn def(self) -> ColumnDef {
395        self
396    }
397
398    fn get_enum_name(&self) -> Option<&DynIden> {
399        enum_name(&self.col_type)
400    }
401}
402
403fn enum_name(col_type: &ColumnType) -> Option<&DynIden> {
404    match col_type {
405        ColumnType::Enum { name, .. } => Some(name),
406        ColumnType::Array(col_type) => enum_name(col_type),
407        _ => None,
408    }
409}
410
411struct Text;
412struct TextArray;
413
414impl Iden for Text {
415    fn quoted(&self) -> Cow<'static, str> {
416        Cow::Borrowed("text")
417    }
418
419    fn unquoted(&self) -> &str {
420        match self.quoted() {
421            Cow::Borrowed(s) => s,
422            _ => unreachable!(),
423        }
424    }
425}
426
427impl Iden for TextArray {
428    fn quoted(&self) -> Cow<'static, str> {
429        // This is Postgres only and it has a special handling for quoting this
430        Cow::Borrowed("text[]")
431    }
432
433    fn unquoted(&self) -> &str {
434        match self.quoted() {
435            Cow::Borrowed(s) => s,
436            _ => unreachable!(),
437        }
438    }
439}
440
441pub(crate) fn select_enum_as(col: Expr, _: DynIden, col_type: &ColumnType) -> SimpleExpr {
442    let type_name = match col_type {
443        ColumnType::Array(_) => TextArray.into_iden(),
444        _ => Text.into_iden(),
445    };
446    col.as_enum(type_name)
447}
448
449pub(crate) fn save_enum_as(col: Expr, enum_name: DynIden, col_type: &ColumnType) -> SimpleExpr {
450    let type_name = match col_type {
451        ColumnType::Array(_) => format!("{enum_name}[]").into_iden(),
452        _ => enum_name,
453    };
454    col.as_enum(type_name)
455}
456
457pub(crate) fn cast_enum_as<F>(expr: Expr, col_def: &ColumnDef, f: F) -> SimpleExpr
458where
459    F: Fn(Expr, DynIden, &ColumnType) -> SimpleExpr,
460{
461    let col_type = col_def.get_column_type();
462
463    match col_type {
464        #[cfg(all(feature = "with-json", feature = "postgres-array"))]
465        ColumnType::Json | ColumnType::JsonBinary => {
466            use sea_query::ArrayType;
467            use serde_json::Value as Json;
468
469            match expr {
470                SimpleExpr::Value(Value::Array(ArrayType::Json, Some(json_vec))) => {
471                    // flatten Array(Vec<Json>) into Json
472                    let json_vec: Vec<Json> = json_vec
473                        .into_iter()
474                        .filter_map(|val| match val {
475                            Value::Json(Some(json)) => Some(json),
476                            _ => None,
477                        })
478                        .collect();
479                    SimpleExpr::Value(Value::Json(Some(json_vec.into())))
480                }
481                SimpleExpr::Value(Value::Array(ArrayType::Json, None)) => {
482                    SimpleExpr::Value(Value::Json(None))
483                }
484                _ => expr,
485            }
486        }
487        _ => match col_type.get_enum_name() {
488            Some(enum_name) => f(expr, enum_name.clone(), col_type),
489            None => expr,
490        },
491    }
492}
493
494#[cfg(test)]
495mod tests {
496    use crate::{
497        ColumnTrait, Condition, DbBackend, EntityTrait, QueryFilter, QueryTrait, tests_cfg::*,
498    };
499    use sea_query::Query;
500
501    #[test]
502    fn test_in_subquery_1() {
503        assert_eq!(
504            cake::Entity::find()
505                .filter(
506                    Condition::any().add(
507                        cake::Column::Id.in_subquery(
508                            Query::select()
509                                .expr(cake::Column::Id.max())
510                                .from(cake::Entity)
511                                .to_owned()
512                        )
513                    )
514                )
515                .build(DbBackend::MySql)
516                .to_string(),
517            [
518                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
519                "WHERE `cake`.`id` IN (SELECT MAX(`cake`.`id`) FROM `cake`)",
520            ]
521            .join(" ")
522        );
523    }
524
525    #[test]
526    fn test_in_subquery_2() {
527        assert_eq!(
528            cake::Entity::find()
529                .filter(
530                    Condition::any().add(
531                        cake::Column::Id.in_subquery(
532                            Query::select()
533                                .column(cake_filling::Column::CakeId)
534                                .from(cake_filling::Entity)
535                                .to_owned()
536                        )
537                    )
538                )
539                .build(DbBackend::MySql)
540                .to_string(),
541            [
542                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
543                "WHERE `cake`.`id` IN (SELECT `cake_id` FROM `cake_filling`)",
544            ]
545            .join(" ")
546        );
547    }
548
549    #[test]
550    #[cfg(feature = "macros")]
551    fn select_as_1() {
552        use crate::{ActiveModelTrait, ActiveValue, Update};
553
554        mod hello_expanded {
555            use crate as sea_orm;
556            use crate::entity::prelude::*;
557            use crate::sea_query::{Expr, ExprTrait, SimpleExpr};
558
559            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
560            pub struct Entity;
561
562            impl EntityName for Entity {
563                fn table_name(&self) -> &'static str {
564                    "hello"
565                }
566            }
567
568            #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
569            pub struct Model {
570                pub id: i32,
571                #[sea_orm(enum_name = "One1")]
572                pub one: i32,
573                pub two: i32,
574                #[sea_orm(enum_name = "Three3")]
575                pub three: i32,
576            }
577
578            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
579            pub enum Column {
580                Id,
581                One1,
582                Two,
583                Three3,
584            }
585
586            impl ColumnTrait for Column {
587                type EntityName = Entity;
588
589                fn def(&self) -> ColumnDef {
590                    match self {
591                        Column::Id => ColumnType::Integer.def(),
592                        Column::One1 => ColumnType::Integer.def(),
593                        Column::Two => ColumnType::Integer.def(),
594                        Column::Three3 => ColumnType::Integer.def(),
595                    }
596                }
597
598                fn select_as(&self, expr: Expr) -> SimpleExpr {
599                    match self {
600                        Self::Two => expr.cast_as("integer"),
601                        _ => self.select_enum_as(expr),
602                    }
603                }
604            }
605
606            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
607            pub enum PrimaryKey {
608                Id,
609            }
610
611            impl PrimaryKeyTrait for PrimaryKey {
612                type ValueType = i32;
613
614                fn auto_increment() -> bool {
615                    true
616                }
617            }
618
619            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
620            pub enum Relation {}
621
622            impl ActiveModelBehavior for ActiveModel {}
623        }
624
625        #[allow(clippy::enum_variant_names)]
626        mod hello_compact {
627            use crate as sea_orm;
628            use crate::entity::prelude::*;
629
630            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
631            #[sea_orm(table_name = "hello")]
632            pub struct Model {
633                #[sea_orm(primary_key)]
634                pub id: i32,
635                #[sea_orm(enum_name = "One1")]
636                pub one: i32,
637                #[sea_orm(select_as = "integer")]
638                pub two: i32,
639                #[sea_orm(enum_name = "Three3")]
640                pub three: i32,
641            }
642
643            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
644            pub enum Relation {}
645
646            impl ActiveModelBehavior for ActiveModel {}
647        }
648
649        fn assert_it<E, A>(active_model: A)
650        where
651            E: EntityTrait,
652            A: ActiveModelTrait<Entity = E>,
653        {
654            assert_eq!(
655                E::find().build(DbBackend::Postgres).to_string(),
656                r#"SELECT "hello"."id", "hello"."one1", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello""#,
657            );
658            assert_eq!(
659                Update::one(active_model)
660                    .validate()
661                    .unwrap()
662                    .build(DbBackend::Postgres)
663                    .to_string(),
664                r#"UPDATE "hello" SET "one1" = 1, "two" = 2, "three3" = 3 WHERE "hello"."id" = 1"#,
665            );
666        }
667
668        assert_it(hello_expanded::ActiveModel {
669            id: ActiveValue::set(1),
670            one: ActiveValue::set(1),
671            two: ActiveValue::set(2),
672            three: ActiveValue::set(3),
673        });
674        assert_it(hello_compact::ActiveModel {
675            id: ActiveValue::set(1),
676            one: ActiveValue::set(1),
677            two: ActiveValue::set(2),
678            three: ActiveValue::set(3),
679        });
680    }
681
682    #[test]
683    #[cfg(feature = "macros")]
684    fn save_as_1() {
685        use crate::{ActiveModelTrait, ActiveValue, Update};
686
687        mod hello_expanded {
688            use crate as sea_orm;
689            use crate::entity::prelude::*;
690            use crate::sea_query::{Expr, ExprTrait, SimpleExpr};
691
692            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
693            pub struct Entity;
694
695            impl EntityName for Entity {
696                fn table_name(&self) -> &'static str {
697                    "hello"
698                }
699            }
700
701            #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
702            pub struct Model {
703                pub id: i32,
704                #[sea_orm(enum_name = "One1")]
705                pub one: i32,
706                pub two: i32,
707                #[sea_orm(enum_name = "Three3")]
708                pub three: i32,
709            }
710
711            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
712            pub enum Column {
713                Id,
714                One1,
715                Two,
716                Three3,
717            }
718
719            impl ColumnTrait for Column {
720                type EntityName = Entity;
721
722                fn def(&self) -> ColumnDef {
723                    match self {
724                        Column::Id => ColumnType::Integer.def(),
725                        Column::One1 => ColumnType::Integer.def(),
726                        Column::Two => ColumnType::Integer.def(),
727                        Column::Three3 => ColumnType::Integer.def(),
728                    }
729                }
730
731                fn save_as(&self, val: Expr) -> SimpleExpr {
732                    match self {
733                        Self::Two => val.cast_as("text"),
734                        _ => self.save_enum_as(val),
735                    }
736                }
737            }
738
739            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
740            pub enum PrimaryKey {
741                Id,
742            }
743
744            impl PrimaryKeyTrait for PrimaryKey {
745                type ValueType = i32;
746
747                fn auto_increment() -> bool {
748                    true
749                }
750            }
751
752            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
753            pub enum Relation {}
754
755            impl ActiveModelBehavior for ActiveModel {}
756        }
757
758        #[allow(clippy::enum_variant_names)]
759        mod hello_compact {
760            use crate as sea_orm;
761            use crate::entity::prelude::*;
762
763            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
764            #[sea_orm(table_name = "hello")]
765            pub struct Model {
766                #[sea_orm(primary_key)]
767                pub id: i32,
768                #[sea_orm(enum_name = "One1")]
769                pub one: i32,
770                #[sea_orm(save_as = "text")]
771                pub two: i32,
772                #[sea_orm(enum_name = "Three3")]
773                pub three: i32,
774            }
775
776            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
777            pub enum Relation {}
778
779            impl ActiveModelBehavior for ActiveModel {}
780        }
781
782        fn assert_it<E, A>(active_model: A)
783        where
784            E: EntityTrait,
785            A: ActiveModelTrait<Entity = E>,
786        {
787            assert_eq!(
788                E::find().build(DbBackend::Postgres).to_string(),
789                r#"SELECT "hello"."id", "hello"."one1", "hello"."two", "hello"."three3" FROM "hello""#,
790            );
791            assert_eq!(
792                Update::one(active_model)
793                    .validate()
794                    .unwrap()
795                    .build(DbBackend::Postgres)
796                    .to_string(),
797                r#"UPDATE "hello" SET "one1" = 1, "two" = CAST(2 AS text), "three3" = 3 WHERE "hello"."id" = 1"#,
798            );
799        }
800
801        assert_it(hello_expanded::ActiveModel {
802            id: ActiveValue::set(1),
803            one: ActiveValue::set(1),
804            two: ActiveValue::set(2),
805            three: ActiveValue::set(3),
806        });
807        assert_it(hello_compact::ActiveModel {
808            id: ActiveValue::set(1),
809            one: ActiveValue::set(1),
810            two: ActiveValue::set(2),
811            three: ActiveValue::set(3),
812        });
813    }
814
815    #[test]
816    #[cfg(feature = "macros")]
817    fn select_as_and_value_1() {
818        use crate::{ActiveModelTrait, ActiveValue, Update};
819
820        mod hello_expanded {
821            use crate as sea_orm;
822            use crate::entity::prelude::*;
823            use crate::sea_query::{Expr, ExprTrait, SimpleExpr};
824
825            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
826            pub struct Entity;
827
828            impl EntityName for Entity {
829                fn table_name(&self) -> &'static str {
830                    "hello"
831                }
832            }
833
834            #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
835            pub struct Model {
836                pub id: i32,
837                #[sea_orm(enum_name = "One1")]
838                pub one: i32,
839                pub two: i32,
840                #[sea_orm(enum_name = "Three3")]
841                pub three: i32,
842            }
843
844            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
845            pub enum Column {
846                Id,
847                One1,
848                Two,
849                Three3,
850            }
851
852            impl ColumnTrait for Column {
853                type EntityName = Entity;
854
855                fn def(&self) -> ColumnDef {
856                    match self {
857                        Column::Id => ColumnType::Integer.def(),
858                        Column::One1 => ColumnType::Integer.def(),
859                        Column::Two => ColumnType::Integer.def(),
860                        Column::Three3 => ColumnType::Integer.def(),
861                    }
862                }
863
864                fn select_as(&self, expr: Expr) -> SimpleExpr {
865                    match self {
866                        Self::Two => expr.cast_as("integer"),
867                        _ => self.select_enum_as(expr),
868                    }
869                }
870
871                fn save_as(&self, val: Expr) -> SimpleExpr {
872                    match self {
873                        Self::Two => val.cast_as("text"),
874                        _ => self.save_enum_as(val),
875                    }
876                }
877            }
878
879            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
880            pub enum PrimaryKey {
881                Id,
882            }
883
884            impl PrimaryKeyTrait for PrimaryKey {
885                type ValueType = i32;
886
887                fn auto_increment() -> bool {
888                    true
889                }
890            }
891
892            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
893            pub enum Relation {}
894
895            impl ActiveModelBehavior for ActiveModel {}
896        }
897
898        #[allow(clippy::enum_variant_names)]
899        mod hello_compact {
900            use crate as sea_orm;
901            use crate::entity::prelude::*;
902
903            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
904            #[sea_orm(table_name = "hello")]
905            pub struct Model {
906                #[sea_orm(primary_key)]
907                pub id: i32,
908                #[sea_orm(enum_name = "One1")]
909                pub one: i32,
910                #[sea_orm(select_as = "integer", save_as = "text")]
911                pub two: i32,
912                #[sea_orm(enum_name = "Three3")]
913                pub three: i32,
914            }
915
916            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
917            pub enum Relation {}
918
919            impl ActiveModelBehavior for ActiveModel {}
920        }
921
922        fn assert_it<E, A>(active_model: A)
923        where
924            E: EntityTrait,
925            A: ActiveModelTrait<Entity = E>,
926        {
927            assert_eq!(
928                E::find().build(DbBackend::Postgres).to_string(),
929                r#"SELECT "hello"."id", "hello"."one1", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello""#,
930            );
931            assert_eq!(
932                Update::one(active_model)
933                    .validate()
934                    .unwrap()
935                    .build(DbBackend::Postgres)
936                    .to_string(),
937                r#"UPDATE "hello" SET "one1" = 1, "two" = CAST(2 AS text), "three3" = 3 WHERE "hello"."id" = 1"#,
938            );
939        }
940
941        assert_it(hello_expanded::ActiveModel {
942            id: ActiveValue::set(1),
943            one: ActiveValue::set(1),
944            two: ActiveValue::set(2),
945            three: ActiveValue::set(3),
946        });
947        assert_it(hello_compact::ActiveModel {
948            id: ActiveValue::set(1),
949            one: ActiveValue::set(1),
950            two: ActiveValue::set(2),
951            three: ActiveValue::set(3),
952        });
953    }
954}