sea_orm/entity/
column.rs

1use crate::{
2    ColumnDef, ColumnType, DbBackend, EntityName, Iden, IdenStatic, IntoSimpleExpr, Iterable,
3};
4#[cfg(feature = "postgres-array")]
5use sea_query::ArrayElement;
6use sea_query::{
7    BinOper, DynIden, Expr, ExprTrait, IntoIden, IntoLikeExpr, SeaRc, SelectStatement, Value,
8};
9use std::{borrow::Cow, str::FromStr};
10
11mod types;
12pub use types::*;
13
14pub(crate) mod macros {
15    macro_rules! bind_oper {
16        ($vis:vis $op:ident, $bin_op:ident) => {
17            #[allow(missing_docs)]
18            $vis fn $op<V>(&self, v: V) -> Expr
19            where
20                V: Into<Value>,
21            {
22                let expr = self.save_as(Expr::val(v));
23                Expr::col(self.as_column_ref()).binary(BinOper::$bin_op, expr)
24            }
25        };
26    }
27
28    macro_rules! bind_func_no_params {
29        ($vis:vis $func:ident) => {
30            /// See also SeaQuery's method with same name.
31            $vis fn $func(&self) -> Expr {
32                Expr::col(self.as_column_ref()).$func()
33            }
34        };
35    }
36
37    macro_rules! bind_vec_func {
38        ($vis:vis $func:ident) => {
39            #[allow(missing_docs)]
40            #[allow(clippy::wrong_self_convention)]
41            $vis fn $func<V, I>(&self, v: I) -> Expr
42            where
43                V: Into<Value>,
44                I: IntoIterator<Item = V>,
45            {
46                let v_with_enum_cast = v.into_iter().map(|v| self.save_as(Expr::val(v)));
47                Expr::col(self.as_column_ref()).$func(v_with_enum_cast)
48            }
49        };
50    }
51
52    macro_rules! bind_subquery_func {
53        ($vis:vis $func:ident) => {
54            #[allow(clippy::wrong_self_convention)]
55            #[allow(missing_docs)]
56            $vis fn $func(&self, s: SelectStatement) -> Expr {
57                Expr::col(self.as_column_ref()).$func(s)
58            }
59        };
60    }
61
62    macro_rules! bind_array_oper {
63        ($vis:vis $op:ident, $oper:ident) => {
64            #[cfg(feature = "postgres-array")]
65            /// Array operator. Postgres only.
66            $vis fn $op<A>(&self, arr: A) -> Expr
67            where
68                A: Into<sea_query::value::Array>,
69            {
70                use sea_query::extension::postgres::PgBinOper;
71
72                let value = Value::Array(arr.into());
73                Expr::col(self.as_column_ref()).binary(PgBinOper::$oper, self.save_as(Expr::val(value)))
74            }
75        };
76    }
77
78    pub(crate) use bind_array_oper;
79    pub(crate) use bind_func_no_params;
80    pub(crate) use bind_oper;
81    pub(crate) use bind_subquery_func;
82    pub(crate) use bind_vec_func;
83}
84
85use macros::*;
86
87/// API for working with a `Column`. Mostly a wrapper of the identically named methods in [`sea_query::Expr`]
88pub trait ColumnTrait: IdenStatic + Iterable + FromStr {
89    #[allow(missing_docs)]
90    type EntityName: EntityName;
91
92    /// Get the column definition with SQL attributes
93    fn def(&self) -> ColumnDef;
94
95    /// Get the enum type name if this is a enum column
96    fn enum_type_name(&self) -> Option<&'static str> {
97        None
98    }
99
100    /// Get the name of the entity the column belongs to
101    fn entity_name(&self) -> DynIden {
102        SeaRc::new(Self::EntityName::default())
103    }
104
105    /// Get the table.column reference
106    fn as_column_ref(&self) -> (DynIden, DynIden) {
107        (self.entity_name(), SeaRc::new(*self))
108    }
109
110    /// Perform equality against a Value. `None` will be converted to `IS NULL`.
111    /// ```
112    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::fruit};
113    ///
114    /// assert_eq!(
115    ///     fruit::Entity::find()
116    ///         .filter(fruit::COLUMN.cake_id.eq(2))
117    ///         .build(DbBackend::MySql)
118    ///         .to_string(),
119    ///     "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`cake_id` = 2"
120    /// );
121    /// assert_eq!(
122    ///     fruit::Entity::find()
123    ///         .filter(fruit::COLUMN.cake_id.eq(Option::<i32>::None))
124    ///         .build(DbBackend::MySql)
125    ///         .to_string(),
126    ///     "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`cake_id` IS NULL"
127    /// );
128    /// ```
129    fn eq<V>(&self, v: V) -> Expr
130    where
131        V: Into<Value>,
132    {
133        let v = v.into();
134        if v == v.as_null() {
135            Expr::col(self.as_column_ref()).is_null()
136        } else {
137            let expr = self.save_as(Expr::val(v));
138            Expr::col(self.as_column_ref()).eq(expr)
139        }
140    }
141
142    /// Perform inequality against a Value. `None` will be converted to `IS NOT NULL`.
143    /// ```
144    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::fruit};
145    ///
146    /// assert_eq!(
147    ///     fruit::Entity::find()
148    ///         .filter(fruit::COLUMN.cake_id.ne(2))
149    ///         .build(DbBackend::MySql)
150    ///         .to_string(),
151    ///     "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`cake_id` <> 2"
152    /// );
153    /// assert_eq!(
154    ///     fruit::Entity::find()
155    ///         .filter(fruit::COLUMN.cake_id.ne(Option::<i32>::None))
156    ///         .build(DbBackend::MySql)
157    ///         .to_string(),
158    ///     "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit` WHERE `fruit`.`cake_id` IS NOT NULL"
159    /// );
160    /// ```
161    fn ne<V>(&self, v: V) -> Expr
162    where
163        V: Into<Value>,
164    {
165        let v = v.into();
166        if v == v.as_null() {
167            Expr::col(self.as_column_ref()).is_not_null()
168        } else {
169            let expr = self.save_as(Expr::val(v));
170            Expr::col(self.as_column_ref()).ne(expr)
171        }
172    }
173
174    bind_oper!(gt, GreaterThan);
175    bind_oper!(gte, GreaterThanOrEqual);
176    bind_oper!(lt, SmallerThan);
177    bind_oper!(lte, SmallerThanOrEqual);
178
179    /// ```
180    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
181    ///
182    /// assert_eq!(
183    ///     cake::Entity::find()
184    ///         .filter(cake::Column::Id.between(2, 3))
185    ///         .build(DbBackend::MySql)
186    ///         .to_string(),
187    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` BETWEEN 2 AND 3"
188    /// );
189    /// ```
190    fn between<V>(&self, a: V, b: V) -> Expr
191    where
192        V: Into<Value>,
193    {
194        Expr::col(self.as_column_ref()).between(a, b)
195    }
196
197    /// ```
198    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
199    ///
200    /// assert_eq!(
201    ///     cake::Entity::find()
202    ///         .filter(cake::Column::Id.not_between(2, 3))
203    ///         .build(DbBackend::MySql)
204    ///         .to_string(),
205    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`id` NOT BETWEEN 2 AND 3"
206    /// );
207    /// ```
208    fn not_between<V>(&self, a: V, b: V) -> Expr
209    where
210        V: Into<Value>,
211    {
212        Expr::col(self.as_column_ref()).not_between(a, b)
213    }
214
215    /// ```
216    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
217    ///
218    /// assert_eq!(
219    ///     cake::Entity::find()
220    ///         .filter(cake::Column::Name.like("cheese"))
221    ///         .build(DbBackend::MySql)
222    ///         .to_string(),
223    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese'"
224    /// );
225    /// ```
226    fn like<T>(&self, s: T) -> Expr
227    where
228        T: IntoLikeExpr,
229    {
230        Expr::col(self.as_column_ref()).like(s)
231    }
232
233    /// ```
234    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
235    ///
236    /// assert_eq!(
237    ///     cake::Entity::find()
238    ///         .filter(cake::Column::Name.not_like("cheese"))
239    ///         .build(DbBackend::MySql)
240    ///         .to_string(),
241    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` NOT LIKE 'cheese'"
242    /// );
243    /// ```
244    fn not_like<T>(&self, s: T) -> Expr
245    where
246        T: IntoLikeExpr,
247    {
248        Expr::col(self.as_column_ref()).not_like(s)
249    }
250
251    /// Postgres Only.
252    /// ```
253    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
254    ///
255    /// assert_eq!(
256    ///     cake::Entity::find()
257    ///         .filter(cake::Column::Name.ilike("cheese"))
258    ///         .build(DbBackend::Postgres)
259    ///         .to_string(),
260    ///     r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."name" ILIKE 'cheese'"#
261    /// );
262    /// ```
263    fn ilike<T>(&self, s: T) -> Expr
264    where
265        T: IntoLikeExpr,
266    {
267        use sea_query::extension::postgres::PgExpr;
268
269        Expr::col(self.as_column_ref()).ilike(s)
270    }
271
272    /// Postgres Only.
273    /// ```
274    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
275    ///
276    /// assert_eq!(
277    ///     cake::Entity::find()
278    ///         .filter(cake::Column::Name.not_ilike("cheese"))
279    ///         .build(DbBackend::Postgres)
280    ///         .to_string(),
281    ///     r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."name" NOT ILIKE 'cheese'"#
282    /// );
283    /// ```
284    fn not_ilike<T>(&self, s: T) -> Expr
285    where
286        T: IntoLikeExpr,
287    {
288        use sea_query::extension::postgres::PgExpr;
289
290        Expr::col(self.as_column_ref()).not_ilike(s)
291    }
292
293    /// This is a simplified shorthand for a more general `like` method.
294    /// Use `like` if you need something more complex, like specifying an escape character.
295    ///
296    /// ## Examples
297    ///
298    /// ```
299    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
300    ///
301    /// assert_eq!(
302    ///     cake::Entity::find()
303    ///         .filter(cake::Column::Name.starts_with("cheese"))
304    ///         .build(DbBackend::MySql)
305    ///         .to_string(),
306    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese%'"
307    /// );
308    /// ```
309    fn starts_with<T>(&self, s: T) -> Expr
310    where
311        T: Into<String>,
312    {
313        let pattern = format!("{}%", s.into());
314        Expr::col(self.as_column_ref()).like(pattern)
315    }
316
317    /// This is a simplified shorthand for a more general `like` method.
318    /// Use `like` if you need something more complex, like specifying an escape character.
319    ///
320    /// ## Examples
321    ///
322    /// ```
323    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
324    ///
325    /// assert_eq!(
326    ///     cake::Entity::find()
327    ///         .filter(cake::Column::Name.ends_with("cheese"))
328    ///         .build(DbBackend::MySql)
329    ///         .to_string(),
330    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese'"
331    /// );
332    /// ```
333    fn ends_with<T>(&self, s: T) -> Expr
334    where
335        T: Into<String>,
336    {
337        let pattern = format!("%{}", s.into());
338        Expr::col(self.as_column_ref()).like(pattern)
339    }
340
341    /// This is a simplified shorthand for a more general `like` method.
342    /// Use `like` if you need something more complex, like specifying an escape character.
343    ///
344    /// ## Examples
345    ///
346    /// ```
347    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
348    ///
349    /// assert_eq!(
350    ///     cake::Entity::find()
351    ///         .filter(cake::Column::Name.contains("cheese"))
352    ///         .build(DbBackend::MySql)
353    ///         .to_string(),
354    ///     "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese%'"
355    /// );
356    /// ```
357    fn contains<T>(&self, s: T) -> Expr
358    where
359        T: Into<String>,
360    {
361        let pattern = format!("%{}%", s.into());
362        Expr::col(self.as_column_ref()).like(pattern)
363    }
364
365    bind_func_no_params!(max);
366    bind_func_no_params!(min);
367    bind_func_no_params!(sum);
368    bind_func_no_params!(avg);
369    bind_func_no_params!(count);
370    bind_func_no_params!(is_null);
371    bind_func_no_params!(is_not_null);
372
373    /// Provide fallback value if the column is null (null coalescing)
374    fn if_null<V>(&self, v: V) -> Expr
375    where
376        V: Into<Value>,
377    {
378        Expr::col(self.as_column_ref()).if_null(v)
379    }
380
381    bind_vec_func!(is_in);
382    bind_vec_func!(is_not_in);
383
384    /// Postgres only.
385    /// ```
386    /// use sea_orm::{DbBackend, entity::*, query::*, tests_cfg::cake};
387    ///
388    /// assert_eq!(
389    ///     cake::Entity::find()
390    ///         .filter(cake::Column::Id.eq_any(vec![4, 5]))
391    ///         .build(DbBackend::Postgres)
392    ///         .to_string(),
393    ///     r#"SELECT "cake"."id", "cake"."name" FROM "cake" WHERE "cake"."id" = ANY(ARRAY[4,5])"#
394    /// );
395    /// ```
396    #[cfg(feature = "postgres-array")]
397    fn eq_any<T>(&self, arr: impl IntoIterator<Item = T>) -> Expr
398    where
399        T: ArrayElement,
400    {
401        use sea_query::extension::postgres::PgFunc;
402
403        let value = Value::Array(arr.into_iter().collect());
404        Expr::col(self.as_column_ref()).eq(PgFunc::any(Expr::val(value)))
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::value::{Array, ArrayType};
560            use serde_json::Value as Json;
561
562            match expr {
563                Expr::Value(Value::Array(Array::Json(json_vec))) => {
564                    // flatten Array(Vec<Json>) into Json
565                    let json_vec: Vec<Json> = json_vec.into_vec().into_iter().flatten().collect();
566                    Expr::Value(Value::Json(Some(json_vec.into())))
567                }
568                Expr::Value(Value::Array(Array::Null(ArrayType::Json))) => {
569                    Expr::Value(Value::Json(None))
570                }
571                _ => expr,
572            }
573        }
574        _ => match col_type.get_enum_name() {
575            Some(enum_name) => f(expr, enum_name.clone(), col_type),
576            None => expr,
577        },
578    }
579}
580
581#[cfg(test)]
582mod tests {
583    use crate::{
584        ColumnTrait, Condition, DbBackend, EntityTrait, QueryFilter, QueryTrait, tests_cfg::*,
585    };
586    use sea_query::Query;
587
588    #[test]
589    fn test_in_subquery_1() {
590        assert_eq!(
591            cake::Entity::find()
592                .filter(
593                    Condition::any().add(
594                        cake::Column::Id.in_subquery(
595                            Query::select()
596                                .expr(cake::Column::Id.max())
597                                .from(cake::Entity)
598                                .to_owned()
599                        )
600                    )
601                )
602                .build(DbBackend::MySql)
603                .to_string(),
604            [
605                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
606                "WHERE `cake`.`id` IN (SELECT MAX(`cake`.`id`) FROM `cake`)",
607            ]
608            .join(" ")
609        );
610    }
611
612    #[test]
613    fn test_in_subquery_2() {
614        assert_eq!(
615            cake::Entity::find()
616                .filter(
617                    Condition::any().add(
618                        cake::Column::Id.in_subquery(
619                            Query::select()
620                                .column(cake_filling::Column::CakeId)
621                                .from(cake_filling::Entity)
622                                .to_owned()
623                        )
624                    )
625                )
626                .build(DbBackend::MySql)
627                .to_string(),
628            [
629                "SELECT `cake`.`id`, `cake`.`name` FROM `cake`",
630                "WHERE `cake`.`id` IN (SELECT `cake_id` FROM `cake_filling`)",
631            ]
632            .join(" ")
633        );
634    }
635
636    #[test]
637    #[cfg(feature = "macros")]
638    fn select_as_1() {
639        use crate::{ActiveModelTrait, ActiveValue, Update};
640
641        mod hello_expanded {
642            use crate as sea_orm;
643            use crate::entity::prelude::*;
644            use crate::sea_query::{Expr, ExprTrait};
645
646            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
647            pub struct Entity;
648
649            impl EntityName for Entity {
650                fn table_name(&self) -> &'static str {
651                    "hello"
652                }
653            }
654
655            #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
656            pub struct Model {
657                pub id: i32,
658                #[sea_orm(enum_name = "One1")]
659                pub one: i32,
660                pub two: i32,
661                #[sea_orm(enum_name = "Three3")]
662                pub three: i32,
663            }
664
665            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
666            pub enum Column {
667                Id,
668                One1,
669                Two,
670                Three3,
671            }
672
673            impl ColumnTrait for Column {
674                type EntityName = Entity;
675
676                fn def(&self) -> ColumnDef {
677                    match self {
678                        Column::Id => ColumnType::Integer.def(),
679                        Column::One1 => ColumnType::Integer.def(),
680                        Column::Two => ColumnType::Integer.def(),
681                        Column::Three3 => ColumnType::Integer.def(),
682                    }
683                }
684
685                fn select_as(&self, expr: Expr) -> Expr {
686                    match self {
687                        Self::Two => expr.cast_as("integer"),
688                        _ => self.select_enum_as(expr),
689                    }
690                }
691            }
692
693            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
694            pub enum PrimaryKey {
695                Id,
696            }
697
698            impl PrimaryKeyTrait for PrimaryKey {
699                type ValueType = i32;
700
701                fn auto_increment() -> bool {
702                    true
703                }
704            }
705
706            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
707            pub enum Relation {}
708
709            impl ActiveModelBehavior for ActiveModel {}
710        }
711
712        #[allow(clippy::enum_variant_names)]
713        mod hello_compact {
714            use crate as sea_orm;
715            use crate::entity::prelude::*;
716
717            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
718            #[sea_orm(table_name = "hello")]
719            pub struct Model {
720                #[sea_orm(primary_key)]
721                pub id: i32,
722                #[sea_orm(enum_name = "One1")]
723                pub one: i32,
724                #[sea_orm(select_as = "integer")]
725                pub two: i32,
726                #[sea_orm(enum_name = "Three3")]
727                pub three: i32,
728            }
729
730            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
731            pub enum Relation {}
732
733            impl ActiveModelBehavior for ActiveModel {}
734        }
735
736        fn assert_it<E, A>(active_model: A)
737        where
738            E: EntityTrait,
739            A: ActiveModelTrait<Entity = E>,
740        {
741            assert_eq!(
742                E::find().build(DbBackend::Postgres).to_string(),
743                r#"SELECT "hello"."id", "hello"."one1", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello""#,
744            );
745            assert_eq!(
746                Update::one(active_model)
747                    .validate()
748                    .unwrap()
749                    .build(DbBackend::Postgres)
750                    .to_string(),
751                r#"UPDATE "hello" SET "one1" = 1, "two" = 2, "three3" = 3 WHERE "hello"."id" = 1"#,
752            );
753        }
754
755        assert_it(hello_expanded::ActiveModel {
756            id: ActiveValue::set(1),
757            one: ActiveValue::set(1),
758            two: ActiveValue::set(2),
759            three: ActiveValue::set(3),
760        });
761        assert_it(hello_compact::ActiveModel {
762            id: ActiveValue::set(1),
763            one: ActiveValue::set(1),
764            two: ActiveValue::set(2),
765            three: ActiveValue::set(3),
766        });
767    }
768
769    #[test]
770    #[cfg(feature = "macros")]
771    fn save_as_1() {
772        use crate::{ActiveModelTrait, ActiveValue, Update};
773
774        mod hello_expanded {
775            use crate as sea_orm;
776            use crate::entity::prelude::*;
777            use crate::sea_query::{Expr, ExprTrait};
778
779            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
780            pub struct Entity;
781
782            impl EntityName for Entity {
783                fn table_name(&self) -> &'static str {
784                    "hello"
785                }
786            }
787
788            #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
789            pub struct Model {
790                pub id: i32,
791                #[sea_orm(enum_name = "One1")]
792                pub one: i32,
793                pub two: i32,
794                #[sea_orm(enum_name = "Three3")]
795                pub three: i32,
796            }
797
798            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
799            pub enum Column {
800                Id,
801                One1,
802                Two,
803                Three3,
804            }
805
806            impl ColumnTrait for Column {
807                type EntityName = Entity;
808
809                fn def(&self) -> ColumnDef {
810                    match self {
811                        Column::Id => ColumnType::Integer.def(),
812                        Column::One1 => ColumnType::Integer.def(),
813                        Column::Two => ColumnType::Integer.def(),
814                        Column::Three3 => ColumnType::Integer.def(),
815                    }
816                }
817
818                fn save_as(&self, val: Expr) -> Expr {
819                    match self {
820                        Self::Two => val.cast_as("text"),
821                        _ => self.save_enum_as(val),
822                    }
823                }
824            }
825
826            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
827            pub enum PrimaryKey {
828                Id,
829            }
830
831            impl PrimaryKeyTrait for PrimaryKey {
832                type ValueType = i32;
833
834                fn auto_increment() -> bool {
835                    true
836                }
837            }
838
839            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
840            pub enum Relation {}
841
842            impl ActiveModelBehavior for ActiveModel {}
843        }
844
845        #[allow(clippy::enum_variant_names)]
846        mod hello_compact {
847            use crate as sea_orm;
848            use crate::entity::prelude::*;
849
850            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
851            #[sea_orm(table_name = "hello")]
852            pub struct Model {
853                #[sea_orm(primary_key)]
854                pub id: i32,
855                #[sea_orm(enum_name = "One1")]
856                pub one: i32,
857                #[sea_orm(save_as = "text")]
858                pub two: i32,
859                #[sea_orm(enum_name = "Three3")]
860                pub three: i32,
861            }
862
863            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
864            pub enum Relation {}
865
866            impl ActiveModelBehavior for ActiveModel {}
867        }
868
869        fn assert_it<E, A>(active_model: A)
870        where
871            E: EntityTrait,
872            A: ActiveModelTrait<Entity = E>,
873        {
874            assert_eq!(
875                E::find().build(DbBackend::Postgres).to_string(),
876                r#"SELECT "hello"."id", "hello"."one1", "hello"."two", "hello"."three3" FROM "hello""#,
877            );
878            assert_eq!(
879                Update::one(active_model)
880                    .validate()
881                    .unwrap()
882                    .build(DbBackend::Postgres)
883                    .to_string(),
884                r#"UPDATE "hello" SET "one1" = 1, "two" = CAST(2 AS text), "three3" = 3 WHERE "hello"."id" = 1"#,
885            );
886        }
887
888        assert_it(hello_expanded::ActiveModel {
889            id: ActiveValue::set(1),
890            one: ActiveValue::set(1),
891            two: ActiveValue::set(2),
892            three: ActiveValue::set(3),
893        });
894        assert_it(hello_compact::ActiveModel {
895            id: ActiveValue::set(1),
896            one: ActiveValue::set(1),
897            two: ActiveValue::set(2),
898            three: ActiveValue::set(3),
899        });
900    }
901
902    #[test]
903    #[cfg(feature = "macros")]
904    fn select_as_and_value_1() {
905        use crate::{ActiveModelTrait, ActiveValue, Update};
906
907        mod hello_expanded {
908            use crate as sea_orm;
909            use crate::entity::prelude::*;
910            use crate::sea_query::{Expr, ExprTrait};
911
912            #[derive(Copy, Clone, Default, Debug, DeriveEntity)]
913            pub struct Entity;
914
915            impl EntityName for Entity {
916                fn table_name(&self) -> &'static str {
917                    "hello"
918                }
919            }
920
921            #[derive(Clone, Debug, PartialEq, Eq, DeriveModel, DeriveActiveModel)]
922            pub struct Model {
923                pub id: i32,
924                #[sea_orm(enum_name = "One1")]
925                pub one: i32,
926                pub two: i32,
927                #[sea_orm(enum_name = "Three3")]
928                pub three: i32,
929            }
930
931            #[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
932            pub enum Column {
933                Id,
934                One1,
935                Two,
936                Three3,
937            }
938
939            impl ColumnTrait for Column {
940                type EntityName = Entity;
941
942                fn def(&self) -> ColumnDef {
943                    match self {
944                        Column::Id => ColumnType::Integer.def(),
945                        Column::One1 => ColumnType::Integer.def(),
946                        Column::Two => ColumnType::Integer.def(),
947                        Column::Three3 => ColumnType::Integer.def(),
948                    }
949                }
950
951                fn select_as(&self, expr: Expr) -> Expr {
952                    match self {
953                        Self::Two => expr.cast_as("integer"),
954                        _ => self.select_enum_as(expr),
955                    }
956                }
957
958                fn save_as(&self, val: Expr) -> Expr {
959                    match self {
960                        Self::Two => val.cast_as("text"),
961                        _ => self.save_enum_as(val),
962                    }
963                }
964            }
965
966            #[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
967            pub enum PrimaryKey {
968                Id,
969            }
970
971            impl PrimaryKeyTrait for PrimaryKey {
972                type ValueType = i32;
973
974                fn auto_increment() -> bool {
975                    true
976                }
977            }
978
979            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
980            pub enum Relation {}
981
982            impl ActiveModelBehavior for ActiveModel {}
983        }
984
985        #[allow(clippy::enum_variant_names)]
986        mod hello_compact {
987            use crate as sea_orm;
988            use crate::entity::prelude::*;
989
990            #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
991            #[sea_orm(table_name = "hello")]
992            pub struct Model {
993                #[sea_orm(primary_key)]
994                pub id: i32,
995                #[sea_orm(enum_name = "One1")]
996                pub one: i32,
997                #[sea_orm(select_as = "integer", save_as = "text")]
998                pub two: i32,
999                #[sea_orm(enum_name = "Three3")]
1000                pub three: i32,
1001            }
1002
1003            #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
1004            pub enum Relation {}
1005
1006            impl ActiveModelBehavior for ActiveModel {}
1007        }
1008
1009        fn assert_it<E, A>(active_model: A)
1010        where
1011            E: EntityTrait,
1012            A: ActiveModelTrait<Entity = E>,
1013        {
1014            assert_eq!(
1015                E::find().build(DbBackend::Postgres).to_string(),
1016                r#"SELECT "hello"."id", "hello"."one1", CAST("hello"."two" AS integer), "hello"."three3" FROM "hello""#,
1017            );
1018            assert_eq!(
1019                Update::one(active_model)
1020                    .validate()
1021                    .unwrap()
1022                    .build(DbBackend::Postgres)
1023                    .to_string(),
1024                r#"UPDATE "hello" SET "one1" = 1, "two" = CAST(2 AS text), "three3" = 3 WHERE "hello"."id" = 1"#,
1025            );
1026        }
1027
1028        assert_it(hello_expanded::ActiveModel {
1029            id: ActiveValue::set(1),
1030            one: ActiveValue::set(1),
1031            two: ActiveValue::set(2),
1032            three: ActiveValue::set(3),
1033        });
1034        assert_it(hello_compact::ActiveModel {
1035            id: ActiveValue::set(1),
1036            one: ActiveValue::set(1),
1037            two: ActiveValue::set(2),
1038            three: ActiveValue::set(3),
1039        });
1040    }
1041}