safe-migrate 0.9.0

Check PostgreSQL migrations against a synchronized database baseline
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
use super::Resolver;
use crate::_internal::analysis::facts::{
    AlterTableActionFact, ColumnFact, FkFact, PersistenceFact, SelectOutputFact,
    TableConstraintFact,
};
use crate::_internal::analysis::mutations::{
    AlterTable, AlterTableActionMutation, ColumnMutation, CreateTable, DropIndex,
    DropMaterializedViewMutation, DropTable, DropViewMutation, FkMutation, LikeSourceMutation,
    Mutation, PersistenceMutation, Rename,
};
use crate::_internal::analysis::state::AnalysisState;
use crate::_internal::ast::identifiers::{ObjectId, QualifiedName};

impl Resolver {
    #[allow(clippy::too_many_arguments)]
    pub(super) fn resolve_create_table(
        name: &QualifiedName,
        if_not_exists: bool,
        as_select: bool,
        persistence: &PersistenceFact,
        on_commit: Option<crate::_internal::analysis::facts::OnCommitFact>,
        columns: &[ColumnFact],
        foreign_keys: &[FkFact],
        table_constraints: &[TableConstraintFact],
        partition_by: &Option<String>,
        partition_strategy: &Option<String>,
        partition_of: &Option<QualifiedName>,
        partition_bound: &Option<String>,
        inherits: &[QualifiedName],
        like_sources: &[crate::_internal::analysis::facts::LikeSourceFact],
        of_type: &Option<QualifiedName>,
        select_source: &Option<QualifiedName>,
        select_outputs: &[SelectOutputFact],
        select_projection_complete: bool,
        state: &AnalysisState,
    ) -> Mutation {
        let persistence = match persistence {
            PersistenceFact::Permanent => PersistenceMutation::Permanent,
            PersistenceFact::Temporary => PersistenceMutation::Temporary,
            PersistenceFact::Unlogged => PersistenceMutation::Unlogged,
        };
        let on_commit = on_commit.map(|action| match action {
            crate::_internal::analysis::facts::OnCommitFact::PreserveRows => {
                crate::_internal::analysis::mutations::OnCommitMutation::PreserveRows
            }
            crate::_internal::analysis::facts::OnCommitFact::DeleteRows => {
                crate::_internal::analysis::mutations::OnCommitMutation::DeleteRows
            }
            crate::_internal::analysis::facts::OnCommitFact::Drop => {
                crate::_internal::analysis::mutations::OnCommitMutation::Drop
            }
        });
        let mut columns: Vec<ColumnMutation> = columns
            .iter()
            .map(|column| ColumnMutation {
                name: column.name.clone(),
                ty: column.ty.clone(),
                type_modifier: None,
                not_null: column.not_null,
                is_primary_key: column.is_primary_key,
                primary_key_constraint_name: column.primary_key_constraint_name.clone(),
                is_unique: column.is_unique,
                unique_constraint_name: column.unique_constraint_name.clone(),
                default: column.default.clone(),
                generation: column.generation,
                identity_sequence: column.identity_sequence.as_ref().map(|options| {
                    crate::_internal::analysis::mutations::IdentitySequenceOptionsMutation {
                        data_type: options.data_type.clone(),
                        start_value: options.start_value,
                        increment: options.increment,
                        min_value: options.min_value,
                        max_value: options.max_value,
                        cache_size: options.cache_size,
                        cycle: options.cycle,
                        persistence: options.persistence,
                        sequence_name: options
                            .sequence_name
                            .as_ref()
                            .map(|name| Self::resolve_creation_name(name, state)),
                    }
                }),
                generated_expr: column.generated_expr.clone(),
                generated_expr_sql: column.generated_expr_sql.clone(),
            })
            .collect();
        let of_type = of_type
            .as_ref()
            .map(|name| Self::resolve_type_lookup_name(name, state));
        if let Some(type_id) = &of_type
            && let Some(crate::_internal::model::types::TypeOverlay::Present(
                crate::_internal::model::types::TypeState {
                    kind: crate::_internal::model::types::TypeKind::Composite { fields },
                    ..
                },
            )) = state.local.types.get(type_id)
        {
            columns.extend(fields.iter().map(|field| ColumnMutation {
                name: field.name.clone(),
                ty: Some(field.data_type.clone()),
                type_modifier: None,
                not_null: false,
                is_primary_key: false,
                primary_key_constraint_name: None,
                is_unique: false,
                unique_constraint_name: None,
                default: None,
                generation: crate::_internal::analysis::facts::ColumnGeneration::Ordinary,
                identity_sequence: None,
                generated_expr: None,
                generated_expr_sql: None,
            }));
        }
        let mut as_select_columns_known = !as_select;
        if as_select && select_projection_complete {
            let source_id = select_source
                .as_ref()
                .map(|source| Self::resolve_relation_lookup_name(source, state));
            if let Some(source_id) = source_id
                && let Some(crate::_internal::model::relation::RelationOverlay::Present(source)) =
                    state.local.relations.get(&source_id)
            {
                let mut projected = Vec::new();
                let mut complete = true;
                for output in select_outputs {
                    match output {
                        SelectOutputFact::AllColumns => {
                            projected.extend(source.columns.iter().map(|column| ColumnMutation {
                                name: column.name.clone(),
                                ty: column.data_type.clone(),
                                type_modifier: column.type_modifier,
                                not_null: false,
                                is_primary_key: false,
                                primary_key_constraint_name: None,
                                is_unique: false,
                                unique_constraint_name: None,
                                default: None,
                                generation:
                                    crate::_internal::analysis::facts::ColumnGeneration::Ordinary,
                                identity_sequence: None,
                                generated_expr: None,
                                generated_expr_sql: None,
                            }));
                        }
                        SelectOutputFact::Column {
                            source_name,
                            output_name,
                        } => {
                            let Some(column) = source.get_column(source_name) else {
                                complete = false;
                                break;
                            };
                            projected.push(ColumnMutation {
                                name: output_name.clone(),
                                ty: column.data_type.clone(),
                                type_modifier: column.type_modifier,
                                not_null: false,
                                is_primary_key: false,
                                primary_key_constraint_name: None,
                                is_unique: false,
                                unique_constraint_name: None,
                                default: None,
                                generation:
                                    crate::_internal::analysis::facts::ColumnGeneration::Ordinary,
                                identity_sequence: None,
                                generated_expr: None,
                                generated_expr_sql: None,
                            });
                        }
                    }
                }
                if complete {
                    columns = projected;
                    as_select_columns_known = true;
                }
            }
        }
        let foreign_keys = foreign_keys
            .iter()
            .map(|foreign_key| FkMutation {
                constraint_name: foreign_key.constraint_name.clone(),
                to_table: Self::resolve_relation_lookup_name(&foreign_key.references, state),
                from_columns: foreign_key.from_columns.clone(),
                to_columns: foreign_key.to_columns.clone(),
            })
            .collect();
        Mutation::CreateTable(CreateTable {
            id: Self::resolve_creation_name(name, state),
            if_not_exists,
            as_select,
            as_select_columns_known,
            persistence,
            on_commit,
            columns,
            foreign_keys,
            table_constraints: table_constraints.to_vec(),
            partition_by: partition_by.clone(),
            partition_strategy: partition_strategy.clone(),
            partition_of: partition_of
                .as_ref()
                .map(|parent| Self::resolve_relation_lookup_name(parent, state)),
            partition_bound: partition_bound.clone(),
            inherits: inherits
                .iter()
                .map(|parent| Self::resolve_relation_lookup_name(parent, state))
                .collect(),
            like_sources: like_sources
                .iter()
                .map(|source| LikeSourceMutation {
                    relation: Self::resolve_relation_lookup_name(&source.relation, state),
                    properties: source.properties,
                })
                .collect(),
            of_type,
        })
    }

