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