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);