    pub(super) fn resolve_alter_table(
        name: &QualifiedName,
        only: bool,
        actions: &[AlterTableActionFact],
        state: &AnalysisState,
    ) -> Vec<Mutation> {
        let id = Self::resolve_relation_lookup_name(name, state);
        let mut mutations = Vec::with_capacity(actions.len());
        for action_fact in actions {
            let action = match action_fact {
                AlterTableActionFact::AddColumn {
                    name,
                    ty,
                    if_not_exists,
                    not_null,
                    default,
                    generation,
                    identity_sequence,
                    generated_expr,
                    generated_expr_sql,
                } => AlterTableActionMutation::AddColumn {
                    name: name.clone(),
                    ty: ty.clone(),
                    if_not_exists: *if_not_exists,
                    not_null: *not_null,
                    default: default.clone(),
                    depends_on: None,
                    generation: *generation,
                    identity_sequence: identity_sequence.as_ref().map(|options| {
                        crate::_internal::analysis::mutations::IdentitySequenceOptionsMutation {
                            data_type: options.data_type.clone(),
                            start_value: options.start_value,
                            increment: options.increment,
                            min_value: options.min_value,
                            max_value: options.max_value,
                            cache_size: options.cache_size,
                            cycle: options.cycle,
                            persistence: options.persistence,
                            sequence_name: options.sequence_name.as_ref().map(|name| {
                                Self::resolve_creation_name(name, state)
                            }),
                        }
                    }),
                    generated_expr: generated_expr.clone(),
                    generated_expr_sql: generated_expr_sql.clone(),
                },
                AlterTableActionFact::DropColumn {
                    name,
                    if_exists,
                    cascade,
                } => AlterTableActionMutation::DropColumn {
                    name: name.clone(),
                    if_exists: *if_exists,
                    cascade: *cascade,
                },
                AlterTableActionFact::RenameColumn { from, to } => {
                    AlterTableActionMutation::RenameColumn {
                        from: from.resolve(),
                        to: to.resolve(),
                    }
                }
                AlterTableActionFact::RenameTo { new_name } => {
                    let mut new_id = ObjectId::new(id.schema.clone(), new_name.resolve());
                    new_id.inferred_schema = id.inferred_schema;
                    mutations.push(Mutation::Rename(Rename {
                        old_id: id.clone(),
                        new_id,
                    }));
                    continue;
                }
                AlterTableActionFact::SetSchema { new_schema } => {
                    mutations.push(Mutation::Rename(Rename {
                        old_id: id.clone(),
                        new_id: ObjectId::new(new_schema, &id.name),
                    }));
                    continue;
                }
                AlterTableActionFact::AddForeignKey {
                    constraint_name,
                    references,
                    from_columns,
                    to_columns,
                    not_valid,
                } => {
                    let to_table = Self::resolve_relation_lookup_name(references, state);
                    AlterTableActionMutation::AddForeignKey {
                        constraint_name: constraint_name.clone(),
                        to_table,
                        from_columns: from_columns.clone(),
                        to_columns: to_columns.clone(),
                        not_valid: *not_valid,
                    }
                }
                AlterTableActionFact::AlterConstraint { name, deferrable } => {
                    AlterTableActionMutation::AlterConstraint {
                        name: name.clone(),
                        deferrable: *deferrable,
                    }
                }
                AlterTableActionFact::RenameConstraint { old_name, new_name } => {
                    AlterTableActionMutation::RenameConstraint {
                        old_name: old_name.clone(),
                        new_name: new_name.clone(),
                    }
                }
                AlterTableActionFact::DropConstraint {
                    name,
                    if_exists,
                    cascade,
                } => AlterTableActionMutation::DropConstraint {
                    name: name.clone(),
                    if_exists: *if_exists,
                    cascade: *cascade,
                },
                AlterTableActionFact::AddCheckConstraint {
                    constraint_name,
                    definition,
                    columns,
                    columns_complete,
                    not_valid,
                } => AlterTableActionMutation::AddCheckConstraint {
                    constraint_name: constraint_name.clone(),
                    definition: definition.clone(),
                    columns: columns.clone(),
                    columns_complete: *columns_complete,
                    not_valid: *not_valid,
                },
                AlterTableActionFact::AddUniqueConstraint {
                    constraint_name,
                    columns,
                    using_index,
                } => AlterTableActionMutation::AddUniqueConstraint {
                    constraint_name: constraint_name.clone(),
                    columns: columns.clone(),
                    using_index: using_index
                        .as_ref()
                        .map(|name| Self::resolve_constraint_index_name(name, &id)),
                },
                AlterTableActionFact::AddPrimaryKeyConstraint {
                    constraint_name,
                    columns,
                    using_index,
                } => AlterTableActionMutation::AddPrimaryKeyConstraint {
                    constraint_name: constraint_name.clone(),
                    columns: columns.clone(),
                    using_index: using_index
                        .as_ref()
                        .map(|name| Self::resolve_constraint_index_name(name, &id)),
                },
                AlterTableActionFact::AddExcludeConstraint {
                    constraint_name,
                    columns,
                    columns_complete,
                } => AlterTableActionMutation::AddExcludeConstraint {
                    constraint_name: constraint_name.clone(),
                    columns: columns.clone(),
                    columns_complete: *columns_complete,
                },
                AlterTableActionFact::SetNotNull { column } => {
                    AlterTableActionMutation::SetNotNull {
                        column: column.clone(),
                    }
                }
                AlterTableActionFact::DropNotNull { column } => {
                    AlterTableActionMutation::DropNotNull {
                        column: column.clone(),
                    }
                }
                AlterTableActionFact::SetType {
                    column,
                    ty,
                    has_using,
                } => AlterTableActionMutation::SetType {
                    column: column.clone(),
                    ty: ty.clone(),
                    has_using: *has_using,
                },
                AlterTableActionFact::SetDefault { column, default } => {
                    AlterTableActionMutation::SetDefault {
                        column: column.clone(),
                        default: default.clone(),
                    }
                }
                AlterTableActionFact::ValidateConstraint { constraint_name } => {
                    AlterTableActionMutation::ValidateConstraint {
                        constraint_name: constraint_name.clone(),
                    }
                }
                AlterTableActionFact::AttachPartition { child, strategy, bound } => {
                    AlterTableActionMutation::AttachPartition {
                        child: Self::resolve_relation_lookup_name(child, state),
                        strategy: strategy.clone(),
                        bound: bound.clone(),
                    }
                }
                AlterTableActionFact::DetachPartition { child, mode } => {
                    AlterTableActionMutation::DetachPartition {
                        child: Self::resolve_relation_lookup_name(child, state),
                        mode: *mode,
                    }
                }
                AlterTableActionFact::SetStorage { column, mode } => {
                    AlterTableActionMutation::SetStorage {
                        column: column.clone(),
                        mode: mode.clone(),
                    }
                }
                AlterTableActionFact::SetCompression { column, method } => {
                    AlterTableActionMutation::SetCompression {
                        column: column.clone(),
                        method: method.clone(),
                    }
                }
                AlterTableActionFact::SetStatistics { column, target } => {
                    AlterTableActionMutation::SetStatistics {
                        column: column.clone(),
                        target: *target,
                    }
                }
                AlterTableActionFact::DropExpression { column, if_exists } => {
                    AlterTableActionMutation::DropGeneratedExpression {
                        column: column.clone(),
                        if_exists: *if_exists,
                    }
                }
                AlterTableActionFact::SetAccessMethod { access_method } => {
                    AlterTableActionMutation::SetAccessMethod {
                        access_method: access_method.clone(),
                    }
                }
                AlterTableActionFact::DisableTrigger { trigger_name } => {
                    AlterTableActionMutation::DisableTrigger {
                        trigger_name: trigger_name.clone(),
                    }
                }
                AlterTableActionFact::EnableTrigger { trigger_name } => {
                    AlterTableActionMutation::EnableTrigger {
                        trigger_name: trigger_name.clone(),
                    }
                }
                AlterTableActionFact::EnableAlwaysTrigger { trigger_name } => {
                    AlterTableActionMutation::SetTriggerMode {
                        trigger_name: trigger_name.clone(),
                        mode: crate::_internal::model::trigger::TriggerEnableMode::Always,
                    }
                }
                AlterTableActionFact::EnableReplicaTrigger { trigger_name } => {
                    AlterTableActionMutation::SetTriggerMode {
                        trigger_name: trigger_name.clone(),
                        mode: crate::_internal::model::trigger::TriggerEnableMode::Replica,
                    }
                }
                AlterTableActionFact::SetRuleMode { rule_name, mode } => {
                    AlterTableActionMutation::SetRuleMode {
                        rule_name: rule_name.clone(),
                        mode: match mode {
                            crate::_internal::analysis::facts::RuleEnableModeFact::Origin => {
                                crate::_internal::model::relation::RuleEnableMode::Origin
                            }
                            crate::_internal::analysis::facts::RuleEnableModeFact::Disabled => {
                                crate::_internal::model::relation::RuleEnableMode::Disabled
                            }
                            crate::_internal::analysis::facts::RuleEnableModeFact::Replica => {
                                crate::_internal::model::relation::RuleEnableMode::Replica
                            }
                            crate::_internal::analysis::facts::RuleEnableModeFact::Always => {
                                crate::_internal::model::relation::RuleEnableMode::Always
                            }
                        },
                    }
                }
                AlterTableActionFact::SetTablespace { tablespace } => {
                    AlterTableActionMutation::SetTablespace {
                        tablespace: tablespace.clone(),
                    }
                }
                AlterTableActionFact::SetLogged => AlterTableActionMutation::SetPersistence {
                    persistence: crate::_internal::model::relation::Persistence::Permanent,
                },
                AlterTableActionFact::SetUnlogged => AlterTableActionMutation::SetPersistence {
                    persistence: crate::_internal::model::relation::Persistence::Unlogged,
                },
                AlterTableActionFact::ClusterOn { index } => AlterTableActionMutation::SetCluster {
                    index: Some(Self::resolve_constraint_index_name(index, &id)),
                },
                AlterTableActionFact::SetWithoutCluster => {
                    AlterTableActionMutation::SetCluster { index: None }
                }
                AlterTableActionFact::ReplicaIdentity { option } => {
                    AlterTableActionMutation::SetReplicaIdentity {
                        option: match option {
                            crate::_internal::analysis::facts::ReplicaIdentityFact::Default =>
                                crate::_internal::analysis::mutations::ReplicaIdentityMutation::Default,
                            crate::_internal::analysis::facts::ReplicaIdentityFact::Full =>
                                crate::_internal::analysis::mutations::ReplicaIdentityMutation::Full,
                            crate::_internal::analysis::facts::ReplicaIdentityFact::Nothing =>
                                crate::_internal::analysis::mutations::ReplicaIdentityMutation::Nothing,
                            crate::_internal::analysis::facts::ReplicaIdentityFact::UsingIndex(index) =>
                                crate::_internal::analysis::mutations::ReplicaIdentityMutation::UsingIndex(
                                    Self::resolve_constraint_index_name(index, &id),
                                ),
                        },
                    }
                }
                AlterTableActionFact::ForceRls => {
                    AlterTableActionMutation::SetForceRowSecurity { enabled: true }
                }
                AlterTableActionFact::NoForceRls => {
                    AlterTableActionMutation::SetForceRowSecurity { enabled: false }
                }
                AlterTableActionFact::EnableRls => {
                    AlterTableActionMutation::SetRowSecurity { enabled: true }
                }
                AlterTableActionFact::DisableRls => {
                    AlterTableActionMutation::SetRowSecurity { enabled: false }
                }
                AlterTableActionFact::SetExpression { column, expr, expression_sql } => {
                    AlterTableActionMutation::SetGeneratedExpression {
                        column: column.clone(),
                        expr: expr.clone(),
                        expression_sql: expression_sql.clone(),
                    }
                }
                AlterTableActionFact::InheritTable { parent } => {
                    AlterTableActionMutation::InheritTable {
                        parent: Self::resolve_relation_lookup_name(parent, state),
                    }
                }
                AlterTableActionFact::NoInheritTable { parent } => {
                    AlterTableActionMutation::NoInheritTable {
                        parent: Self::resolve_relation_lookup_name(parent, state),
                    }
                }
                AlterTableActionFact::OfType { type_name } => {
                    AlterTableActionMutation::SetOfType {
                        type_id: Some(Self::resolve_type_lookup_name(type_name, state)),
                    }
                }
                AlterTableActionFact::NotOf => AlterTableActionMutation::SetOfType { type_id: None },
                AlterTableActionFact::SetOptions { column, attributes } => {
                    AlterTableActionMutation::SetColumnOptions {
                        column: column.clone(),
                        attributes: attributes.clone(),
                    }
                }
                AlterTableActionFact::ResetOptions { column, names } => {
                    AlterTableActionMutation::ResetColumnOptions {
                        column: column.clone(),
                        names: names.clone(),
                    }
                }
                AlterTableActionFact::SetTableOptions { attributes } => {
                    AlterTableActionMutation::SetTableOptions {
                        attributes: attributes.clone(),
                    }
                }
                AlterTableActionFact::ResetTableOptions { names } => {
                    AlterTableActionMutation::ResetTableOptions {
                        names: names.clone(),
                    }
                }
                AlterTableActionFact::Inherit { .. } | AlterTableActionFact::NoInherit { .. } => {
                    AlterTableActionMutation::AlterColumnInheritance
                }
                AlterTableActionFact::MergePartitions { .. }
                | AlterTableActionFact::SplitPartition => {
                    AlterTableActionMutation::PartitionReshape
                }
                AlterTableActionFact::OwnerTo { new_owner } => AlterTableActionMutation::OwnerTo {
                    new_owner: new_owner.clone(),
                },
            };
            mutations.push(Mutation::AlterTable(AlterTable {
                id: id.clone(),
                only,
                action,
            }));
        }
        mutations
    }

