sea_query/extension/postgres/
types.rs

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