sea_query/extension/postgres/
types.rs

1use crate::{prepare::*, types::*, QueryBuilder, QuotedBuilder};
2
3/// Helper for constructing any type statement
4#[derive(Debug)]
5pub struct Type;
6
7#[derive(Clone, Debug)]
8pub enum TypeRef {
9    Type(DynIden),
10    SchemaType(DynIden, DynIden),
11    DatabaseSchemaType(DynIden, DynIden, DynIden),
12}
13
14pub trait IntoTypeRef {
15    fn into_type_ref(self) -> TypeRef;
16}
17
18impl IntoTypeRef for TypeRef {
19    fn into_type_ref(self) -> TypeRef {
20        self
21    }
22}
23
24impl<I> IntoTypeRef for I
25where
26    I: IntoIden,
27{
28    fn into_type_ref(self) -> TypeRef {
29        TypeRef::Type(self.into_iden())
30    }
31}
32
33impl<A, B> IntoTypeRef for (A, B)
34where
35    A: IntoIden,
36    B: IntoIden,
37{
38    fn into_type_ref(self) -> TypeRef {
39        TypeRef::SchemaType(self.0.into_iden(), self.1.into_iden())
40    }
41}
42
43impl<A, B, C> IntoTypeRef for (A, B, C)
44where
45    A: IntoIden,
46    B: IntoIden,
47    C: IntoIden,
48{
49    fn into_type_ref(self) -> TypeRef {
50        TypeRef::DatabaseSchemaType(self.0.into_iden(), self.1.into_iden(), self.2.into_iden())
51    }
52}
53
54#[derive(Debug, Clone, Default)]
55pub struct TypeCreateStatement {
56    pub(crate) name: Option<TypeRef>,
57    pub(crate) as_type: Option<TypeAs>,
58    pub(crate) values: Vec<DynIden>,
59}
60
61#[derive(Debug, Clone)]
62pub enum TypeAs {
63    // Composite,
64    Enum,
65    /* Range,
66     * Base,
67     * Array, */
68}
69
70#[derive(Debug, Clone, Default)]
71pub struct TypeDropStatement {
72    pub(crate) names: Vec<TypeRef>,
73    pub(crate) option: Option<TypeDropOpt>,
74    pub(crate) if_exists: bool,
75}
76
77#[derive(Debug, Clone, Default)]
78pub struct TypeAlterStatement {
79    pub(crate) name: Option<TypeRef>,
80    pub(crate) option: Option<TypeAlterOpt>,
81}
82
83#[derive(Debug, Clone)]
84pub enum TypeDropOpt {
85    Cascade,
86    Restrict,
87}
88
89#[derive(Debug, Clone)]
90pub enum TypeAlterOpt {
91    Add {
92        value: DynIden,
93        placement: Option<TypeAlterAddOpt>,
94        if_not_exists: bool,
95    },
96    Rename(DynIden),
97    RenameValue(DynIden, DynIden),
98}
99
100#[derive(Debug, Clone)]
101pub enum TypeAlterAddOpt {
102    Before(DynIden),
103    After(DynIden),
104}
105
106pub trait TypeBuilder: QuotedBuilder {
107    /// Translate [`TypeCreateStatement`] into database specific SQL statement.
108    fn prepare_type_create_statement(&self, create: &TypeCreateStatement, sql: &mut dyn SqlWriter);
109
110    /// Translate [`TypeDropStatement`] into database specific SQL statement.
111    fn prepare_type_drop_statement(&self, drop: &TypeDropStatement, sql: &mut dyn SqlWriter);
112
113    /// Translate [`TypeAlterStatement`] into database specific SQL statement.
114    fn prepare_type_alter_statement(&self, alter: &TypeAlterStatement, sql: &mut dyn SqlWriter);
115
116    /// Translate [`TypeRef`] into SQL statement.
117    fn prepare_type_ref(&self, type_ref: &TypeRef, sql: &mut dyn SqlWriter) {
118        match type_ref {
119            TypeRef::Type(name) => {
120                name.prepare(sql.as_writer(), self.quote());
121            }
122            TypeRef::SchemaType(schema, name) => {
123                schema.prepare(sql.as_writer(), self.quote());
124                write!(sql, ".").unwrap();
125                name.prepare(sql.as_writer(), self.quote());
126            }
127            TypeRef::DatabaseSchemaType(database, schema, name) => {
128                database.prepare(sql.as_writer(), self.quote());
129                write!(sql, ".").unwrap();
130                schema.prepare(sql.as_writer(), self.quote());
131                write!(sql, ".").unwrap();
132                name.prepare(sql.as_writer(), self.quote());
133            }
134        }
135    }
136}
137
138impl Type {
139    /// Construct type [`TypeCreateStatement`]
140    pub fn create() -> TypeCreateStatement {
141        TypeCreateStatement::new()
142    }
143
144    /// Construct type [`TypeDropStatement`]
145    pub fn drop() -> TypeDropStatement {
146        TypeDropStatement::new()
147    }
148
149    /// Construct type [`TypeAlterStatement`]
150    pub fn alter() -> TypeAlterStatement {
151        TypeAlterStatement::new()
152    }
153}
154
155impl TypeCreateStatement {
156    pub fn new() -> Self {
157        Self::default()
158    }
159
160    /// Create enum as custom type
161    ///
162    /// ```
163    /// use sea_query::{extension::postgres::Type, *};
164    ///
165    /// enum FontFamily {
166    ///     Type,
167    ///     Serif,
168    ///     Sans,
169    ///     Monospace,
170    /// }
171    ///
172    /// impl Iden for FontFamily {
173    ///     fn unquoted(&self, s: &mut dyn Write) {
174    ///         write!(
175    ///             s,
176    ///             "{}",
177    ///             match self {
178    ///                 Self::Type => "font_family",
179    ///                 Self::Serif => "serif",
180    ///                 Self::Sans => "sans",
181    ///                 Self::Monospace => "monospace",
182    ///             }
183    ///         )
184    ///         .unwrap();
185    ///     }
186    /// }
187    ///
188    /// assert_eq!(
189    ///     Type::create()
190    ///         .as_enum(FontFamily::Type)
191    ///         .values([FontFamily::Serif, FontFamily::Sans, FontFamily::Monospace])
192    ///         .to_string(PostgresQueryBuilder),
193    ///     r#"CREATE TYPE "font_family" AS ENUM ('serif', 'sans', 'monospace')"#
194    /// );
195    /// ```
196    pub fn as_enum<T>(&mut self, name: T) -> &mut Self
197    where
198        T: IntoTypeRef,
199    {
200        self.name = Some(name.into_type_ref());
201        self.as_type = Some(TypeAs::Enum);
202        self
203    }
204
205    pub fn values<T, I>(&mut self, values: I) -> &mut Self
206    where
207        T: IntoIden,
208        I: IntoIterator<Item = T>,
209    {
210        for v in values.into_iter() {
211            self.values.push(v.into_iden());
212        }
213        self
214    }
215}
216
217impl TypeDropStatement {
218    pub fn new() -> Self {
219        Self::default()
220    }
221
222    /// Drop a type
223    ///
224    /// ```
225    /// use sea_query::{extension::postgres::Type, *};
226    ///
227    /// struct FontFamily;
228    ///
229    /// impl Iden for FontFamily {
230    ///     fn unquoted(&self, s: &mut dyn Write) {
231    ///         write!(s, "{}", "font_family").unwrap();
232    ///     }
233    /// }
234    ///
235    /// assert_eq!(
236    ///     Type::drop()
237    ///         .if_exists()
238    ///         .name(FontFamily)
239    ///         .restrict()
240    ///         .to_string(PostgresQueryBuilder),
241    ///     r#"DROP TYPE IF EXISTS "font_family" RESTRICT"#
242    /// );
243    /// ```
244    pub fn name<T>(&mut self, name: T) -> &mut Self
245    where
246        T: IntoTypeRef,
247    {
248        self.names.push(name.into_type_ref());
249        self
250    }
251
252    /// Drop multiple types
253    ///
254    /// ```
255    /// use sea_query::{extension::postgres::Type, *};
256    ///
257    /// #[derive(Iden)]
258    /// enum KycStatus {
259    ///     #[iden = "kyc_status"]
260    ///     Type,
261    ///     Pending,
262    ///     Approved,
263    /// }
264    ///
265    /// #[derive(Iden)]
266    /// enum FontFamily {
267    ///     #[iden = "font_family"]
268    ///     Type,
269    ///     Aerial,
270    ///     Forte,
271    /// }
272    ///
273    /// assert_eq!(
274    ///     Type::drop()
275    ///         .if_exists()
276    ///         .names([
277    ///             SeaRc::new(KycStatus::Type) as DynIden,
278    ///             SeaRc::new(FontFamily::Type) as DynIden,
279    ///         ])
280    ///         .cascade()
281    ///         .to_string(PostgresQueryBuilder),
282    ///     r#"DROP TYPE IF EXISTS "kyc_status", "font_family" CASCADE"#
283    /// );
284    /// ```
285    pub fn names<T, I>(&mut self, names: I) -> &mut Self
286    where
287        T: IntoTypeRef,
288        I: IntoIterator<Item = T>,
289    {
290        for n in names.into_iter() {
291            self.names.push(n.into_type_ref());
292        }
293        self
294    }
295
296    /// Set `IF EXISTS`
297    pub fn if_exists(&mut self) -> &mut Self {
298        self.if_exists = true;
299        self
300    }
301
302    /// Set `CASCADE`
303    pub fn cascade(&mut self) -> &mut Self {
304        self.option = Some(TypeDropOpt::Cascade);
305        self
306    }
307
308    /// Set `RESTRICT`
309    pub fn restrict(&mut self) -> &mut Self {
310        self.option = Some(TypeDropOpt::Restrict);
311        self
312    }
313}
314
315impl TypeAlterStatement {
316    pub fn new() -> Self {
317        Self::default()
318    }
319
320    /// Change the definition of a type
321    ///
322    /// ```
323    /// use sea_query::{extension::postgres::Type, *};
324    ///
325    /// enum FontFamily {
326    ///     Type,
327    ///     Serif,
328    ///     Sans,
329    ///     Monospace,
330    /// }
331    ///
332    /// impl Iden for FontFamily {
333    ///     fn unquoted(&self, s: &mut dyn Write) {
334    ///         write!(
335    ///             s,
336    ///             "{}",
337    ///             match self {
338    ///                 Self::Type => "font_family",
339    ///                 Self::Serif => "serif",
340    ///                 Self::Sans => "sans",
341    ///                 Self::Monospace => "monospace",
342    ///             }
343    ///         )
344    ///         .unwrap();
345    ///     }
346    /// }
347    ///
348    /// assert_eq!(
349    ///     Type::alter()
350    ///         .name(FontFamily::Type)
351    ///         .add_value(Alias::new("cursive"))
352    ///         .to_string(PostgresQueryBuilder),
353    ///     r#"ALTER TYPE "font_family" ADD VALUE 'cursive'"#
354    /// );
355    /// ```
356    pub fn name<T>(mut self, name: T) -> Self
357    where
358        T: IntoTypeRef,
359    {
360        self.name = Some(name.into_type_ref());
361        self
362    }
363
364    pub fn add_value<T>(self, value: T) -> Self
365    where
366        T: IntoIden,
367    {
368        self.alter_option(TypeAlterOpt::Add {
369            value: value.into_iden(),
370            placement: None,
371            if_not_exists: false,
372        })
373    }
374
375    /// Add a enum value before an existing value
376    ///
377    /// ```
378    /// use sea_query::{extension::postgres::Type, tests_cfg::*, *};
379    ///
380    /// assert_eq!(
381    ///     Type::alter()
382    ///         .name(Font::Table)
383    ///         .add_value(Alias::new("weight"))
384    ///         .before(Font::Variant)
385    ///         .to_string(PostgresQueryBuilder),
386    ///     r#"ALTER TYPE "font" ADD VALUE 'weight' BEFORE 'variant'"#
387    /// )
388    /// ```
389    pub fn before<T>(mut self, value: T) -> Self
390    where
391        T: IntoIden,
392    {
393        if let Some(option) = self.option {
394            self.option = Some(option.before(value));
395        }
396        self
397    }
398
399    pub fn after<T>(mut self, value: T) -> Self
400    where
401        T: IntoIden,
402    {
403        if let Some(option) = self.option {
404            self.option = Some(option.after(value));
405        }
406        self
407    }
408
409    /// Add a enum value if not already exists
410    ///
411    /// ```
412    /// use sea_query::{extension::postgres::Type, tests_cfg::*, *};
413    ///
414    /// assert_eq!(
415    ///     Type::alter()
416    ///         .name(Font::Table)
417    ///         .add_value(Alias::new("weight"))
418    ///         .if_not_exists()
419    ///         .after(Font::Variant)
420    ///         .to_string(PostgresQueryBuilder),
421    ///     r#"ALTER TYPE "font" ADD VALUE IF NOT EXISTS 'weight' AFTER 'variant'"#
422    /// )
423    /// ```
424    pub fn if_not_exists(mut self) -> Self {
425        if let Some(option) = self.option {
426            self.option = Some(option.if_not_exists());
427        }
428        self
429    }
430
431    pub fn rename_to<T>(self, name: T) -> Self
432    where
433        T: IntoIden,
434    {
435        self.alter_option(TypeAlterOpt::Rename(name.into_iden()))
436    }
437
438    /// Rename a enum value
439    ///
440    /// ```
441    /// use sea_query::{extension::postgres::Type, tests_cfg::*, *};
442    ///
443    /// assert_eq!(
444    ///     Type::alter()
445    ///         .name(Font::Table)
446    ///         .rename_value(Alias::new("variant"), Alias::new("language"))
447    ///         .to_string(PostgresQueryBuilder),
448    ///     r#"ALTER TYPE "font" RENAME VALUE 'variant' TO 'language'"#
449    /// )
450    /// ```
451    pub fn rename_value<T, V>(self, existing: T, new_name: V) -> Self
452    where
453        T: IntoIden,
454        V: IntoIden,
455    {
456        self.alter_option(TypeAlterOpt::RenameValue(
457            existing.into_iden(),
458            new_name.into_iden(),
459        ))
460    }
461
462    fn alter_option(mut self, option: TypeAlterOpt) -> Self {
463        self.option = Some(option);
464        self
465    }
466}
467
468impl TypeAlterOpt {
469    /// Changes only `ADD VALUE x` options into `ADD VALUE x BEFORE` options, does nothing otherwise
470    pub fn before<T>(self, value: T) -> Self
471    where
472        T: IntoIden,
473    {
474        match self {
475            TypeAlterOpt::Add {
476                value: iden,
477                if_not_exists,
478                ..
479            } => Self::Add {
480                value: iden,
481                if_not_exists,
482                placement: Some(TypeAlterAddOpt::Before(value.into_iden())),
483            },
484            _ => self,
485        }
486    }
487
488    /// Changes only `ADD VALUE x` options into `ADD VALUE x AFTER` options, does nothing otherwise
489    pub fn after<T>(self, value: T) -> Self
490    where
491        T: IntoIden,
492    {
493        match self {
494            TypeAlterOpt::Add {
495                value: iden,
496                if_not_exists,
497                ..
498            } => Self::Add {
499                value: iden,
500                if_not_exists,
501                placement: Some(TypeAlterAddOpt::After(value.into_iden())),
502            },
503            _ => self,
504        }
505    }
506
507    /// Changes only `ADD VALUE x` options into `ADD VALUE IF NOT EXISTS x` options, does nothing otherwise
508    pub fn if_not_exists(self) -> Self {
509        match self {
510            TypeAlterOpt::Add {
511                value, placement, ..
512            } => Self::Add {
513                value,
514                placement,
515                if_not_exists: true,
516            },
517            _ => self,
518        }
519    }
520}
521
522macro_rules! impl_type_statement_builder {
523    ( $struct_name: ident, $func_name: ident ) => {
524        impl $struct_name {
525            pub fn build_ref<T: TypeBuilder>(&self, type_builder: &T) -> String {
526                let mut sql = String::with_capacity(256);
527                self.build_collect_ref(type_builder, &mut sql)
528            }
529
530            pub fn build_collect<T: TypeBuilder>(
531                &self,
532                type_builder: T,
533                sql: &mut dyn SqlWriter,
534            ) -> String {
535                self.build_collect_ref(&type_builder, sql)
536            }
537
538            pub fn build_collect_ref<T: TypeBuilder>(
539                &self,
540                type_builder: &T,
541                sql: &mut dyn SqlWriter,
542            ) -> String {
543                type_builder.$func_name(self, sql);
544                sql.to_string()
545            }
546
547            /// Build corresponding SQL statement and return SQL string
548            pub fn to_string<T>(&self, type_builder: T) -> String
549            where
550                T: TypeBuilder + QueryBuilder,
551            {
552                self.build_ref(&type_builder)
553            }
554        }
555    };
556}
557
558impl_type_statement_builder!(TypeCreateStatement, prepare_type_create_statement);
559impl_type_statement_builder!(TypeAlterStatement, prepare_type_alter_statement);
560impl_type_statement_builder!(TypeDropStatement, prepare_type_drop_statement);