    pub(super) fn resolve_drop_table(
        names: &[QualifiedName],
        if_exists: bool,
        cascade: bool,
        state: &AnalysisState,
    ) -> Mutation {
        Mutation::DropTable(DropTable {
            ids: names
                .iter()
                .map(|name| Self::resolve_relation_lookup_name(name, state))
                .collect(),
            if_exists,
            cascade,
        })
    }

    pub(super) fn resolve_drop_view(
        names: &[QualifiedName],
        if_exists: bool,
        cascade: bool,
        state: &AnalysisState,
    ) -> Mutation {
        Mutation::DropView(DropViewMutation {
            ids: names
                .iter()
                .map(|name| Self::resolve_relation_lookup_name(name, state))
                .collect(),
            if_exists,
            cascade,
        })
    }

    pub(super) fn resolve_drop_materialized_view(
        names: &[QualifiedName],
        if_exists: bool,
        cascade: bool,
        state: &AnalysisState,
    ) -> Mutation {
        Mutation::DropMaterializedView(DropMaterializedViewMutation {
            ids: names
                .iter()
                .map(|name| Self::resolve_relation_lookup_name(name, state))
                .collect(),
            if_exists,
            cascade,
        })
    }

    pub(super) fn resolve_drop_indexes(
        names: &[QualifiedName],
        if_exists: bool,
        concurrently: bool,
        cascade: bool,
        state: &AnalysisState,
    ) -> Mutation {
        Mutation::DropIndex(DropIndex {
            ids: names
                .iter()
                .map(|name| Self::resolve_relation_lookup_name(name, state))
                .collect(),
            if_exists,
            concurrently,
            cascade,
        })
    }
}