Skip to main content

gaman_core/
operations.rs

1use std::borrow::Cow;
2
3use serde::{Deserialize, Serialize};
4
5use crate::states::types::{Dep, EntityKind};
6use crate::states::{
7    Column, Constraint, EnumDef, ExtensionDef, ForeignKey, FunctionDef, Index, Table,
8    TableOptionsMeta, TriggerDef, ViewDef,
9};
10
11/// All possible schema change operations.
12/// Each variant carries the minimal data needed to describe the change.
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
14#[serde(tag = "type", rename_all = "snake_case")]
15pub enum Operation {
16    CreateTable {
17        table: Table,
18    },
19    DropTable {
20        table: Table,
21    },
22    RenameTable {
23        old_name: String,
24        new_name: String,
25    },
26    AcknowledgeTableOptions {
27        table_name: String,
28        old: TableOptionsMeta,
29        new: TableOptionsMeta,
30    },
31    AddColumn {
32        table_name: String,
33        column: Column,
34    },
35    DropColumn {
36        table_name: String,
37        column: Column,
38        #[serde(default)]
39        cascade: bool,
40    },
41    RenameColumn {
42        table_name: String,
43        old_name: String,
44        new_name: String,
45    },
46    AlterColumn {
47        table_name: String,
48        old: Column,
49        new: Column,
50        #[serde(default)]
51        cast_expr: Option<String>,
52    },
53    AddForeignKey {
54        table_name: String,
55        foreign_key: ForeignKey,
56    },
57    DropForeignKey {
58        table_name: String,
59        foreign_key: ForeignKey,
60        #[serde(default)]
61        cascade: bool,
62    },
63    AddIndex {
64        table_name: String,
65        index: Index,
66        #[serde(default)]
67        concurrent: bool,
68    },
69    DropIndex {
70        table_name: String,
71        index: Index,
72        #[serde(default)]
73        concurrent: bool,
74    },
75    AddConstraint {
76        table_name: String,
77        constraint: Constraint,
78    },
79    DropConstraint {
80        table_name: String,
81        constraint: Constraint,
82    },
83    Statement {
84        up: String,
85        down: Option<String>,
86    },
87    CreateFunction {
88        function: FunctionDef,
89    },
90    AlterFunction {
91        old: FunctionDef,
92        new: FunctionDef,
93    },
94    DropFunction {
95        function: FunctionDef,
96    },
97    CreateTrigger {
98        table_name: String,
99        trigger: TriggerDef,
100    },
101    AlterTrigger {
102        table_name: String,
103        old: TriggerDef,
104        new: TriggerDef,
105    },
106    DropTrigger {
107        table_name: String,
108        trigger: TriggerDef,
109    },
110    CreateView {
111        view: ViewDef,
112    },
113    DropView {
114        view: ViewDef,
115    },
116    ReplaceView {
117        old: ViewDef,
118        new: ViewDef,
119    },
120    CreateExtension {
121        extension: ExtensionDef,
122    },
123    DropExtension {
124        extension: ExtensionDef,
125    },
126    CreateEnum {
127        enum_def: EnumDef,
128    },
129    DropEnum {
130        enum_def: EnumDef,
131    },
132    RenameEnumValue {
133        enum_name: String,
134        #[serde(default)]
135        schema: Option<String>,
136        old_value: String,
137        new_value: String,
138    },
139    AlterEnum {
140        old: EnumDef,
141        new: EnumDef,
142    },
143}
144
145impl Operation {
146    pub fn inverse(&self) -> Option<Operation> {
147        match self {
148            Self::CreateTable { .. }
149            | Self::DropTable { .. }
150            | Self::RenameTable { .. }
151            | Self::AcknowledgeTableOptions { .. } => self.inverse_table_op(),
152            Self::AddColumn { .. }
153            | Self::DropColumn { .. }
154            | Self::RenameColumn { .. }
155            | Self::AlterColumn { .. }
156            | Self::AddForeignKey { .. }
157            | Self::DropForeignKey { .. }
158            | Self::AddIndex { .. }
159            | Self::DropIndex { .. }
160            | Self::AddConstraint { .. }
161            | Self::DropConstraint { .. } => self.inverse_table_item_op(),
162            Self::Statement { .. }
163            | Self::CreateFunction { .. }
164            | Self::AlterFunction { .. }
165            | Self::DropFunction { .. } => self.inverse_function_op(),
166            Self::CreateTrigger { .. } | Self::AlterTrigger { .. } | Self::DropTrigger { .. } => {
167                self.inverse_trigger_op()
168            }
169            Self::CreateView { .. } | Self::DropView { .. } | Self::ReplaceView { .. } => {
170                self.inverse_view_op()
171            }
172            Self::CreateExtension { .. } | Self::DropExtension { .. } => {
173                self.inverse_extension_op()
174            }
175            Self::CreateEnum { .. }
176            | Self::DropEnum { .. }
177            | Self::RenameEnumValue { .. }
178            | Self::AlterEnum { .. } => self.inverse_enum_op(),
179        }
180    }
181
182    fn inverse_table_op(&self) -> Option<Operation> {
183        match self {
184            Self::CreateTable { table } => Some(Self::DropTable {
185                table: table.clone(),
186            }),
187            Self::DropTable { table } => Some(Self::CreateTable {
188                table: table.clone(),
189            }),
190            Self::RenameTable { old_name, new_name } => Some(Self::RenameTable {
191                old_name: new_name.clone(),
192                new_name: old_name.clone(),
193            }),
194            Self::AcknowledgeTableOptions {
195                table_name,
196                old,
197                new,
198            } => Some(Self::AcknowledgeTableOptions {
199                table_name: table_name.clone(),
200                old: new.clone(),
201                new: old.clone(),
202            }),
203            _ => None,
204        }
205    }
206
207    fn inverse_table_item_op(&self) -> Option<Operation> {
208        match self {
209            Self::AddColumn { table_name, column } => Some(Self::DropColumn {
210                table_name: table_name.clone(),
211                column: column.clone(),
212                cascade: false,
213            }),
214            Self::DropColumn {
215                table_name, column, ..
216            } => Some(Self::AddColumn {
217                table_name: table_name.clone(),
218                column: column.clone(),
219            }),
220            Self::RenameColumn {
221                table_name,
222                old_name,
223                new_name,
224            } => Some(Self::RenameColumn {
225                table_name: table_name.clone(),
226                old_name: new_name.clone(),
227                new_name: old_name.clone(),
228            }),
229            Self::AlterColumn {
230                table_name,
231                old,
232                new,
233                ..
234            } => Some(Self::AlterColumn {
235                table_name: table_name.clone(),
236                old: new.clone(),
237                new: old.clone(),
238                cast_expr: None,
239            }),
240            Self::AddForeignKey {
241                table_name,
242                foreign_key,
243            } => Some(Self::DropForeignKey {
244                table_name: table_name.clone(),
245                foreign_key: foreign_key.clone(),
246                cascade: false,
247            }),
248            Self::DropForeignKey {
249                table_name,
250                foreign_key,
251                ..
252            } => Some(Self::AddForeignKey {
253                table_name: table_name.clone(),
254                foreign_key: foreign_key.clone(),
255            }),
256            Self::AddIndex {
257                table_name,
258                index,
259                concurrent,
260            } => Some(Self::DropIndex {
261                table_name: table_name.clone(),
262                index: index.clone(),
263                concurrent: *concurrent,
264            }),
265            Self::DropIndex {
266                table_name,
267                index,
268                concurrent,
269            } => Some(Self::AddIndex {
270                table_name: table_name.clone(),
271                index: index.clone(),
272                concurrent: *concurrent,
273            }),
274            Self::AddConstraint {
275                table_name,
276                constraint,
277            } => Some(Self::DropConstraint {
278                table_name: table_name.clone(),
279                constraint: constraint.clone(),
280            }),
281            Self::DropConstraint {
282                table_name,
283                constraint,
284            } => Some(Self::AddConstraint {
285                table_name: table_name.clone(),
286                constraint: constraint.clone(),
287            }),
288            _ => None,
289        }
290    }
291
292    fn inverse_function_op(&self) -> Option<Operation> {
293        match self {
294            Self::Statement {
295                up,
296                down: Some(down),
297            } => Some(Self::Statement {
298                up: down.clone(),
299                down: Some(up.clone()),
300            }),
301            Self::Statement { down: None, .. } => None,
302            Self::CreateFunction { function } => Some(Self::DropFunction {
303                function: function.clone(),
304            }),
305            Self::DropFunction { function } => Some(Self::CreateFunction {
306                function: function.clone(),
307            }),
308            Self::AlterFunction { old, new } => Some(Self::AlterFunction {
309                old: new.clone(),
310                new: old.clone(),
311            }),
312            _ => None,
313        }
314    }
315
316    fn inverse_trigger_op(&self) -> Option<Operation> {
317        match self {
318            Self::CreateTrigger {
319                table_name,
320                trigger,
321            } => Some(Self::DropTrigger {
322                table_name: table_name.clone(),
323                trigger: trigger.clone(),
324            }),
325            Self::DropTrigger {
326                table_name,
327                trigger,
328            } => Some(Self::CreateTrigger {
329                table_name: table_name.clone(),
330                trigger: trigger.clone(),
331            }),
332            Self::AlterTrigger {
333                table_name,
334                old,
335                new,
336            } => Some(Self::AlterTrigger {
337                table_name: table_name.clone(),
338                old: new.clone(),
339                new: old.clone(),
340            }),
341            _ => None,
342        }
343    }
344
345    fn inverse_view_op(&self) -> Option<Operation> {
346        match self {
347            Self::CreateView { view } => Some(Self::DropView { view: view.clone() }),
348            Self::DropView { view } => Some(Self::CreateView { view: view.clone() }),
349            Self::ReplaceView { old, new } => Some(Self::ReplaceView {
350                old: new.clone(),
351                new: old.clone(),
352            }),
353            _ => None,
354        }
355    }
356
357    fn inverse_extension_op(&self) -> Option<Operation> {
358        match self {
359            Self::CreateExtension { extension } => Some(Self::DropExtension {
360                extension: extension.clone(),
361            }),
362            Self::DropExtension { extension } => Some(Self::CreateExtension {
363                extension: extension.clone(),
364            }),
365            _ => None,
366        }
367    }
368
369    fn inverse_enum_op(&self) -> Option<Operation> {
370        match self {
371            Self::CreateEnum { enum_def } => Some(Self::DropEnum {
372                enum_def: enum_def.clone(),
373            }),
374            Self::DropEnum { enum_def } => Some(Self::CreateEnum {
375                enum_def: enum_def.clone(),
376            }),
377            Self::RenameEnumValue {
378                enum_name,
379                schema,
380                old_value,
381                new_value,
382            } => Some(Self::RenameEnumValue {
383                enum_name: enum_name.clone(),
384                schema: schema.clone(),
385                old_value: new_value.clone(),
386                new_value: old_value.clone(),
387            }),
388            Self::AlterEnum { .. } => None,
389            _ => None,
390        }
391    }
392
393    pub fn table_name(&self) -> Option<&str> {
394        match self {
395            Self::CreateTable { table } | Self::DropTable { table } => Some(&table.name),
396            Self::RenameTable { old_name, .. } => Some(old_name),
397            Self::AcknowledgeTableOptions { table_name, .. } => Some(table_name),
398            Self::AddColumn { table_name, .. }
399            | Self::DropColumn { table_name, .. }
400            | Self::RenameColumn { table_name, .. }
401            | Self::AlterColumn { table_name, .. }
402            | Self::AddForeignKey { table_name, .. }
403            | Self::DropForeignKey { table_name, .. }
404            | Self::AddIndex { table_name, .. }
405            | Self::DropIndex { table_name, .. }
406            | Self::AddConstraint { table_name, .. }
407            | Self::DropConstraint { table_name, .. }
408            | Self::CreateTrigger { table_name, .. }
409            | Self::AlterTrigger { table_name, .. }
410            | Self::DropTrigger { table_name, .. } => Some(table_name),
411            _ => None,
412        }
413    }
414
415    pub fn entity_name(&self) -> Cow<'_, str> {
416        match self {
417            Self::CreateTable { table } | Self::DropTable { table } => {
418                if table.schema.is_some() {
419                    Cow::Owned(table.qualified_name())
420                } else {
421                    Cow::Borrowed(&table.name)
422                }
423            }
424            Self::RenameTable { old_name, .. } => Cow::Borrowed(old_name),
425            Self::AcknowledgeTableOptions { table_name, .. } => Cow::Borrowed(table_name),
426            Self::AddColumn { column, .. } | Self::DropColumn { column, .. } => {
427                Cow::Borrowed(&column.name)
428            }
429            Self::RenameColumn { old_name, .. } => Cow::Borrowed(old_name),
430            Self::AlterColumn { old, .. } => Cow::Borrowed(&old.name),
431            Self::AddForeignKey { foreign_key, .. } | Self::DropForeignKey { foreign_key, .. } => {
432                Cow::Borrowed(&foreign_key.name)
433            }
434            Self::AddIndex { index, .. } | Self::DropIndex { index, .. } => {
435                Cow::Borrowed(&index.name)
436            }
437            Self::AddConstraint { constraint, .. } | Self::DropConstraint { constraint, .. } => {
438                Cow::Borrowed(constraint.name())
439            }
440            Self::CreateTrigger { trigger, .. } | Self::DropTrigger { trigger, .. } => {
441                Cow::Borrowed(trigger.name.as_deref().unwrap_or(""))
442            }
443            Self::AlterTrigger { old, .. } => Cow::Borrowed(old.name.as_deref().unwrap_or("")),
444            Self::CreateFunction { function } | Self::DropFunction { function } => {
445                if function.schema.is_some() {
446                    Cow::Owned(function.qualified_name())
447                } else {
448                    Cow::Borrowed(&function.name)
449                }
450            }
451            Self::AlterFunction { old, .. } => {
452                if old.schema.is_some() {
453                    Cow::Owned(old.qualified_name())
454                } else {
455                    Cow::Borrowed(&old.name)
456                }
457            }
458            Self::CreateView { view } | Self::DropView { view } => {
459                if view.schema.is_some() {
460                    Cow::Owned(view.qualified_name())
461                } else {
462                    Cow::Borrowed(&view.name)
463                }
464            }
465            Self::ReplaceView { old, .. } => {
466                if old.schema.is_some() {
467                    Cow::Owned(old.qualified_name())
468                } else {
469                    Cow::Borrowed(&old.name)
470                }
471            }
472            Self::CreateExtension { extension } | Self::DropExtension { extension } => {
473                if extension.schema.is_some() {
474                    Cow::Owned(extension.qualified_name())
475                } else {
476                    Cow::Borrowed(&extension.name)
477                }
478            }
479            Self::CreateEnum { enum_def } | Self::DropEnum { enum_def } => {
480                if enum_def.schema.is_some() {
481                    Cow::Owned(enum_def.qualified_name())
482                } else {
483                    Cow::Borrowed(&enum_def.name)
484                }
485            }
486            Self::RenameEnumValue {
487                enum_name, schema, ..
488            } => {
489                if let Some(schema) = schema {
490                    Cow::Owned(format!("{schema}.{enum_name}"))
491                } else {
492                    Cow::Borrowed(enum_name)
493                }
494            }
495            Self::AlterEnum { old, .. } => {
496                if old.schema.is_some() {
497                    Cow::Owned(old.qualified_name())
498                } else {
499                    Cow::Borrowed(&old.name)
500                }
501            }
502            Self::Statement { up, .. } => Cow::Borrowed(up),
503        }
504    }
505
506    pub fn type_name(&self) -> &'static str {
507        match self {
508            Self::CreateTable { .. } => "create_table",
509            Self::DropTable { .. } => "drop_table",
510            Self::RenameTable { .. } => "rename_table",
511            Self::AcknowledgeTableOptions { .. } => "acknowledge_table_options",
512            Self::AddColumn { .. } => "add_column",
513            Self::DropColumn { .. } => "drop_column",
514            Self::RenameColumn { .. } => "rename_column",
515            Self::AlterColumn { .. } => "alter_column",
516            Self::AddForeignKey { .. } => "add_foreign_key",
517            Self::DropForeignKey { .. } => "drop_foreign_key",
518            Self::AddIndex { .. } => "add_index",
519            Self::DropIndex { .. } => "drop_index",
520            Self::AddConstraint { .. } => "add_constraint",
521            Self::DropConstraint { .. } => "drop_constraint",
522            Self::Statement { .. } => "statement",
523            Self::CreateFunction { .. } => "create_function",
524            Self::AlterFunction { .. } => "alter_function",
525            Self::DropFunction { .. } => "drop_function",
526            Self::CreateTrigger { .. } => "create_trigger",
527            Self::AlterTrigger { .. } => "alter_trigger",
528            Self::DropTrigger { .. } => "drop_trigger",
529            Self::CreateView { .. } => "create_view",
530            Self::DropView { .. } => "drop_view",
531            Self::ReplaceView { .. } => "replace_view",
532            Self::CreateExtension { .. } => "create_extension",
533            Self::DropExtension { .. } => "drop_extension",
534            Self::CreateEnum { .. } => "create_enum",
535            Self::DropEnum { .. } => "drop_enum",
536            Self::RenameEnumValue { .. } => "rename_enum_value",
537            Self::AlterEnum { .. } => "alter_enum",
538        }
539    }
540
541    pub fn entity_kind(&self) -> Option<EntityKind> {
542        match self {
543            Self::CreateTable { .. }
544            | Self::DropTable { .. }
545            | Self::AcknowledgeTableOptions { .. } => Some(EntityKind::Table),
546            Self::CreateFunction { .. }
547            | Self::AlterFunction { .. }
548            | Self::DropFunction { .. } => Some(EntityKind::Function),
549            Self::CreateEnum { .. }
550            | Self::RenameEnumValue { .. }
551            | Self::AlterEnum { .. }
552            | Self::DropEnum { .. } => Some(EntityKind::Enum),
553            Self::CreateExtension { .. } | Self::DropExtension { .. } => {
554                Some(EntityKind::Extension)
555            }
556            Self::CreateView { .. } | Self::DropView { .. } | Self::ReplaceView { .. } => {
557                Some(EntityKind::View)
558            }
559            Self::AddColumn { .. }
560            | Self::DropColumn { .. }
561            | Self::AlterColumn { .. }
562            | Self::RenameColumn { .. } => Some(EntityKind::Column),
563            Self::AddForeignKey { .. } | Self::DropForeignKey { .. } => {
564                Some(EntityKind::ForeignKey)
565            }
566            Self::AddIndex { .. } | Self::DropIndex { .. } => Some(EntityKind::Index),
567            Self::AddConstraint { .. } | Self::DropConstraint { .. } => {
568                Some(EntityKind::Constraint)
569            }
570            Self::CreateTrigger { .. } | Self::AlterTrigger { .. } | Self::DropTrigger { .. } => {
571                Some(EntityKind::Trigger)
572            }
573            Self::RenameTable { .. } | Self::Statement { .. } => None,
574        }
575    }
576
577    pub fn is_create(&self) -> bool {
578        matches!(
579            self,
580            Self::CreateTable { .. }
581                | Self::CreateFunction { .. }
582                | Self::CreateEnum { .. }
583                | Self::CreateExtension { .. }
584                | Self::CreateView { .. }
585                | Self::CreateTrigger { .. }
586                | Self::RenameEnumValue { .. }
587                | Self::AlterEnum { .. }
588                | Self::AddColumn { .. }
589                | Self::AddForeignKey { .. }
590                | Self::AddIndex { .. }
591                | Self::AddConstraint { .. }
592                | Self::AcknowledgeTableOptions { .. }
593        )
594    }
595
596    pub fn is_drop(&self) -> bool {
597        matches!(
598            self,
599            Self::DropTable { .. }
600                | Self::DropFunction { .. }
601                | Self::DropEnum { .. }
602                | Self::DropExtension { .. }
603                | Self::DropView { .. }
604                | Self::DropTrigger { .. }
605                | Self::DropColumn { .. }
606                | Self::DropForeignKey { .. }
607                | Self::DropIndex { .. }
608                | Self::DropConstraint { .. }
609        )
610    }
611
612    pub fn forward_deps(&self) -> Vec<Dep> {
613        match self {
614            Self::CreateTable { table } | Self::DropTable { table } => {
615                let mut deps = vec![Dep::all_of(EntityKind::Extension)];
616                for col in &table.columns {
617                    deps.push(Dep::new(EntityKind::Enum, &col.col_type));
618                }
619                deps
620            }
621            Self::AddColumn { table_name, column }
622            | Self::DropColumn {
623                table_name, column, ..
624            } => {
625                vec![
626                    Dep::new(EntityKind::Table, table_name),
627                    Dep::new(EntityKind::Enum, &column.col_type),
628                ]
629            }
630            Self::AlterColumn {
631                table_name, new, ..
632            } => {
633                vec![
634                    Dep::new(EntityKind::Table, table_name),
635                    Dep::new(EntityKind::Enum, &new.col_type),
636                ]
637            }
638            Self::AddForeignKey {
639                table_name,
640                foreign_key,
641            }
642            | Self::DropForeignKey {
643                table_name,
644                foreign_key,
645                ..
646            } => {
647                vec![
648                    Dep::new(EntityKind::Table, table_name),
649                    Dep::new(EntityKind::Table, &foreign_key.to_table),
650                ]
651            }
652            Self::AddIndex { table_name, .. } | Self::DropIndex { table_name, .. } => {
653                vec![Dep::new(EntityKind::Table, table_name)]
654            }
655            Self::AddConstraint { table_name, .. } | Self::DropConstraint { table_name, .. } => {
656                vec![Dep::new(EntityKind::Table, table_name)]
657            }
658            Self::CreateTrigger {
659                table_name,
660                trigger,
661            }
662            | Self::AlterTrigger {
663                table_name,
664                new: trigger,
665                ..
666            } => {
667                let mut deps = vec![Dep::new(EntityKind::Table, table_name)];
668                if let Some(fn_name) = trigger.function_name.as_deref() {
669                    deps.push(Dep::new(EntityKind::Function, fn_name));
670                }
671                deps
672            }
673            Self::DropTrigger {
674                table_name,
675                trigger,
676            } => {
677                let mut deps = vec![Dep::new(EntityKind::Table, table_name)];
678                if let Some(fn_name) = trigger.function_name.as_deref() {
679                    deps.push(Dep::new(EntityKind::Function, fn_name));
680                }
681                deps
682            }
683            Self::CreateFunction { .. }
684            | Self::AlterFunction { .. }
685            | Self::DropFunction { .. } => {
686                vec![Dep::all_of(EntityKind::Extension)]
687            }
688            Self::CreateView { .. } | Self::ReplaceView { .. } | Self::DropView { .. } => {
689                vec![
690                    Dep::all_of(EntityKind::Table),
691                    Dep::all_of(EntityKind::Function),
692                ]
693            }
694            Self::CreateExtension { .. } | Self::DropExtension { .. } => vec![],
695            Self::CreateEnum { .. }
696            | Self::RenameEnumValue { .. }
697            | Self::AlterEnum { .. }
698            | Self::DropEnum { .. } => vec![],
699            Self::AcknowledgeTableOptions { table_name, .. } => {
700                vec![Dep::new(EntityKind::Table, table_name)]
701            }
702            Self::RenameTable { .. } | Self::RenameColumn { .. } | Self::Statement { .. } => vec![],
703        }
704    }
705
706    pub fn backward_deps(&self) -> Vec<Dep> {
707        match self {
708            Self::AlterColumn {
709                table_name, old, ..
710            } => {
711                vec![
712                    Dep::new(EntityKind::Table, table_name),
713                    Dep::new(EntityKind::Enum, &old.col_type),
714                ]
715            }
716            _ => self.forward_deps(),
717        }
718    }
719
720    /// Returns the primary entity label used for deterministic migration names.
721    pub fn entity_label(&self) -> Option<&str> {
722        match self {
723            Self::CreateTable { table } | Self::DropTable { table } => Some(&table.name),
724            Self::RenameTable { new_name, .. } => Some(new_name),
725            Self::AcknowledgeTableOptions { table_name, .. }
726            | Self::AddColumn { table_name, .. }
727            | Self::DropColumn { table_name, .. }
728            | Self::RenameColumn { table_name, .. }
729            | Self::AlterColumn { table_name, .. }
730            | Self::AddForeignKey { table_name, .. }
731            | Self::DropForeignKey { table_name, .. }
732            | Self::AddIndex { table_name, .. }
733            | Self::DropIndex { table_name, .. }
734            | Self::AddConstraint { table_name, .. }
735            | Self::DropConstraint { table_name, .. }
736            | Self::CreateTrigger { table_name, .. }
737            | Self::AlterTrigger { table_name, .. }
738            | Self::DropTrigger { table_name, .. } => Some(table_name),
739            Self::CreateFunction { function } | Self::DropFunction { function } => {
740                Some(&function.name)
741            }
742            Self::AlterFunction { new, .. } => Some(&new.name),
743            Self::CreateView { view } | Self::DropView { view } => Some(&view.name),
744            Self::ReplaceView { new, .. } => Some(&new.name),
745            Self::CreateExtension { extension } | Self::DropExtension { extension } => {
746                Some(&extension.name)
747            }
748            Self::CreateEnum { enum_def } | Self::DropEnum { enum_def } => Some(&enum_def.name),
749            Self::RenameEnumValue { enum_name, .. } => Some(enum_name),
750            Self::AlterEnum { new, .. } => Some(&new.name),
751            Self::Statement { .. } => None,
752        }
753    }
754
755    /// Returns the stable entity identities touched by this operation.
756    pub fn touched_entities(&self) -> Vec<(EntityKind, String)> {
757        match self {
758            Self::CreateTable { table } | Self::DropTable { table } => {
759                let mut entities = vec![(EntityKind::Table, table.qualified_name())];
760                entities.extend(
761                    table
762                        .foreign_keys
763                        .iter()
764                        .map(|fk| (EntityKind::Table, fk.to_table.clone())),
765                );
766                entities
767            }
768            Self::AddForeignKey {
769                table_name,
770                foreign_key,
771            }
772            | Self::DropForeignKey {
773                table_name,
774                foreign_key,
775                ..
776            } => vec![
777                (EntityKind::Table, table_name.clone()),
778                (EntityKind::Table, foreign_key.to_table.clone()),
779            ],
780            Self::CreateEnum { enum_def }
781            | Self::DropEnum { enum_def }
782            | Self::AlterEnum { new: enum_def, .. } => {
783                vec![(EntityKind::Enum, enum_def.qualified_name())]
784            }
785            Self::RenameEnumValue {
786                enum_name, schema, ..
787            } => vec![(
788                EntityKind::Enum,
789                crate::states::schema_qualified_key(enum_name, schema.as_deref()),
790            )],
791            Self::CreateFunction { function }
792            | Self::DropFunction { function }
793            | Self::AlterFunction { new: function, .. } => {
794                vec![(EntityKind::Function, function.qualified_name())]
795            }
796            Self::CreateView { view }
797            | Self::DropView { view }
798            | Self::ReplaceView { new: view, .. } => {
799                vec![(EntityKind::View, view.qualified_name())]
800            }
801            Self::CreateExtension { extension } | Self::DropExtension { extension } => {
802                vec![(EntityKind::Extension, extension.qualified_name())]
803            }
804            _ => self.table_name().map_or_else(Vec::new, |table| {
805                vec![(EntityKind::Table, table.to_string())]
806            }),
807        }
808    }
809}