Skip to main content

drizzle_types/mysql/ddl/
mod.rs

1//! MySQL schema objects as stored in migration snapshots.
2//!
3//! The migration tooling builds these from your schema or by introspecting a
4//! live database, saves them in snapshot files, and diffs two sets to plan a
5//! migration. A full schema is a list of [`MySQLEntity`] values.
6//!
7//! Every entity has a `database` field. `None` means the connection's current
8//! database. SQL fragments (`sql_type`, defaults, expressions, view
9//! definitions) are stored as written and are trusted schema SQL.
10
11use crate::alloc_prelude::*;
12
13#[cfg(feature = "serde")]
14use crate::serde_helpers::{cow_from_string, cow_option_from_string, cow_vec_from_strings};
15
16/// Storage mode for a generated MySQL column.
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
20pub enum GeneratedType {
21    /// `VIRTUAL`: computed when read.
22    Virtual,
23    /// `STORED`: computed on write and stored. The default.
24    #[default]
25    Stored,
26}
27
28/// The `GENERATED ALWAYS AS (...)` part of a column definition.
29#[derive(Clone, Debug, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
31#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
32pub struct Generated {
33    #[cfg_attr(
34        feature = "serde",
35        serde(rename = "as", deserialize_with = "cow_from_string")
36    )]
37    /// SQL expression that computes the value (serialized as `as`).
38    pub expression: Cow<'static, str>,
39    /// Whether the value is stored or virtual (serialized as `type`).
40    #[cfg_attr(feature = "serde", serde(rename = "type"))]
41    pub generation_type: GeneratedType,
42}
43
44impl Generated {
45    /// Creates a `STORED` generated column from a SQL expression.
46    #[must_use]
47    pub fn stored(expression: impl Into<Cow<'static, str>>) -> Self {
48        Self {
49            expression: expression.into(),
50            generation_type: GeneratedType::Stored,
51        }
52    }
53
54    /// Creates a `VIRTUAL` generated column from a SQL expression.
55    #[must_use]
56    pub fn virtual_column(expression: impl Into<Cow<'static, str>>) -> Self {
57        Self {
58            expression: expression.into(),
59            generation_type: GeneratedType::Virtual,
60        }
61    }
62}
63
64/// The allowed values of an inline `ENUM(...)` or `SET(...)` column type, in
65/// declaration order.
66#[derive(Clone, Debug, Default, PartialEq, Eq)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
69pub struct InlineEnum {
70    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
71    pub values: Vec<Cow<'static, str>>,
72}
73
74impl InlineEnum {
75    /// Creates the value list from any iterable of strings.
76    #[must_use]
77    pub fn new(values: impl IntoIterator<Item = impl Into<String>>) -> Self {
78        Self {
79            values: values
80                .into_iter()
81                .map(|value| Cow::Owned(value.into()))
82                .collect(),
83        }
84    }
85}
86
87/// The values of an inline `ENUM` or `SET` column, kept beside its rendered
88/// `sql_type` so a diff can compare them without parsing SQL.
89#[derive(Clone, Debug, PartialEq, Eq)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91#[cfg_attr(
92    feature = "serde",
93    serde(tag = "kind", content = "definition", rename_all = "lowercase")
94)]
95pub enum InlineType {
96    /// `ENUM('a', 'b', ...)`: exactly one of the values.
97    Enum(InlineEnum),
98    /// `SET('a', 'b', ...)`: any combination of the values.
99    Set(InlineEnum),
100}
101
102/// A table, with its table options. Columns, keys and indexes are separate
103/// entities that name the table.
104#[derive(Clone, Debug, PartialEq, Eq)]
105#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
106#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
107pub struct Table {
108    #[cfg_attr(
109        feature = "serde",
110        serde(
111            default,
112            deserialize_with = "cow_option_from_string",
113            skip_serializing_if = "Option::is_none"
114        )
115    )]
116    pub database: Option<Cow<'static, str>>,
117    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
118    pub name: Cow<'static, str>,
119    #[cfg_attr(
120        feature = "serde",
121        serde(default, skip_serializing_if = "core::ops::Not::not")
122    )]
123    /// `CREATE TEMPORARY TABLE`.
124    pub temporary: bool,
125    #[cfg_attr(
126        feature = "serde",
127        serde(
128            default,
129            deserialize_with = "cow_option_from_string",
130            skip_serializing_if = "Option::is_none"
131        )
132    )]
133    /// Storage engine, such as `InnoDB`.
134    pub engine: Option<Cow<'static, str>>,
135    #[cfg_attr(
136        feature = "serde",
137        serde(
138            default,
139            deserialize_with = "cow_option_from_string",
140            skip_serializing_if = "Option::is_none"
141        )
142    )]
143    pub charset: Option<Cow<'static, str>>,
144    #[cfg_attr(
145        feature = "serde",
146        serde(
147            default,
148            deserialize_with = "cow_option_from_string",
149            skip_serializing_if = "Option::is_none"
150        )
151    )]
152    pub collation: Option<Cow<'static, str>>,
153    #[cfg_attr(
154        feature = "serde",
155        serde(
156            default,
157            deserialize_with = "cow_option_from_string",
158            skip_serializing_if = "Option::is_none"
159        )
160    )]
161    pub comment: Option<Cow<'static, str>>,
162    #[cfg_attr(
163        feature = "serde",
164        serde(default, skip_serializing_if = "Vec::is_empty")
165    )]
166    /// Other table options, which drizzle keeps but cannot change.
167    pub options: Vec<TableOption>,
168}
169
170impl Table {
171    /// Creates a table in the current database with no options set.
172    #[must_use]
173    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
174        Self {
175            database: None,
176            name: name.into(),
177            temporary: false,
178            engine: None,
179            charset: None,
180            collation: None,
181            comment: None,
182            options: Vec::new(),
183        }
184    }
185}
186
187/// A table column and its full MySQL definition.
188#[derive(Clone, Debug, PartialEq, Eq)]
189#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
190#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
191pub struct Column {
192    #[cfg_attr(
193        feature = "serde",
194        serde(
195            default,
196            deserialize_with = "cow_option_from_string",
197            skip_serializing_if = "Option::is_none"
198        )
199    )]
200    pub database: Option<Cow<'static, str>>,
201    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
202    pub table: Cow<'static, str>,
203    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
204    pub name: Cow<'static, str>,
205    #[cfg_attr(
206        feature = "serde",
207        serde(rename = "type", deserialize_with = "cow_from_string")
208    )]
209    /// Column type as written in DDL, such as `varchar(255)` or
210    /// `int unsigned` (serialized as `type`).
211    pub sql_type: Cow<'static, str>,
212    /// `NOT NULL`.
213    #[cfg_attr(feature = "serde", serde(default))]
214    pub not_null: bool,
215    /// `AUTO_INCREMENT`.
216    #[cfg_attr(feature = "serde", serde(default))]
217    pub autoincrement: bool,
218    /// Whether the column is part of the primary key.
219    #[cfg_attr(feature = "serde", serde(default))]
220    pub primary_key: bool,
221    /// Whether the column has a single-column unique constraint.
222    #[cfg_attr(feature = "serde", serde(default))]
223    pub unique: bool,
224    #[cfg_attr(
225        feature = "serde",
226        serde(
227            default,
228            deserialize_with = "cow_option_from_string",
229            skip_serializing_if = "Option::is_none"
230        )
231    )]
232    /// `DEFAULT` value, as SQL.
233    pub default: Option<Cow<'static, str>>,
234    #[cfg_attr(
235        feature = "serde",
236        serde(
237            default,
238            deserialize_with = "cow_option_from_string",
239            skip_serializing_if = "Option::is_none"
240        )
241    )]
242    /// `ON UPDATE` value, as SQL, such as `CURRENT_TIMESTAMP`.
243    pub on_update: Option<Cow<'static, str>>,
244    /// `GENERATED ALWAYS AS (...)`, for a generated column.
245    #[cfg_attr(
246        feature = "serde",
247        serde(default, skip_serializing_if = "Option::is_none")
248    )]
249    pub generated: Option<Generated>,
250    #[cfg_attr(
251        feature = "serde",
252        serde(default, skip_serializing_if = "Option::is_none")
253    )]
254    /// Values of an `ENUM` or `SET` column.
255    pub inline_type: Option<InlineType>,
256    #[cfg_attr(
257        feature = "serde",
258        serde(
259            default,
260            deserialize_with = "cow_option_from_string",
261            skip_serializing_if = "Option::is_none"
262        )
263    )]
264    pub charset: Option<Cow<'static, str>>,
265    #[cfg_attr(
266        feature = "serde",
267        serde(
268            default,
269            deserialize_with = "cow_option_from_string",
270            skip_serializing_if = "Option::is_none"
271        )
272    )]
273    pub collation: Option<Cow<'static, str>>,
274    #[cfg_attr(
275        feature = "serde",
276        serde(
277            default,
278            deserialize_with = "cow_option_from_string",
279            skip_serializing_if = "Option::is_none"
280        )
281    )]
282    pub comment: Option<Cow<'static, str>>,
283}
284
285impl Column {
286    /// Creates a nullable column with no default, constraints or options.
287    #[must_use]
288    pub fn new(
289        table: impl Into<Cow<'static, str>>,
290        name: impl Into<Cow<'static, str>>,
291        sql_type: impl Into<Cow<'static, str>>,
292    ) -> Self {
293        Self {
294            database: None,
295            table: table.into(),
296            name: name.into(),
297            sql_type: sql_type.into(),
298            not_null: false,
299            autoincrement: false,
300            primary_key: false,
301            unique: false,
302            default: None,
303            on_update: None,
304            generated: None,
305            inline_type: None,
306            charset: None,
307            collation: None,
308            comment: None,
309        }
310    }
311}
312
313/// One key part of an index: a column name or a SQL expression.
314#[derive(Clone, Debug, PartialEq, Eq)]
315#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
316#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
317pub struct IndexColumn {
318    #[cfg_attr(
319        feature = "serde",
320        serde(alias = "value", deserialize_with = "cow_from_string")
321    )]
322    /// The column name, or the SQL expression when `is_expression` is set.
323    /// drizzle-kit writes it as `value`.
324    pub expression: Cow<'static, str>,
325    /// Whether `expression` is a SQL expression rather than a column name.
326    #[cfg_attr(feature = "serde", serde(default))]
327    pub is_expression: bool,
328    #[cfg_attr(
329        feature = "serde",
330        serde(default, skip_serializing_if = "Option::is_none")
331    )]
332    /// Prefix length, as in `name(10)`.
333    pub length: Option<u32>,
334    #[cfg_attr(
335        feature = "serde",
336        serde(default, skip_serializing_if = "Option::is_none")
337    )]
338    /// Sort order: `Some(true)` for `ASC`, `Some(false)` for `DESC`, `None`
339    /// when not written (ascending).
340    pub ascending: Option<bool>,
341}
342
343impl IndexColumn {
344    /// Creates a key part for a column.
345    #[must_use]
346    pub fn column(name: impl Into<Cow<'static, str>>) -> Self {
347        Self {
348            expression: name.into(),
349            is_expression: false,
350            length: None,
351            ascending: None,
352        }
353    }
354
355    /// Creates a key part for a SQL expression.
356    #[must_use]
357    pub fn expression(sql: impl Into<Cow<'static, str>>) -> Self {
358        Self {
359            expression: sql.into(),
360            is_expression: true,
361            length: None,
362            ascending: None,
363        }
364    }
365}
366
367/// A table option drizzle does not model, kept as a name and value so it is
368/// not silently dropped. The planner reports changes to these as unsupported.
369#[derive(Clone, Debug, PartialEq, Eq)]
370#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
371#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
372pub struct TableOption {
373    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
374    /// Option name.
375    pub name: Cow<'static, str>,
376    /// Option value, as SQL.
377    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
378    pub value: Cow<'static, str>,
379}
380
381#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
382#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
383#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
384/// Index type, from `USING BTREE` or `USING HASH`.
385pub enum IndexMethod {
386    /// `BTREE`.
387    Btree,
388    /// `HASH`.
389    Hash,
390}
391
392#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
393#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
394#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
395/// `ALGORITHM` option used when creating an index.
396pub enum IndexAlgorithm {
397    /// `ALGORITHM = DEFAULT`.
398    Default,
399    /// `ALGORITHM = INPLACE`.
400    Inplace,
401    /// `ALGORITHM = COPY`.
402    Copy,
403}
404
405#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
406#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
407#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
408/// `LOCK` option used when creating an index.
409pub enum IndexLock {
410    /// `LOCK = DEFAULT`.
411    Default,
412    /// `LOCK = NONE`.
413    None,
414    /// `LOCK = SHARED`.
415    Shared,
416    /// `LOCK = EXCLUSIVE`.
417    Exclusive,
418}
419
420#[derive(Clone, Debug, PartialEq, Eq)]
421#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
422#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
423/// An index on a table.
424pub struct Index {
425    #[cfg_attr(
426        feature = "serde",
427        serde(
428            default,
429            deserialize_with = "cow_option_from_string",
430            skip_serializing_if = "Option::is_none"
431        )
432    )]
433    pub database: Option<Cow<'static, str>>,
434    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
435    pub table: Cow<'static, str>,
436    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
437    pub name: Cow<'static, str>,
438    /// Key parts, in order.
439    pub columns: Vec<IndexColumn>,
440    /// `UNIQUE INDEX`.
441    #[cfg_attr(feature = "serde", serde(default))]
442    pub unique: bool,
443    #[cfg_attr(
444        feature = "serde",
445        serde(default, skip_serializing_if = "Option::is_none")
446    )]
447    pub using: Option<IndexMethod>,
448    #[cfg_attr(
449        feature = "serde",
450        serde(default, skip_serializing_if = "Option::is_none")
451    )]
452    pub algorithm: Option<IndexAlgorithm>,
453    #[cfg_attr(
454        feature = "serde",
455        serde(default, skip_serializing_if = "Option::is_none")
456    )]
457    pub lock: Option<IndexLock>,
458    #[cfg_attr(
459        feature = "serde",
460        serde(
461            default,
462            deserialize_with = "cow_option_from_string",
463            skip_serializing_if = "Option::is_none"
464        )
465    )]
466    pub comment: Option<Cow<'static, str>>,
467    #[cfg_attr(
468        feature = "serde",
469        serde(default, skip_serializing_if = "Option::is_none")
470    )]
471    /// `VISIBLE` (`Some(true)`) or `INVISIBLE` (`Some(false)`); `None`
472    /// means not written (visible).
473    pub visible: Option<bool>,
474}
475
476impl Index {
477    /// Creates a non-unique index with no options.
478    #[must_use]
479    pub fn new(
480        table: impl Into<Cow<'static, str>>,
481        name: impl Into<Cow<'static, str>>,
482        columns: Vec<IndexColumn>,
483    ) -> Self {
484        Self {
485            database: None,
486            table: table.into(),
487            name: name.into(),
488            columns,
489            unique: false,
490            using: None,
491            algorithm: None,
492            lock: None,
493            comment: None,
494            visible: None,
495        }
496    }
497}
498
499#[derive(Clone, Debug, PartialEq, Eq)]
500#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
501#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
502/// A table's primary key.
503pub struct PrimaryKey {
504    #[cfg_attr(
505        feature = "serde",
506        serde(
507            default,
508            deserialize_with = "cow_option_from_string",
509            skip_serializing_if = "Option::is_none"
510        )
511    )]
512    pub database: Option<Cow<'static, str>>,
513    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
514    pub table: Cow<'static, str>,
515    #[cfg_attr(
516        feature = "serde",
517        serde(
518            default,
519            deserialize_with = "cow_option_from_string",
520            skip_serializing_if = "Option::is_none"
521        )
522    )]
523    pub name: Option<Cow<'static, str>>,
524    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
525    pub columns: Vec<Cow<'static, str>>,
526}
527
528impl PrimaryKey {
529    /// Creates a primary key over `columns`, named `PRIMARY` (MySQL's fixed
530    /// name for primary keys).
531    #[must_use]
532    pub fn new(
533        table: impl Into<Cow<'static, str>>,
534        columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
535    ) -> Self {
536        Self {
537            database: None,
538            table: table.into(),
539            name: Some(Cow::Borrowed("PRIMARY")),
540            columns: columns.into_iter().map(Into::into).collect(),
541        }
542    }
543}
544
545#[derive(Clone, Debug, PartialEq, Eq)]
546#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
547#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
548/// A named unique constraint.
549pub struct UniqueConstraint {
550    #[cfg_attr(
551        feature = "serde",
552        serde(
553            default,
554            deserialize_with = "cow_option_from_string",
555            skip_serializing_if = "Option::is_none"
556        )
557    )]
558    pub database: Option<Cow<'static, str>>,
559    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
560    pub table: Cow<'static, str>,
561    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
562    pub name: Cow<'static, str>,
563    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
564    pub columns: Vec<Cow<'static, str>>,
565}
566
567impl UniqueConstraint {
568    /// Creates a unique constraint over `columns`.
569    #[must_use]
570    pub fn new(
571        table: impl Into<Cow<'static, str>>,
572        name: impl Into<Cow<'static, str>>,
573        columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
574    ) -> Self {
575        Self {
576            database: None,
577            table: table.into(),
578            name: name.into(),
579            columns: columns.into_iter().map(Into::into).collect(),
580        }
581    }
582}
583
584#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
585#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
586/// Foreign key `ON DELETE` / `ON UPDATE` action. Serialized as the SQL
587/// keywords (`"CASCADE"`, `"SET NULL"`, ...).
588pub enum ReferentialAction {
589    /// `CASCADE`.
590    #[cfg_attr(feature = "serde", serde(rename = "CASCADE"))]
591    Cascade,
592    /// `SET NULL`.
593    #[cfg_attr(feature = "serde", serde(rename = "SET NULL"))]
594    SetNull,
595    /// `RESTRICT`.
596    #[cfg_attr(feature = "serde", serde(rename = "RESTRICT"))]
597    Restrict,
598    /// `NO ACTION`.
599    #[cfg_attr(feature = "serde", serde(rename = "NO ACTION"))]
600    NoAction,
601}
602
603#[derive(Clone, Debug, PartialEq, Eq)]
604#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
605#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
606/// A foreign key from `table(columns)` to `foreign_table(foreign_columns)`.
607pub struct ForeignKey {
608    #[cfg_attr(
609        feature = "serde",
610        serde(
611            default,
612            deserialize_with = "cow_option_from_string",
613            skip_serializing_if = "Option::is_none"
614        )
615    )]
616    pub database: Option<Cow<'static, str>>,
617    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
618    pub table: Cow<'static, str>,
619    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
620    pub name: Cow<'static, str>,
621    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
622    pub columns: Vec<Cow<'static, str>>,
623    #[cfg_attr(
624        feature = "serde",
625        serde(
626            default,
627            deserialize_with = "cow_option_from_string",
628            skip_serializing_if = "Option::is_none"
629        )
630    )]
631    /// Database of the referenced table; `None` for the current database.
632    pub foreign_database: Option<Cow<'static, str>>,
633    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
634    pub foreign_table: Cow<'static, str>,
635    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_vec_from_strings"))]
636    pub foreign_columns: Vec<Cow<'static, str>>,
637    #[cfg_attr(
638        feature = "serde",
639        serde(default, skip_serializing_if = "Option::is_none")
640    )]
641    /// `ON DELETE` action; `None` when not written.
642    pub on_delete: Option<ReferentialAction>,
643    #[cfg_attr(
644        feature = "serde",
645        serde(default, skip_serializing_if = "Option::is_none")
646    )]
647    /// `ON UPDATE` action; `None` when not written.
648    pub on_update: Option<ReferentialAction>,
649}
650
651impl ForeignKey {
652    /// Creates a foreign key with no `ON DELETE` / `ON UPDATE` actions.
653    #[must_use]
654    pub fn new(
655        table: impl Into<Cow<'static, str>>,
656        name: impl Into<Cow<'static, str>>,
657        columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
658        foreign_table: impl Into<Cow<'static, str>>,
659        foreign_columns: impl IntoIterator<Item = impl Into<Cow<'static, str>>>,
660    ) -> Self {
661        Self {
662            database: None,
663            table: table.into(),
664            name: name.into(),
665            columns: columns.into_iter().map(Into::into).collect(),
666            foreign_database: None,
667            foreign_table: foreign_table.into(),
668            foreign_columns: foreign_columns.into_iter().map(Into::into).collect(),
669            on_delete: None,
670            on_update: None,
671        }
672    }
673}
674
675#[derive(Clone, Debug, PartialEq, Eq)]
676#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
677#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
678/// A named `CHECK` constraint.
679pub struct CheckConstraint {
680    #[cfg_attr(
681        feature = "serde",
682        serde(
683            default,
684            deserialize_with = "cow_option_from_string",
685            skip_serializing_if = "Option::is_none"
686        )
687    )]
688    pub database: Option<Cow<'static, str>>,
689    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
690    pub table: Cow<'static, str>,
691    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
692    pub name: Cow<'static, str>,
693    /// The check expression. drizzle-kit writes it as `value`.
694    #[cfg_attr(
695        feature = "serde",
696        serde(alias = "value", deserialize_with = "cow_from_string")
697    )]
698    pub expression: Cow<'static, str>,
699    #[cfg_attr(
700        feature = "serde",
701        serde(default, skip_serializing_if = "Option::is_none")
702    )]
703    /// `ENFORCED` (`Some(true)`) or `NOT ENFORCED` (`Some(false)`); `None`
704    /// means not written (enforced).
705    pub enforced: Option<bool>,
706}
707
708impl CheckConstraint {
709    /// Creates a check constraint from a SQL boolean expression.
710    #[must_use]
711    pub fn new(
712        table: impl Into<Cow<'static, str>>,
713        name: impl Into<Cow<'static, str>>,
714        expression: impl Into<Cow<'static, str>>,
715    ) -> Self {
716        Self {
717            database: None,
718            table: table.into(),
719            name: name.into(),
720            expression: expression.into(),
721            enforced: None,
722        }
723    }
724}
725
726#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
727#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
728#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
729/// View `ALGORITHM`.
730pub enum ViewAlgorithm {
731    /// `UNDEFINED`: MySQL chooses.
732    Undefined,
733    /// `MERGE`.
734    Merge,
735    /// `TEMPTABLE`.
736    Temptable,
737}
738
739#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
740#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
741#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
742/// View `SQL SECURITY`: whose privileges the view runs with.
743pub enum ViewSqlSecurity {
744    /// `DEFINER`.
745    Definer,
746    /// `INVOKER`.
747    Invoker,
748}
749
750#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
751#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
752#[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
753/// View `WITH ... CHECK OPTION`.
754pub enum ViewCheckOption {
755    /// `WITH CASCADED CHECK OPTION`.
756    Cascaded,
757    /// `WITH LOCAL CHECK OPTION`.
758    Local,
759}
760
761/// A view.
762///
763/// A view with `is_existing` set is managed outside drizzle: it is referenced
764/// but never created or dropped, and needs no `definition`.
765#[derive(Clone, Debug, PartialEq, Eq)]
766#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
767#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
768pub struct View {
769    #[cfg_attr(
770        feature = "serde",
771        serde(
772            default,
773            deserialize_with = "cow_option_from_string",
774            skip_serializing_if = "Option::is_none"
775        )
776    )]
777    pub database: Option<Cow<'static, str>>,
778    #[cfg_attr(feature = "serde", serde(deserialize_with = "cow_from_string"))]
779    pub name: Cow<'static, str>,
780    #[cfg_attr(
781        feature = "serde",
782        serde(
783            default,
784            deserialize_with = "cow_option_from_string",
785            skip_serializing_if = "Option::is_none"
786        )
787    )]
788    /// The view's `SELECT` statement. Required unless `is_existing` is set.
789    pub definition: Option<Cow<'static, str>>,
790    #[cfg_attr(
791        feature = "serde",
792        serde(default, skip_serializing_if = "Option::is_none")
793    )]
794    pub algorithm: Option<ViewAlgorithm>,
795    #[cfg_attr(
796        feature = "serde",
797        serde(
798            default,
799            deserialize_with = "cow_option_from_string",
800            skip_serializing_if = "Option::is_none"
801        )
802    )]
803    /// `DEFINER` account, such as `` `app`@`%` ``.
804    pub definer: Option<Cow<'static, str>>,
805    #[cfg_attr(
806        feature = "serde",
807        serde(default, skip_serializing_if = "Option::is_none")
808    )]
809    pub sql_security: Option<ViewSqlSecurity>,
810    #[cfg_attr(
811        feature = "serde",
812        serde(default, skip_serializing_if = "Option::is_none")
813    )]
814    pub check_option: Option<ViewCheckOption>,
815    #[cfg_attr(
816        feature = "serde",
817        serde(
818            default,
819            deserialize_with = "cow_option_from_string",
820            skip_serializing_if = "Option::is_none"
821        )
822    )]
823    pub charset: Option<Cow<'static, str>>,
824    #[cfg_attr(
825        feature = "serde",
826        serde(
827            default,
828            deserialize_with = "cow_option_from_string",
829            skip_serializing_if = "Option::is_none"
830        )
831    )]
832    pub collation: Option<Cow<'static, str>>,
833    #[cfg_attr(feature = "serde", serde(default))]
834    /// Whether the view exists outside drizzle's control.
835    pub is_existing: bool,
836}
837
838impl View {
839    /// Creates a view from its `SELECT` statement, with no options.
840    #[must_use]
841    pub fn new(
842        name: impl Into<Cow<'static, str>>,
843        definition: impl Into<Cow<'static, str>>,
844    ) -> Self {
845        Self {
846            database: None,
847            name: name.into(),
848            definition: Some(definition.into()),
849            algorithm: None,
850            definer: None,
851            sql_security: None,
852            check_option: None,
853            charset: None,
854            collation: None,
855            is_existing: false,
856        }
857    }
858}
859
860/// Any MySQL schema object: one element of a snapshot's `ddl` array.
861///
862/// With `serde`, the variant is stored in an `entityType` field (`"tables"`,
863/// `"columns"`, `"indexes"`, `"pks"`, `"uniques"`, `"fks"`, `"checks"`,
864/// `"views"`).
865#[derive(Clone, Debug, PartialEq, Eq)]
866#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
867#[cfg_attr(feature = "serde", serde(tag = "entityType"))]
868pub enum MySQLEntity {
869    #[cfg_attr(feature = "serde", serde(rename = "tables"))]
870    Table(Table),
871    #[cfg_attr(feature = "serde", serde(rename = "columns"))]
872    Column(Column),
873    #[cfg_attr(feature = "serde", serde(rename = "indexes"))]
874    Index(Index),
875    #[cfg_attr(feature = "serde", serde(rename = "pks"))]
876    PrimaryKey(PrimaryKey),
877    #[cfg_attr(feature = "serde", serde(rename = "uniques"))]
878    UniqueConstraint(UniqueConstraint),
879    #[cfg_attr(feature = "serde", serde(rename = "fks"))]
880    ForeignKey(ForeignKey),
881    #[cfg_attr(feature = "serde", serde(rename = "checks"))]
882    CheckConstraint(CheckConstraint),
883    #[cfg_attr(feature = "serde", serde(rename = "views"))]
884    View(View),
885}
886
887impl MySQLEntity {
888    /// Returns the entity's database, or `None` for the current database.
889    #[must_use]
890    pub fn database(&self) -> Option<&str> {
891        match self {
892            Self::Table(entity) => entity.database.as_deref(),
893            Self::Column(entity) => entity.database.as_deref(),
894            Self::Index(entity) => entity.database.as_deref(),
895            Self::PrimaryKey(entity) => entity.database.as_deref(),
896            Self::UniqueConstraint(entity) => entity.database.as_deref(),
897            Self::ForeignKey(entity) => entity.database.as_deref(),
898            Self::CheckConstraint(entity) => entity.database.as_deref(),
899            Self::View(entity) => entity.database.as_deref(),
900        }
901    }
902}