Skip to main content

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