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