Skip to main content

drizzle_types/mysql/ddl/
mod.rs

1//! Serializable MySQL DDL entities used by snapshots and migration planning.
2
3use crate::alloc_prelude::*;
4
5#[cfg(feature = "serde")]
6use crate::serde_helpers::{cow_from_string, cow_option_from_string, cow_vec_from_strings};
7
8/// Storage mode for a generated MySQL column.
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
12pub enum GeneratedType {
13    Virtual,
14    #[default]
15    Stored,
16}
17
18/// Generated-column metadata.
19#[derive(Clone, Debug, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
22pub struct Generated {
23    #[cfg_attr(
24        feature = "serde",
25        serde(rename = "as", deserialize_with = "cow_from_string")
26    )]
27    pub expression: Cow<'static, str>,
28    #[cfg_attr(feature = "serde", serde(rename = "type"))]
29    pub generation_type: GeneratedType,
30}
31
32impl Generated {
33    #[must_use]
34    pub fn stored(expression: impl Into<Cow<'static, str>>) -> Self {
35        Self {
36            expression: expression.into(),
37            generation_type: GeneratedType::Stored,
38        }
39    }
40
41    #[must_use]
42    pub fn virtual_column(expression: impl Into<Cow<'static, str>>) -> Self {
43        Self {
44            expression: expression.into(),
45            generation_type: GeneratedType::Virtual,
46        }
47    }
48}
49
50/// Values belonging to an inline `ENUM` or `SET` declaration.
51#[derive(Clone, Debug, Default, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
54pub struct InlineEnum {
55    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
56    pub values: Vec<Cow<'static, str>>,
57}
58
59impl InlineEnum {
60    #[must_use]
61    pub fn new(values: impl IntoIterator<Item = impl Into<String>>) -> Self {
62        Self {
63            values: values
64                .into_iter()
65                .map(|value| Cow::Owned(value.into()))
66                .collect(),
67        }
68    }
69}
70
71/// Structured inline type data retained beside the rendered SQL type.
72#[derive(Clone, Debug, PartialEq, Eq)]
73#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
74#[cfg_attr(
75    feature = "serde",
76    serde(tag = "kind", content = "definition", rename_all = "lowercase")
77)]
78pub enum InlineType {
79    Enum(InlineEnum),
80    Set(InlineEnum),
81}
82
83/// A table in the one selected MySQL database scope.
84#[derive(Clone, Debug, PartialEq, Eq)]
85#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
87pub struct Table {
88    #[cfg_attr(
89        feature = "serde",
90        serde(
91            default,
92            deserialize_with = "cow_option_from_string",
93            skip_serializing_if = "Option::is_none"
94        )
95    )]
96    pub database: Option<Cow<'static, str>>,
97    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
98    pub name: Cow<'static, str>,
99    #[cfg_attr(
100        feature = "serde",
101        serde(default, skip_serializing_if = "core::ops::Not::not")
102    )]
103    pub temporary: bool,
104    #[cfg_attr(
105        feature = "serde",
106        serde(
107            default,
108            deserialize_with = "cow_option_from_string",
109            skip_serializing_if = "Option::is_none"
110        )
111    )]
112    pub engine: Option<Cow<'static, str>>,
113    #[cfg_attr(
114        feature = "serde",
115        serde(
116            default,
117            deserialize_with = "cow_option_from_string",
118            skip_serializing_if = "Option::is_none"
119        )
120    )]
121    pub charset: Option<Cow<'static, str>>,
122    #[cfg_attr(
123        feature = "serde",
124        serde(
125            default,
126            deserialize_with = "cow_option_from_string",
127            skip_serializing_if = "Option::is_none"
128        )
129    )]
130    pub collation: Option<Cow<'static, str>>,
131    #[cfg_attr(
132        feature = "serde",
133        serde(
134            default,
135            deserialize_with = "cow_option_from_string",
136            skip_serializing_if = "Option::is_none"
137        )
138    )]
139    pub comment: Option<Cow<'static, str>>,
140    #[cfg_attr(
141        feature = "serde",
142        serde(default, skip_serializing_if = "Vec::is_empty")
143    )]
144    pub options: Vec<TableOption>,
145}
146
147impl Table {
148    #[must_use]
149    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
150        Self {
151            database: None,
152            name: name.into(),
153            temporary: false,
154            engine: None,
155            charset: None,
156            collation: None,
157            comment: None,
158            options: Vec::new(),
159        }
160    }
161}
162
163/// A column and its complete MySQL definition.
164#[derive(Clone, Debug, PartialEq, Eq)]
165#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
166#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
167pub struct Column {
168    #[cfg_attr(
169        feature = "serde",
170        serde(
171            default,
172            deserialize_with = "cow_option_from_string",
173            skip_serializing_if = "Option::is_none"
174        )
175    )]
176    pub database: Option<Cow<'static, str>>,
177    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
178    pub table: Cow<'static, str>,
179    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
180    pub name: Cow<'static, str>,
181    #[cfg_attr(
182        feature = "serde",
183        serde(rename = "type", deserialize_with = "cow_from_string")
184    )]
185    pub sql_type: Cow<'static, str>,
186    #[cfg_attr(feature = "serde", serde(default))]
187    pub not_null: bool,
188    #[cfg_attr(feature = "serde", serde(default))]
189    pub autoincrement: bool,
190    #[cfg_attr(feature = "serde", serde(default))]
191    pub primary_key: bool,
192    #[cfg_attr(feature = "serde", serde(default))]
193    pub unique: bool,
194    #[cfg_attr(
195        feature = "serde",
196        serde(
197            default,
198            deserialize_with = "cow_option_from_string",
199            skip_serializing_if = "Option::is_none"
200        )
201    )]
202    pub default: Option<Cow<'static, str>>,
203    #[cfg_attr(
204        feature = "serde",
205        serde(
206            default,
207            deserialize_with = "cow_option_from_string",
208            skip_serializing_if = "Option::is_none"
209        )
210    )]
211    pub on_update: Option<Cow<'static, str>>,
212    #[cfg_attr(
213        feature = "serde",
214        serde(default, skip_serializing_if = "Option::is_none")
215    )]
216    pub generated: Option<Generated>,
217    #[cfg_attr(
218        feature = "serde",
219        serde(default, skip_serializing_if = "Option::is_none")
220    )]
221    pub inline_type: Option<InlineType>,
222    #[cfg_attr(
223        feature = "serde",
224        serde(
225            default,
226            deserialize_with = "cow_option_from_string",
227            skip_serializing_if = "Option::is_none"
228        )
229    )]
230    pub charset: Option<Cow<'static, str>>,
231    #[cfg_attr(
232        feature = "serde",
233        serde(
234            default,
235            deserialize_with = "cow_option_from_string",
236            skip_serializing_if = "Option::is_none"
237        )
238    )]
239    pub collation: Option<Cow<'static, str>>,
240    #[cfg_attr(
241        feature = "serde",
242        serde(
243            default,
244            deserialize_with = "cow_option_from_string",
245            skip_serializing_if = "Option::is_none"
246        )
247    )]
248    pub comment: Option<Cow<'static, str>>,
249}
250
251impl Column {
252    #[must_use]
253    pub fn new(
254        table: impl Into<Cow<'static, str>>,
255        name: impl Into<Cow<'static, str>>,
256        sql_type: impl Into<Cow<'static, str>>,
257    ) -> Self {
258        Self {
259            database: None,
260            table: table.into(),
261            name: name.into(),
262            sql_type: sql_type.into(),
263            not_null: false,
264            autoincrement: false,
265            primary_key: false,
266            unique: false,
267            default: None,
268            on_update: None,
269            generated: None,
270            inline_type: None,
271            charset: None,
272            collation: None,
273            comment: None,
274        }
275    }
276}
277
278/// One index key part. Expressions are trusted schema SQL; plain column names
279/// are distinguished by `is_expression`.
280#[derive(Clone, Debug, PartialEq, Eq)]
281#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
282#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
283pub struct IndexColumn {
284    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
285    pub expression: Cow<'static, str>,
286    #[cfg_attr(feature = "serde", serde(default))]
287    pub is_expression: bool,
288    #[cfg_attr(
289        feature = "serde",
290        serde(default, skip_serializing_if = "Option::is_none")
291    )]
292    pub length: Option<u32>,
293    #[cfg_attr(
294        feature = "serde",
295        serde(default, skip_serializing_if = "Option::is_none")
296    )]
297    pub ascending: Option<bool>,
298}
299
300impl IndexColumn {
301    #[must_use]
302    pub fn column(name: impl Into<Cow<'static, str>>) -> Self {
303        Self {
304            expression: name.into(),
305            is_expression: false,
306            length: None,
307            ascending: None,
308        }
309    }
310
311    #[must_use]
312    pub fn expression(sql: impl Into<Cow<'static, str>>) -> Self {
313        Self {
314            expression: sql.into(),
315            is_expression: true,
316            length: None,
317            ascending: None,
318        }
319    }
320}
321
322/// A currently unsupported table option. Keeping it typed prevents accepted
323/// source metadata from being silently discarded by the planner.
324#[derive(Clone, Debug, PartialEq, Eq)]
325#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
326#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
327pub struct TableOption {
328    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
329    pub name: Cow<'static, str>,
330    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
331    pub value: Cow<'static, str>,
332}
333
334#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
335#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
336#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
337pub enum IndexMethod {
338    Btree,
339    Hash,
340}
341
342#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
343#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
344#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
345pub enum IndexAlgorithm {
346    Default,
347    Inplace,
348    Copy,
349}
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
352#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
353#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
354pub enum IndexLock {
355    Default,
356    None,
357    Shared,
358    Exclusive,
359}
360
361#[derive(Clone, Debug, PartialEq, Eq)]
362#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
363#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
364pub struct Index {
365    #[cfg_attr(
366        feature = "serde",
367        serde(
368            default,
369            deserialize_with = "cow_option_from_string",
370            skip_serializing_if = "Option::is_none"
371        )
372    )]
373    pub database: Option<Cow<'static, str>>,
374    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
375    pub table: Cow<'static, str>,
376    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
377    pub name: Cow<'static, str>,
378    pub columns: Vec<IndexColumn>,
379    #[cfg_attr(feature = "serde", serde(default))]
380    pub unique: bool,
381    #[cfg_attr(
382        feature = "serde",
383        serde(default, skip_serializing_if = "Option::is_none")
384    )]
385    pub using: Option<IndexMethod>,
386    #[cfg_attr(
387        feature = "serde",
388        serde(default, skip_serializing_if = "Option::is_none")
389    )]
390    pub algorithm: Option<IndexAlgorithm>,
391    #[cfg_attr(
392        feature = "serde",
393        serde(default, skip_serializing_if = "Option::is_none")
394    )]
395    pub lock: Option<IndexLock>,
396    #[cfg_attr(
397        feature = "serde",
398        serde(
399            default,
400            deserialize_with = "cow_option_from_string",
401            skip_serializing_if = "Option::is_none"
402        )
403    )]
404    pub comment: Option<Cow<'static, str>>,
405    #[cfg_attr(
406        feature = "serde",
407        serde(default, skip_serializing_if = "Option::is_none")
408    )]
409    pub visible: Option<bool>,
410}
411
412impl Index {
413    #[must_use]
414    pub fn new(
415        table: impl Into<Cow<'static, str>>,
416        name: impl Into<Cow<'static, str>>,
417        columns: Vec<IndexColumn>,
418    ) -> Self {
419        Self {
420            database: None,
421            table: table.into(),
422            name: name.into(),
423            columns,
424            unique: false,
425            using: None,
426            algorithm: None,
427            lock: None,
428            comment: None,
429            visible: None,
430        }
431    }
432}
433
434#[derive(Clone, Debug, PartialEq, Eq)]
435#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
436#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
437pub struct PrimaryKey {
438    #[cfg_attr(
439        feature = "serde",
440        serde(
441            default,
442            deserialize_with = "cow_option_from_string",
443            skip_serializing_if = "Option::is_none"
444        )
445    )]
446    pub database: Option<Cow<'static, str>>,
447    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
448    pub table: Cow<'static, str>,
449    #[cfg_attr(
450        feature = "serde",
451        serde(
452            default,
453            deserialize_with = "cow_option_from_string",
454            skip_serializing_if = "Option::is_none"
455        )
456    )]
457    pub name: Option<Cow<'static, str>>,
458    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
459    pub columns: Vec<Cow<'static, str>>,
460}
461
462impl PrimaryKey {
463    #[must_use]
464    pub fn new(
465        table: impl Into<Cow<'static, str>>,
466        columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
467    ) -> Self {
468        Self {
469            database: None,
470            table: table.into(),
471            name: Some(Cow::Borrowed("PRIMARY")),
472            columns: columns.into_iter().map(Into::into).collect(),
473        }
474    }
475}
476
477#[derive(Clone, Debug, PartialEq, Eq)]
478#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
479#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
480pub struct UniqueConstraint {
481    #[cfg_attr(
482        feature = "serde",
483        serde(
484            default,
485            deserialize_with = "cow_option_from_string",
486            skip_serializing_if = "Option::is_none"
487        )
488    )]
489    pub database: Option<Cow<'static, str>>,
490    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
491    pub table: Cow<'static, str>,
492    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
493    pub name: Cow<'static, str>,
494    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
495    pub columns: Vec<Cow<'static, str>>,
496}
497
498impl UniqueConstraint {
499    #[must_use]
500    pub fn new(
501        table: impl Into<Cow<'static, str>>,
502        name: impl Into<Cow<'static, str>>,
503        columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
504    ) -> Self {
505        Self {
506            database: None,
507            table: table.into(),
508            name: name.into(),
509            columns: columns.into_iter().map(Into::into).collect(),
510        }
511    }
512}
513
514#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
515#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
516pub enum ReferentialAction {
517    #[cfg_attr(feature = "serde", serde(rename = "CASCADE"))]
518    Cascade,
519    #[cfg_attr(feature = "serde", serde(rename = "SET NULL"))]
520    SetNull,
521    #[cfg_attr(feature = "serde", serde(rename = "RESTRICT"))]
522    Restrict,
523    #[cfg_attr(feature = "serde", serde(rename = "NO ACTION"))]
524    NoAction,
525}
526
527#[derive(Clone, Debug, PartialEq, Eq)]
528#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
529#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
530pub struct ForeignKey {
531    #[cfg_attr(
532        feature = "serde",
533        serde(
534            default,
535            deserialize_with = "cow_option_from_string",
536            skip_serializing_if = "Option::is_none"
537        )
538    )]
539    pub database: Option<Cow<'static, str>>,
540    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
541    pub table: Cow<'static, str>,
542    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
543    pub name: Cow<'static, str>,
544    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
545    pub columns: Vec<Cow<'static, str>>,
546    #[cfg_attr(
547        feature = "serde",
548        serde(
549            default,
550            deserialize_with = "cow_option_from_string",
551            skip_serializing_if = "Option::is_none"
552        )
553    )]
554    pub foreign_database: Option<Cow<'static, str>>,
555    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
556    pub foreign_table: Cow<'static, str>,
557    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
558    pub foreign_columns: Vec<Cow<'static, str>>,
559    #[cfg_attr(
560        feature = "serde",
561        serde(default, skip_serializing_if = "Option::is_none")
562    )]
563    pub on_delete: Option<ReferentialAction>,
564    #[cfg_attr(
565        feature = "serde",
566        serde(default, skip_serializing_if = "Option::is_none")
567    )]
568    pub on_update: Option<ReferentialAction>,
569}
570
571impl ForeignKey {
572    #[must_use]
573    pub fn new(
574        table: impl Into<Cow<'static, str>>,
575        name: impl Into<Cow<'static, str>>,
576        columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
577        foreign_table: impl Into<Cow<'static, str>>,
578        foreign_columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
579    ) -> Self {
580        Self {
581            database: None,
582            table: table.into(),
583            name: name.into(),
584            columns: columns.into_iter().map(Into::into).collect(),
585            foreign_database: None,
586            foreign_table: foreign_table.into(),
587            foreign_columns: foreign_columns.into_iter().map(Into::into).collect(),
588            on_delete: None,
589            on_update: None,
590        }
591    }
592}
593
594#[derive(Clone, Debug, PartialEq, Eq)]
595#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
596#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
597pub struct CheckConstraint {
598    #[cfg_attr(
599        feature = "serde",
600        serde(
601            default,
602            deserialize_with = "cow_option_from_string",
603            skip_serializing_if = "Option::is_none"
604        )
605    )]
606    pub database: Option<Cow<'static, str>>,
607    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
608    pub table: Cow<'static, str>,
609    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
610    pub name: Cow<'static, str>,
611    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
612    pub expression: Cow<'static, str>,
613    #[cfg_attr(
614        feature = "serde",
615        serde(default, skip_serializing_if = "Option::is_none")
616    )]
617    pub enforced: Option<bool>,
618}
619
620impl CheckConstraint {
621    #[must_use]
622    pub fn new(
623        table: impl Into<Cow<'static, str>>,
624        name: impl Into<Cow<'static, str>>,
625        expression: impl Into<Cow<'static, str>>,
626    ) -> Self {
627        Self {
628            database: None,
629            table: table.into(),
630            name: name.into(),
631            expression: expression.into(),
632            enforced: None,
633        }
634    }
635}
636
637#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
638#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
639#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
640pub enum ViewAlgorithm {
641    Undefined,
642    Merge,
643    Temptable,
644}
645
646#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
647#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
648#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
649pub enum ViewSqlSecurity {
650    Definer,
651    Invoker,
652}
653
654#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
655#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
656#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
657pub enum ViewCheckOption {
658    Cascaded,
659    Local,
660}
661
662/// A MySQL view that can be faithfully recreated.
663#[derive(Clone, Debug, PartialEq, Eq)]
664#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
665#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
666pub struct View {
667    #[cfg_attr(
668        feature = "serde",
669        serde(
670            default,
671            deserialize_with = "cow_option_from_string",
672            skip_serializing_if = "Option::is_none"
673        )
674    )]
675    pub database: Option<Cow<'static, str>>,
676    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
677    pub name: Cow<'static, str>,
678    #[cfg_attr(
679        feature = "serde",
680        serde(
681            default,
682            deserialize_with = "cow_option_from_string",
683            skip_serializing_if = "Option::is_none"
684        )
685    )]
686    pub definition: Option<Cow<'static, str>>,
687    #[cfg_attr(
688        feature = "serde",
689        serde(default, skip_serializing_if = "Option::is_none")
690    )]
691    pub algorithm: Option<ViewAlgorithm>,
692    #[cfg_attr(
693        feature = "serde",
694        serde(
695            default,
696            deserialize_with = "cow_option_from_string",
697            skip_serializing_if = "Option::is_none"
698        )
699    )]
700    pub definer: Option<Cow<'static, str>>,
701    #[cfg_attr(
702        feature = "serde",
703        serde(default, skip_serializing_if = "Option::is_none")
704    )]
705    pub sql_security: Option<ViewSqlSecurity>,
706    #[cfg_attr(
707        feature = "serde",
708        serde(default, skip_serializing_if = "Option::is_none")
709    )]
710    pub check_option: Option<ViewCheckOption>,
711    #[cfg_attr(
712        feature = "serde",
713        serde(
714            default,
715            deserialize_with = "cow_option_from_string",
716            skip_serializing_if = "Option::is_none"
717        )
718    )]
719    pub charset: Option<Cow<'static, str>>,
720    #[cfg_attr(
721        feature = "serde",
722        serde(
723            default,
724            deserialize_with = "cow_option_from_string",
725            skip_serializing_if = "Option::is_none"
726        )
727    )]
728    pub collation: Option<Cow<'static, str>>,
729    #[cfg_attr(feature = "serde", serde(default))]
730    pub is_existing: bool,
731}
732
733impl View {
734    #[must_use]
735    pub fn new(
736        name: impl Into<Cow<'static, str>>,
737        definition: impl Into<Cow<'static, str>>,
738    ) -> Self {
739        Self {
740            database: None,
741            name: name.into(),
742            definition: Some(definition.into()),
743            algorithm: None,
744            definer: None,
745            sql_security: None,
746            check_option: None,
747            charset: None,
748            collation: None,
749            is_existing: false,
750        }
751    }
752}
753
754/// Unified MySQL entity for the v6 `ddl` snapshot array.
755#[derive(Clone, Debug, PartialEq, Eq)]
756#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
757#[cfg_attr(feature = "serde", serde(tag = "entityType"))]
758pub enum MySQLEntity {
759    #[cfg_attr(feature = "serde", serde(rename = "tables"))]
760    Table(Table),
761    #[cfg_attr(feature = "serde", serde(rename = "columns"))]
762    Column(Column),
763    #[cfg_attr(feature = "serde", serde(rename = "indexes"))]
764    Index(Index),
765    #[cfg_attr(feature = "serde", serde(rename = "pks"))]
766    PrimaryKey(PrimaryKey),
767    #[cfg_attr(feature = "serde", serde(rename = "uniques"))]
768    UniqueConstraint(UniqueConstraint),
769    #[cfg_attr(feature = "serde", serde(rename = "fks"))]
770    ForeignKey(ForeignKey),
771    #[cfg_attr(feature = "serde", serde(rename = "checks"))]
772    CheckConstraint(CheckConstraint),
773    #[cfg_attr(feature = "serde", serde(rename = "views"))]
774    View(View),
775}
776
777impl MySQLEntity {
778    /// Explicit database carried by this entity, if any.
779    #[must_use]
780    pub fn database(&self) -> Option<&str> {
781        match self {
782            Self::Table(entity) => entity.database.as_deref(),
783            Self::Column(entity) => entity.database.as_deref(),
784            Self::Index(entity) => entity.database.as_deref(),
785            Self::PrimaryKey(entity) => entity.database.as_deref(),
786            Self::UniqueConstraint(entity) => entity.database.as_deref(),
787            Self::ForeignKey(entity) => entity.database.as_deref(),
788            Self::CheckConstraint(entity) => entity.database.as_deref(),
789            Self::View(entity) => entity.database.as_deref(),
790        }
791    }
792}