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