Skip to main content

safe_migrate/analysis/
mutations.rs

1// FILE: src/analysis/mutations.rs
2use crate::analysis::expr_ir::ExprIr;
3use crate::analysis::facts::{SearchPathTarget, TableConstraintFact};
4use crate::ast::identifiers::ObjectId;
5use crate::model::types::TypeKind;
6
7#[derive(Clone, Debug, PartialEq)]
8pub enum PersistenceMutation {
9    Permanent,
10    Temporary,
11    Unlogged,
12}
13
14#[derive(Clone, Debug, PartialEq)]
15pub enum Mutation {
16    CreateSchema(CreateSchemaMutation),
17    AlterSchema(AlterSchemaMutation),
18    DropSchema(DropSchemaMutation),
19    CreateTable(CreateTable),
20    CreateView(CreateView),
21    CreateMaterializedView(CreateMaterializedView),
22    RefreshMaterializedView(RefreshMaterializedViewMutation),
23    CreateIndex(CreateIndex),
24    CreatePolicy(CreatePolicyMutation),
25    DropPolicy(DropPolicyMutation),
26    CreateTrigger(CreateTriggerMutation),
27    DropTrigger(DropTriggerMutation),
28    AlterTable(AlterTable),
29    CreateType(CreateTypeMutation),
30    AlterType(AlterTypeMutation),
31    CreateDomain(CreateDomainMutation),
32    AlterDomain(AlterDomainMutation),
33    DropDomain(DropDomainMutation),
34    DropType(DropTypeMutation),
35    CreateSequence(CreateSequenceMutation),
36    AlterSequence(AlterSequenceMutation),
37    DropSequence(DropSequenceMutation),
38    Rename(Rename),
39    DropTable(DropTable),
40    DropView(DropViewMutation),
41    DropMaterializedView(DropMaterializedViewMutation),
42    DropIndex(DropIndex),
43    ChangeRelationOwner {
44        id: ObjectId,
45        new_owner: crate::analysis::facts::RoleFact,
46    },
47    SearchPath(SearchPathChange),
48    BeginTransaction,
49    CommitTransaction,
50    CommitAndChain,
51    RollbackTransaction,
52    RollbackAndChain,
53    RollbackToSavepoint(RollbackToSavepointMutation),
54    Savepoint(SavepointMutation),
55    ReleaseSavepoint(ReleaseSavepointMutation),
56    CreateFunction(CreateFunctionMutation),
57    AlterFunction(AlterFunctionMutation),
58    DropFunction(DropFunctionMutation),
59    CreateProcedure(CreateProcedureMutation),
60    AlterProcedure(AlterProcedureMutation),
61    DropProcedure(DropProcedureMutation),
62    CreatePublication(CreatePublicationMutation),
63    AlterPublication(AlterPublicationMutation),
64    DropPublication(DropPublicationMutation),
65    CreateSubscription(CreateSubscriptionMutation),
66    AlterSubscription(AlterSubscriptionMutation),
67    DropSubscription(DropSubscriptionMutation),
68    CreateRole(CreateRoleMutation),
69    AlterRole(AlterRoleMutation),
70    DropRole(DropRoleMutation),
71    Grant(GrantMutation),
72    Revoke(RevokeMutation),
73    CreateDatabase(CreateDatabaseMutation),
74    AlterDatabase(AlterDatabaseMutation),
75    DropDatabase(DropDatabaseMutation),
76    /// Produced by `SET [LOCAL] ROLE` and `SET [LOCAL] SESSION AUTHORIZATION`.
77    /// `role = None` means ROLE NONE / SESSION AUTHORIZATION DEFAULT.
78    /// `local = true` means the active value expires at transaction end.
79    SwitchRole {
80        role: Option<crate::analysis::facts::RoleFact>,
81        local: bool,
82        is_session_auth: bool,
83    },
84    Opaque(OpaqueMutation),
85    Vacuum {
86        table_id: Option<ObjectId>,
87        is_full: bool,
88    },
89}
90
91#[derive(Clone, Debug, PartialEq)]
92pub struct CreateSchemaMutation {
93    pub name: String,
94    pub if_not_exists: bool,
95    pub authorization: Option<crate::analysis::facts::RoleFact>,
96}
97
98#[derive(Clone, Debug, PartialEq)]
99pub enum AlterSchemaMutation {
100    Rename {
101        old_name: String,
102        new_name: String,
103    },
104    OwnerTo {
105        name: String,
106        new_owner: crate::analysis::facts::RoleFact,
107    },
108}
109
110#[derive(Clone, Debug, PartialEq)]
111pub struct DropSchemaMutation {
112    pub names: Vec<String>,
113    pub if_exists: bool,
114    pub cascade: bool,
115}
116
117#[derive(Clone, Debug, PartialEq)]
118pub struct CreatePolicyMutation {
119    pub name: String,
120    pub table: ObjectId,
121    pub permissive: bool,
122    pub command: crate::analysis::facts::PolicyCommand,
123}
124
125#[derive(Clone, Debug, PartialEq)]
126pub struct DropPolicyMutation {
127    pub name: String,
128    pub table: ObjectId,
129    pub if_exists: bool,
130}
131
132#[derive(Clone, Debug, PartialEq)]
133pub struct CreateTriggerMutation {
134    pub name: String,
135    pub table: ObjectId,
136    pub function_id: ObjectId,
137}
138
139#[derive(Clone, Debug, PartialEq)]
140pub struct DropTriggerMutation {
141    pub name: String,
142    pub table: ObjectId,
143    pub if_exists: bool,
144}
145
146#[derive(Clone, Debug, PartialEq)]
147pub struct DropViewMutation {
148    pub ids: Vec<ObjectId>,
149    pub if_exists: bool,
150    pub cascade: bool,
151}
152
153#[derive(Clone, Debug, PartialEq)]
154pub struct DropMaterializedViewMutation {
155    pub ids: Vec<ObjectId>,
156    pub if_exists: bool,
157    pub cascade: bool,
158}
159
160#[derive(Clone, Debug, PartialEq)]
161pub struct CreateMaterializedView {
162    pub id: ObjectId,
163    pub depends_on: Vec<ObjectId>,
164}
165
166#[derive(Clone, Debug, PartialEq)]
167pub struct RefreshMaterializedViewMutation {
168    pub id: ObjectId,
169    pub concurrently: bool,
170}
171
172#[derive(Clone, Debug, PartialEq)]
173pub struct CreateSequenceMutation {
174    pub id: ObjectId,
175    pub if_not_exists: bool,
176    pub owned_by: Option<(ObjectId, String)>,
177}
178
179#[derive(Clone, Debug, PartialEq)]
180pub struct AlterSequenceMutation {
181    pub id: ObjectId,
182    pub if_exists: bool,
183    pub action: AlterSequenceActionMutation,
184}
185
186#[derive(Clone, Debug, PartialEq)]
187pub enum AlterSequenceActionMutation {
188    OwnedBy(Option<(ObjectId, String)>),
189    OwnerTo(crate::analysis::facts::RoleFact),
190    RenameTo(ObjectId),
191    SetSchema(ObjectId),
192    Other,
193}
194
195#[derive(Clone, Debug, PartialEq)]
196pub struct DropSequenceMutation {
197    pub ids: Vec<ObjectId>,
198    pub if_exists: bool,
199    pub cascade: bool,
200}
201
202#[derive(Clone, Debug, PartialEq)]
203pub struct CreateDomainMutation {
204    pub id: ObjectId,
205    pub base_type: String,
206}
207
208#[derive(Clone, Debug, PartialEq)]
209pub struct AlterDomainMutation {
210    pub id: ObjectId,
211    pub action: Option<crate::analysis::facts::AlterDomainActionFact>,
212}
213
214#[derive(Clone, Debug, PartialEq)]
215pub struct DropDomainMutation {
216    pub ids: Vec<ObjectId>,
217    pub if_exists: bool,
218    pub cascade: bool,
219}
220
221#[derive(Clone, Debug, PartialEq)]
222pub struct DropTypeMutation {
223    pub ids: Vec<ObjectId>,
224    pub if_exists: bool,
225    pub cascade: bool,
226}
227
228#[derive(Clone, Debug, PartialEq)]
229pub struct CreateTypeMutation {
230    pub id: ObjectId,
231    pub kind: TypeKind,
232}
233
234#[derive(Clone, Debug, PartialEq)]
235pub struct AlterTypeMutation {
236    pub id: ObjectId,
237    pub action: AlterTypeActionMutation,
238}
239
240#[derive(Clone, Debug, PartialEq)]
241pub enum AlterTypeActionMutation {
242    AddValue {
243        new_value: String,
244        neighbor: Option<String>,
245        before: bool,
246    },
247    RenameValue {
248        old_value: String,
249        new_value: String,
250    },
251}
252
253#[derive(Clone, Debug, PartialEq)]
254pub struct CreateTable {
255    pub id: ObjectId,
256    pub if_not_exists: bool,
257    pub as_select: bool,
258    pub persistence: PersistenceMutation,
259    pub columns: Vec<ColumnMutation>,
260    pub foreign_keys: Vec<FkMutation>,
261    pub table_constraints: Vec<TableConstraintFact>,
262    pub partition_by: Option<String>,
263    pub partition_of: Option<ObjectId>,
264    pub partition_type: Option<String>,
265}
266
267#[derive(Clone, Debug, PartialEq)]
268pub struct ColumnMutation {
269    pub name: String,
270    pub ty: Option<String>,
271    pub not_null: bool,
272    pub is_primary_key: bool,
273    pub primary_key_constraint_name: Option<String>,
274    pub is_unique: bool,
275    pub unique_constraint_name: Option<String>,
276    pub default: Option<ExprIr>,
277    pub generation: crate::analysis::facts::ColumnGeneration,
278}
279
280#[derive(Clone, Debug, PartialEq)]
281pub struct FkMutation {
282    pub constraint_name: Option<String>,
283    pub to_table: ObjectId,
284    pub from_columns: Vec<String>,
285    pub to_columns: Vec<String>,
286}
287
288#[derive(Clone, Debug, PartialEq)]
289pub struct CreateView {
290    pub id: ObjectId,
291    pub or_replace: bool,
292    pub depends_on: Vec<ObjectId>,
293}
294
295#[derive(Clone, Debug, PartialEq)]
296pub struct CreateIndex {
297    pub id: ObjectId,
298    pub table: ObjectId,
299    pub if_not_exists: bool,
300    pub concurrently: bool,
301    pub using_method: Option<String>,
302    pub has_predicate: bool,
303    pub unique: bool,
304}
305
306#[derive(Clone, Debug, PartialEq)]
307pub struct AlterTable {
308    pub id: ObjectId,
309    pub action: AlterTableActionMutation,
310}
311
312#[derive(Clone, Debug, PartialEq)]
313pub struct Rename {
314    pub old_id: ObjectId,
315    pub new_id: ObjectId,
316}
317
318#[derive(Clone, Debug, PartialEq)]
319pub struct DropTable {
320    pub id: ObjectId,
321    pub if_exists: bool,
322    pub cascade: bool,
323}
324
325#[derive(Clone, Debug, PartialEq)]
326pub struct DropIndex {
327    pub id: ObjectId,
328    pub if_exists: bool,
329    pub concurrently: bool,
330}
331
332#[derive(Clone, Debug, PartialEq)]
333pub struct SearchPathChange {
334    pub target: SearchPathTarget,
335}
336
337#[derive(Clone, Debug, PartialEq)]
338pub struct SavepointMutation {
339    pub name: String,
340}
341
342#[derive(Clone, Debug, PartialEq)]
343pub struct ReleaseSavepointMutation {
344    pub name: String,
345}
346
347#[derive(Clone, Debug, PartialEq)]
348pub struct RollbackToSavepointMutation {
349    pub name: String,
350}
351
352#[derive(Clone, Debug, PartialEq)]
353pub struct CreateFunctionMutation {
354    pub id: ObjectId,
355    pub or_replace: bool,
356    pub params: Vec<crate::analysis::facts::ParamFact>,
357    pub return_type: Option<crate::analysis::facts::RetTypeFact>,
358    pub options: Vec<crate::analysis::facts::FuncOptionFact>,
359}
360
361#[derive(Clone, Debug, PartialEq)]
362pub struct AlterFunctionMutation {
363    pub id: ObjectId,
364    pub action: crate::analysis::facts::AlterFunctionAction,
365}
366
367#[derive(Clone, Debug, PartialEq)]
368pub struct DropFunctionMutation {
369    pub signatures: Vec<crate::analysis::facts::FunctionSigFact>,
370    pub if_exists: bool,
371    pub cascade: bool,
372}
373
374#[derive(Clone, Debug, PartialEq)]
375pub struct CreateProcedureMutation {
376    pub id: ObjectId,
377    pub or_replace: bool,
378    pub params: Vec<crate::analysis::facts::ParamFact>,
379    pub options: Vec<crate::analysis::facts::FuncOptionFact>,
380}
381
382#[derive(Clone, Debug, PartialEq)]
383pub struct AlterProcedureMutation {
384    pub id: ObjectId,
385    pub action: crate::analysis::facts::AlterFunctionAction,
386}
387
388#[derive(Clone, Debug, PartialEq)]
389pub struct DropProcedureMutation {
390    pub signatures: Vec<crate::analysis::facts::FunctionSigFact>,
391    pub if_exists: bool,
392    pub cascade: bool,
393}
394
395#[derive(Clone, Debug, PartialEq)]
396pub struct CreatePublicationMutation {
397    pub name: String,
398    pub scope: crate::analysis::facts::PublicationScope,
399    pub params: Vec<crate::analysis::facts::AttributeFact>,
400}
401
402#[derive(Clone, Debug, PartialEq)]
403pub struct AlterPublicationMutation {
404    pub name: String,
405}
406
407#[derive(Clone, Debug, PartialEq)]
408pub struct DropPublicationMutation {
409    pub names: Vec<String>,
410    pub if_exists: bool,
411    pub cascade: bool,
412}
413
414#[derive(Clone, Debug, PartialEq)]
415pub struct CreateSubscriptionMutation {
416    pub name: Option<String>,
417    pub connection: crate::analysis::facts::ConnectionTarget,
418    pub publications: Vec<String>,
419    pub params: Option<Vec<crate::analysis::facts::AttributeFact>>,
420}
421
422#[derive(Clone, Debug, PartialEq)]
423pub struct AlterSubscriptionMutation {
424    pub name: String,
425}
426
427#[derive(Clone, Debug, PartialEq)]
428pub struct DropSubscriptionMutation {
429    pub name: String,
430    pub if_exists: bool,
431}
432
433#[derive(Clone, Debug, PartialEq)]
434pub struct CreateRoleMutation {
435    pub name: String,
436    pub inherits: bool,
437}
438
439#[derive(Clone, Debug, PartialEq)]
440pub struct AlterRoleMutation {
441    pub name: crate::analysis::facts::RoleFact,
442    pub inherits: Option<bool>,
443}
444
445#[derive(Clone, Debug, PartialEq)]
446pub struct DropRoleMutation {
447    pub names: Vec<String>,
448    pub if_exists: bool,
449}
450
451#[derive(Clone, Debug, PartialEq)]
452pub enum ResolvedGrantTarget {
453    Tables(Vec<ObjectId>),
454    AllTablesInSchema(Vec<String>),
455}
456
457#[derive(Clone, Debug, PartialEq)]
458pub struct GrantMutation {
459    pub privileges: crate::analysis::facts::PrivilegeSpec,
460    pub target: ResolvedGrantTarget,
461    pub grantees: Vec<crate::analysis::facts::RoleFact>,
462    pub with_grant_option: bool,
463    pub granted_by: Option<crate::analysis::facts::RoleFact>,
464}
465
466#[derive(Clone, Debug, PartialEq)]
467pub struct RevokeMutation {
468    pub grant_option_only: bool,
469    pub privileges: crate::analysis::facts::PrivilegeSpec,
470    pub target: ResolvedGrantTarget,
471    pub revokees: Vec<crate::analysis::facts::RoleFact>,
472    pub granted_by: Option<crate::analysis::facts::RoleFact>,
473    pub cascade: bool,
474}
475
476#[derive(Clone, Debug, PartialEq)]
477pub struct CreateDatabaseMutation {
478    pub name: String,
479    pub options: Vec<crate::analysis::facts::DatabaseOptionFact>,
480}
481
482#[derive(Clone, Debug, PartialEq)]
483pub struct AlterDatabaseMutation {
484    pub id: ObjectId,
485    pub action: crate::analysis::facts::AlterDatabaseAction,
486}
487
488#[derive(Clone, Debug, PartialEq)]
489pub struct DropDatabaseMutation {
490    pub id: ObjectId,
491    pub if_exists: bool,
492}
493
494#[derive(Clone, Debug, PartialEq)]
495pub enum OpaqueMutation {
496    /// Squawk accepted the statement but safe-migrate has no typed extractor
497    /// for it. Treating it as a no-op would leave later analysis falsely exact.
498    UnsupportedStatement,
499    DoBlock,
500    Execute,
501    DynamicSql,
502    PrepareTransaction,
503    SetTransaction,
504    SetConstraints,
505    StateCollision(String),
506    UnresolvedReference {
507        object_kind: crate::report::violations::ObjectKind,
508        object_name: String,
509    },
510}
511
512#[derive(Clone, Debug, PartialEq)]
513pub enum AlterTableActionMutation {
514    AddColumn {
515        name: String,
516        ty: Option<String>,
517        if_not_exists: bool,
518        not_null: bool,
519        default: Option<ExprIr>,
520        depends_on: Option<(ObjectId, String)>,
521        generation: crate::analysis::facts::ColumnGeneration,
522    },
523    DropColumn {
524        name: String,
525        if_exists: bool,
526    },
527    RenameColumn {
528        from: String,
529        to: String,
530    },
531    AddForeignKey {
532        constraint_name: Option<String>,
533        to_table: ObjectId,
534        from_columns: Vec<String>,
535        to_columns: Vec<String>,
536        not_valid: bool,
537    },
538    AlterConstraint {
539        name: Option<String>,
540        deferrable: bool,
541    },
542    RenameConstraint {
543        old_name: String,
544        new_name: String,
545    },
546    DropConstraint {
547        name: String,
548    },
549    AddCheckConstraint {
550        constraint_name: Option<String>,
551        not_valid: bool,
552    },
553    AddUniqueConstraint {
554        constraint_name: Option<String>,
555        using_index: Option<ObjectId>,
556    },
557    AddPrimaryKeyConstraint {
558        constraint_name: Option<String>,
559        using_index: Option<ObjectId>,
560    },
561    AddExcludeConstraint {
562        constraint_name: Option<String>,
563    },
564    SetNotNull {
565        column: String,
566    },
567    DropNotNull {
568        column: String,
569    },
570    SetType {
571        column: String,
572        ty: String,
573        has_using: bool,
574    },
575    SetDefault {
576        column: String,
577        default: Option<ExprIr>,
578    },
579    ValidateConstraint {
580        constraint_name: String,
581    },
582    DisableTrigger {
583        trigger_name: Option<String>,
584    },
585    EnableTrigger {
586        trigger_name: Option<String>,
587    },
588    AttachPartition {
589        child: ObjectId,
590        strategy: Option<String>,
591    },
592    DetachPartition {
593        child: ObjectId,
594    },
595    SetStorage {
596        column: String,
597    },
598    SetAccessMethod,
599    OwnerTo {
600        new_owner: crate::analysis::facts::RoleFact,
601    },
602    Opaque,
603}