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