Skip to main content

safe_migrate/analysis/
facts.rs

1// FILE: src/analysis/facts.rs
2use crate::analysis::expr_ir::ExprIr;
3use crate::ast::identifiers::{Ident, QualifiedName};
4
5#[derive(Clone, Debug, PartialEq)]
6pub enum PersistenceFact {
7    Permanent,
8    Temporary,
9    Unlogged,
10}
11
12#[derive(Clone, Debug, PartialEq)]
13pub enum PolicyCommand {
14    All,
15    Select,
16    Insert,
17    Update,
18    Delete,
19}
20
21#[derive(Clone, Debug, PartialEq)]
22pub enum SearchPathTarget {
23    Default,
24    Schemas(Vec<String>),
25}
26
27#[derive(Clone, Debug, PartialEq)]
28pub enum TypeCreationKind {
29    Enum { variants: Vec<String> },
30    Range,
31    Composite,
32    Base,
33}
34
35#[derive(Clone, Debug, PartialEq)]
36pub enum AlterViewAction {
37    RenameTo {
38        new_name: Ident,
39    },
40    OwnerTo {
41        new_owner: RoleFact,
42    },
43    SetSchema {
44        new_schema: String,
45    },
46    SetDefault {
47        column: String,
48        default: Option<ExprIr>,
49    },
50    DropDefault {
51        column: String,
52    },
53    RenameColumn {
54        from: Ident,
55        to: Ident,
56    },
57    SetOptions {
58        options: Vec<String>,
59    },
60    ResetOptions {
61        options: Vec<String>,
62    },
63}
64
65#[derive(Clone, Debug, PartialEq)]
66pub enum AlterSchemaActionFact {
67    RenameTo { new_name: Ident },
68    OwnerTo { new_owner: RoleFact },
69}
70
71#[derive(Clone, Debug, PartialEq)]
72pub enum StatementFact {
73    CreateSchema {
74        name: QualifiedName,
75        if_not_exists: bool,
76        authorization: Option<RoleFact>,
77    },
78    AlterSchema {
79        name: QualifiedName,
80        action: AlterSchemaActionFact,
81    },
82    DropSchema {
83        names: Vec<QualifiedName>,
84        if_exists: bool,
85        cascade: bool,
86    },
87    CreateTable {
88        name: QualifiedName,
89        if_not_exists: bool,
90        as_select: bool,
91        persistence: PersistenceFact,
92        columns: Vec<ColumnFact>,
93        foreign_keys: Vec<FkFact>,
94        table_constraints: Vec<TableConstraintFact>,
95        partition_by: Option<String>,
96        partition_of: Option<QualifiedName>,
97        partition_type: Option<String>,
98    },
99    CreateView {
100        name: QualifiedName,
101        or_replace: bool,
102        depends_on: Vec<QualifiedName>,
103    },
104    AlterView {
105        name: QualifiedName,
106        action: AlterViewAction,
107    },
108    CreateMaterializedView {
109        name: QualifiedName,
110        depends_on: Vec<QualifiedName>,
111    },
112    AlterMaterializedView {
113        name: QualifiedName,
114        new_name: Option<Ident>,
115    },
116    RefreshMaterializedView {
117        name: QualifiedName,
118        concurrently: bool,
119    },
120    CreateIndex {
121        name: QualifiedName,
122        relation: QualifiedName,
123        if_not_exists: bool,
124        concurrently: bool,
125        using_method: Option<String>,
126        has_predicate: bool,
127        unique: bool, // Added for CreateIndexFact
128    },
129    CreatePolicy {
130        name: String,
131        table: QualifiedName,
132        permissive: bool,
133        command: PolicyCommand,
134    },
135    DropPolicy {
136        name: String,
137        table: QualifiedName,
138        if_exists: bool,
139    },
140    CreateTrigger {
141        name: String,
142        table: QualifiedName,
143        function: Option<QualifiedName>,
144    },
145    DropTrigger {
146        name: String,
147        table: QualifiedName,
148        if_exists: bool,
149    },
150    AlterTrigger {
151        name: String,
152        table: QualifiedName,
153        new_name: String,
154    },
155    AlterTable {
156        name: QualifiedName,
157        actions: Vec<AlterTableActionFact>,
158    },
159    AlterIndex {
160        name: QualifiedName,
161        actions: Vec<AlterIndexActionFact>,
162    },
163    CreateType(CreateTypeFact),
164    AlterType(AlterTypeFact),
165    CreateDomain {
166        name: QualifiedName,
167        base_type: String,
168    },
169    AlterDomain {
170        name: QualifiedName,
171        action: Option<AlterDomainActionFact>,
172    },
173    DropDomain {
174        names: Vec<QualifiedName>,
175        if_exists: bool,
176        cascade: bool,
177    },
178    DropType {
179        names: Vec<QualifiedName>,
180        if_exists: bool,
181        cascade: bool,
182    },
183    CreateSequence {
184        name: QualifiedName,
185        if_not_exists: bool,
186        owned_by: Option<(QualifiedName, String)>,
187    },
188    AlterSequence {
189        name: QualifiedName,
190        if_exists: bool,
191        action: AlterSequenceActionFact,
192    },
193    DropSequence {
194        names: Vec<QualifiedName>,
195        if_exists: bool,
196        cascade: bool,
197    },
198    DropTable {
199        name: QualifiedName,
200        if_exists: bool,
201        cascade: bool,
202    },
203    DropView {
204        name: QualifiedName,
205        if_exists: bool,
206        cascade: bool,
207    },
208    DropMaterializedView {
209        names: Vec<QualifiedName>,
210        if_exists: bool,
211        cascade: bool,
212    },
213    DropIndex {
214        names: Vec<QualifiedName>,
215        if_exists: bool,
216        concurrently: bool,
217    },
218    SetSearchPath {
219        target: SearchPathTarget,
220    },
221    BeginTransaction,
222    CommitTransaction,
223    CommitAndChain,
224    RollbackTransaction,
225    RollbackAndChain,
226    RollbackToSavepoint {
227        name: String,
228    },
229    Savepoint {
230        name: String,
231    },
232    ReleaseSavepoint {
233        name: String,
234    },
235    PrepareTransaction {
236        name: String,
237    },
238    SetTransaction,
239    SetConstraints,
240    OpaqueBlock,
241    Execute,
242    /// Parsed SQL that changes PostgreSQL metadata but no schema state modeled
243    /// by safe-migrate, such as `COMMENT ON`.
244    SchemaNeutralNoop,
245    Vacuum {
246        relation: Option<QualifiedName>,
247        is_full: bool,
248    },
249    CreateFunction(CreateFunctionFact),
250    AlterFunction(AlterFunctionFact),
251    DropFunction(DropFunctionFact),
252    CreateProcedure(CreateProcedureFact),
253    AlterProcedure(AlterProcedureFact),
254    DropProcedure(DropProcedureFact),
255    CreatePublication(CreatePublicationFact),
256    AlterPublication(AlterPublicationFact),
257    DropPublication(DropPublicationFact),
258    CreateSubscription(CreateSubscriptionFact),
259    AlterSubscription(AlterSubscriptionFact),
260    DropSubscription(DropSubscriptionFact),
261    CreateRole(CreateRoleFact),
262    AlterRole(AlterRoleFact),
263    DropRole(DropRoleFact),
264    Grant(GrantFact),
265    Revoke(RevokeFact),
266    CreateDatabase(CreateDatabaseFact),
267    AlterDatabase(AlterDatabaseFact),
268    DropDatabase(DropDatabaseFact),
269    /// `SET [LOCAL] ROLE { rolename | NONE }` or
270    /// `SET [LOCAL] SESSION AUTHORIZATION { rolename | DEFAULT }`.
271    /// `role = None` means NONE / DEFAULT (restore the session default).
272    /// `local = true` means the change is scoped to the current transaction.
273    /// `is_session_auth = true` means SET SESSION AUTHORIZATION (which also
274    /// updates the session-level default, unlike plain SET ROLE).
275    SetRole {
276        role: Option<RoleFact>,
277        local: bool,
278        is_session_auth: bool,
279    },
280}
281
282#[derive(Clone, Debug, PartialEq)]
283pub enum AlterSequenceActionFact {
284    OwnedBy(Option<(QualifiedName, String)>),
285    OwnerTo(RoleFact),
286    RenameTo(Ident),
287    SetSchema(String),
288    Other,
289}
290
291#[derive(Clone, Debug, PartialEq)]
292pub enum AlterIndexActionFact {
293    RenameTo { new_name: Ident },
294}
295
296#[derive(Clone, Debug, PartialEq)]
297pub struct CreateTypeFact {
298    pub name: QualifiedName,
299    pub kind: TypeCreationKind,
300}
301
302#[derive(Clone, Debug, PartialEq)]
303pub struct AlterTypeFact {
304    pub name: QualifiedName,
305    pub actions: Vec<AlterTypeActionFact>,
306}
307
308#[derive(Clone, Debug, PartialEq)]
309pub enum AlterTypeActionFact {
310    RenameTo {
311        new_name: Ident,
312    },
313    SetSchema {
314        new_schema: String,
315    },
316    AddValue {
317        new_value: String,
318        neighbor: Option<String>,
319        before: bool,
320    },
321    RenameValue {
322        old_value: String,
323        new_value: String,
324    },
325}
326
327#[derive(Clone, Debug, PartialEq, Default)]
328pub enum RoleFact {
329    #[default]
330    Unknown,
331    Named {
332        name: String,
333        via_legacy_group_syntax: bool,
334    },
335    CurrentUser,
336    CurrentRole,
337    SessionUser,
338}
339
340#[derive(Clone, Debug, PartialEq)]
341pub struct CreateRoleFact {
342    pub name: String,
343    pub inherits: bool,
344}
345
346#[derive(Clone, Debug, PartialEq)]
347pub struct AlterRoleFact {
348    pub name: RoleFact,
349    pub inherits: Option<bool>,
350}
351
352#[derive(Clone, Debug, PartialEq)]
353pub struct DropRoleFact {
354    pub names: Vec<String>,
355    pub if_exists: bool,
356}
357
358#[derive(Clone, Debug, PartialEq)]
359pub struct CreateFunctionFact {
360    pub name: QualifiedName,
361    pub or_replace: bool,
362    pub params: Vec<ParamFact>,
363    pub return_type: Option<RetTypeFact>,
364    pub options: Vec<FuncOptionFact>,
365}
366
367#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
368pub struct ParamFact {
369    pub mode: ParamModeFact,
370    pub name: Option<String>,
371    pub ty: String,
372    pub default: Option<ExprIr>,
373}
374
375#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
376pub enum ParamModeFact {
377    In,
378    Out,
379    InOut,
380    Variadic,
381}
382
383#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
384pub enum RetTypeFact {
385    Table(Vec<ColumnFact>),
386    Scalar(String),
387}
388
389#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
390pub enum FuncOptionFact {
391    Language(String),
392    Volatility(VolatilityKind),
393    Security(SecurityKind),
394    Strict(StrictKind),
395    Leakproof(bool),
396    Parallel(String),
397    Cost,
398    Rows,
399    Reset(String),
400    As {
401        definition: Option<String>,
402        obj_file: Option<String>,
403        link_symbol: Option<String>,
404    },
405    Transform,
406    Window,
407    Support,
408    Unknown,
409}
410
411#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
412pub enum VolatilityKind {
413    Immutable,
414    Stable,
415    Volatile,
416}
417#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
418pub enum SecurityKind {
419    Invoker,
420    Definer,
421}
422#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
423pub enum StrictKind {
424    Strict,
425    CalledOnNull,
426    ReturnsNullOnNull,
427}
428
429#[derive(Clone, Debug, PartialEq)]
430pub struct AlterFunctionFact {
431    pub name: QualifiedName,
432    pub params: Vec<String>,
433    pub action: AlterFunctionAction,
434}
435
436#[derive(Clone, Debug, PartialEq)]
437pub enum AlterFunctionAction {
438    Rename { from: String, to: String },
439    OwnerChange(RoleFact),
440    SchemaChange { new_schema: String },
441    DependsOnExtension { extension: String },
442    NoDependsOnExtension { extension: String },
443    OptionsChange(Vec<FuncOptionFact>),
444}
445
446#[derive(Clone, Debug, PartialEq)]
447pub struct DropFunctionFact {
448    pub signatures: Vec<FunctionSigFact>,
449    pub if_exists: bool,
450    pub cascade: bool,
451}
452
453#[derive(Clone, Debug, PartialEq)]
454pub struct FunctionSigFact {
455    pub name: QualifiedName,
456    pub params: Vec<String>,
457}
458
459#[derive(Clone, Debug, PartialEq)]
460pub struct CreateProcedureFact {
461    pub name: QualifiedName,
462    pub or_replace: bool,
463    pub params: Vec<ParamFact>,
464    pub options: Vec<FuncOptionFact>,
465}
466
467#[derive(Clone, Debug, PartialEq)]
468pub struct AlterProcedureFact {
469    pub name: QualifiedName,
470    pub params: Vec<String>,
471    pub action: AlterFunctionAction,
472}
473
474#[derive(Clone, Debug, PartialEq)]
475pub struct DropProcedureFact {
476    pub signatures: Vec<FunctionSigFact>,
477    pub if_exists: bool,
478    pub cascade: bool,
479}
480
481#[derive(Clone, Debug, PartialEq)]
482pub struct CreatePublicationFact {
483    pub name: String,
484    pub scope: PublicationScope,
485    pub params: Vec<AttributeFact>,
486}
487
488#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
489pub enum PublicationScope {
490    AllTables { except: Vec<String> },
491    Explicit(Vec<PublicationObjectFact>),
492}
493
494#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
495pub enum PublicationObjectFact {
496    Table {
497        name: QualifiedName,
498        only: bool,
499        include_partitions: bool,
500        columns: Option<Vec<String>>,
501        row_filter: Option<ExprIr>,
502    },
503    SchemaTables {
504        schema: String,
505        row_filter: Option<ExprIr>,
506    },
507    CurrentSchemaShorthand,
508    Unknown,
509}
510
511#[derive(Clone, Debug, PartialEq)]
512pub struct AlterPublicationFact {
513    pub name: String,
514}
515
516#[derive(Clone, Debug, PartialEq)]
517pub struct DropPublicationFact {
518    pub names: Vec<String>,
519    pub if_exists: bool,
520    pub cascade: bool,
521}
522
523#[derive(Clone, Debug, PartialEq)]
524pub struct CreateSubscriptionFact {
525    pub name: Option<String>,
526    pub connection: ConnectionTarget,
527    pub publications: Vec<String>,
528    pub params: Option<Vec<AttributeFact>>,
529}
530
531#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
532pub enum ConnectionTarget {
533    Literal(Option<String>),
534    Server(Option<String>),
535}
536
537#[derive(Clone, Debug, PartialEq)]
538pub struct AlterSubscriptionFact {
539    pub name: String,
540}
541
542#[derive(Clone, Debug, PartialEq)]
543pub struct DropSubscriptionFact {
544    pub name: String,
545    pub if_exists: bool,
546}
547
548#[derive(Clone, Debug, PartialEq)]
549pub struct GrantFact {
550    pub privileges: PrivilegeSpec,
551    pub target: GrantTarget,
552    pub grantees: Vec<RoleFact>,
553    pub with_grant_option: bool,
554    pub granted_by: Option<RoleFact>,
555}
556
557#[derive(Clone, Debug, PartialEq)]
558pub struct RevokeFact {
559    pub grant_option_only: bool,
560    pub privileges: PrivilegeSpec,
561    pub target: GrantTarget,
562    pub revokees: Vec<RoleFact>,
563    pub granted_by: Option<RoleFact>,
564    pub cascade: bool,
565}
566
567#[derive(Clone, Debug, PartialEq)]
568pub enum PrivilegeSpec {
569    All,
570    List(Vec<PrivilegeFact>),
571}
572
573#[derive(Clone, Debug, PartialEq)]
574pub enum PrivilegeFact {
575    Select,
576    Insert,
577    Update,
578    Delete,
579    Truncate,
580    References,
581    Trigger,
582    Execute,
583    Create,
584    Temporary,
585    AlterSystem,
586    All,
587    Named(String),
588    RoleMembership(String),
589    Unknown,
590}
591
592#[derive(Clone, Debug, PartialEq)]
593pub enum GrantTarget {
594    Tables(Vec<QualifiedName>),
595    AllTablesInSchema(Vec<String>),
596}
597
598#[derive(Clone, Debug, PartialEq)]
599pub struct CreateDatabaseFact {
600    pub name: String,
601    pub options: Vec<DatabaseOptionFact>,
602}
603
604#[derive(Clone, Debug, PartialEq)]
605pub enum DatabaseOptionFact {
606    Owner(DatabaseOptionValue),
607    Template(DatabaseOptionValue),
608    Encoding(DatabaseOptionValue),
609    Tablespace(DatabaseOptionValue),
610    ConnectionLimit(DatabaseOptionValue),
611    Named(String, DatabaseOptionValue),
612    Unknown(DatabaseOptionValue),
613}
614
615#[derive(Clone, Debug, PartialEq)]
616pub enum DatabaseOptionValue {
617    Default,
618    Literal(Option<String>),
619}
620
621#[derive(Clone, Debug, PartialEq)]
622pub struct AlterDatabaseFact {
623    pub name: QualifiedName,
624    pub action: AlterDatabaseAction,
625}
626
627#[derive(Clone, Debug, PartialEq)]
628pub enum AlterDatabaseAction {
629    Rename { to: String },
630    OwnerChange(RoleFact),
631    TablespaceChange { new_tablespace: String },
632    SetConfigParam { param: String },
633    ResetConfigParam { param: Option<String> },
634    RefreshCollationVersion,
635    OptionChanges(Vec<DatabaseOptionFact>),
636}
637
638#[derive(Clone, Debug, PartialEq)]
639pub struct DropDatabaseFact {
640    pub name: QualifiedName,
641    pub if_exists: bool,
642}
643
644#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
645pub struct AttributeFact {
646    pub name: String,
647    pub value: String,
648}
649
650#[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
651pub enum ColumnGeneration {
652    Ordinary,
653    Serial,
654    Identity,
655}
656
657#[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
658pub struct ColumnFact {
659    pub name: String,
660    pub ty: Option<String>,
661    pub not_null: bool,
662    pub is_primary_key: bool,
663    pub primary_key_constraint_name: Option<String>,
664    pub is_unique: bool,
665    pub unique_constraint_name: Option<String>,
666    pub default: Option<ExprIr>,
667    pub generation: ColumnGeneration,
668}
669
670#[derive(Clone, Debug, PartialEq)]
671pub struct FkFact {
672    pub constraint_name: Option<String>,
673    pub references: QualifiedName,
674    pub from_columns: Vec<String>,
675    pub to_columns: Vec<String>,
676}
677
678#[derive(Clone, Debug, PartialEq)]
679pub enum TableConstraintFact {
680    PrimaryKey {
681        constraint_name: Option<String>,
682        columns: Vec<String>,
683    },
684    Unique {
685        constraint_name: Option<String>,
686        columns: Vec<String>,
687    },
688    Check,
689    Exclude,
690}
691
692#[derive(Clone, Debug, PartialEq)]
693pub enum AlterDomainActionFact {
694    AddConstraint,
695    DropConstraint,
696    DropDefault,
697    DropNotNull,
698    OwnerChange,
699    RenameConstraint,
700    RenameTo,
701    SetDefault,
702    SetNotNull,
703    SetSchema,
704    ValidateConstraint,
705}
706
707#[derive(Clone, Debug, PartialEq)]
708pub enum AlterTableActionFact {
709    AddColumn {
710        name: String,
711        ty: Option<String>,
712        if_not_exists: bool,
713        not_null: bool,
714        default: Option<ExprIr>,
715        generation: ColumnGeneration,
716    },
717    DropColumn {
718        name: String,
719        if_exists: bool,
720    },
721    RenameColumn {
722        from: Ident,
723        to: Ident,
724    },
725    RenameTo {
726        new_name: Ident,
727    },
728    AddForeignKey {
729        constraint_name: Option<String>,
730        references: QualifiedName,
731        from_columns: Vec<String>,
732        to_columns: Vec<String>,
733        not_valid: bool,
734    },
735    AlterConstraint {
736        name: Option<String>,
737        deferrable: bool,
738    },
739    RenameConstraint {
740        old_name: String,
741        new_name: String,
742    },
743    DropConstraint {
744        name: String,
745    },
746    AddCheckConstraint {
747        constraint_name: Option<String>,
748        not_valid: bool,
749    },
750    AddUniqueConstraint {
751        constraint_name: Option<String>,
752        using_index: Option<QualifiedName>,
753    },
754    AddPrimaryKeyConstraint {
755        constraint_name: Option<String>,
756        using_index: Option<QualifiedName>,
757    },
758    AddExcludeConstraint {
759        constraint_name: Option<String>,
760    },
761    SetNotNull {
762        column: String,
763    },
764    DropNotNull {
765        column: String,
766    },
767    SetType {
768        column: String,
769        ty: String,
770        has_using: bool,
771    },
772    SetDefault {
773        column: String,
774        default: Option<ExprIr>,
775    },
776    SetExpression {
777        column: String,
778        expr: ExprIr,
779    },
780    SetOptions {
781        column: String,
782        attributes: Vec<AttributeFact>,
783    },
784    Inherit {
785        column: String,
786        parent: QualifiedName,
787    },
788    NoInherit {
789        column: String,
790        parent: QualifiedName,
791    },
792    ValidateConstraint {
793        constraint_name: String,
794    },
795    DisableTrigger {
796        trigger_name: Option<String>,
797    },
798    EnableTrigger {
799        trigger_name: Option<String>,
800    },
801    AttachPartition {
802        child: QualifiedName,
803        strategy: Option<String>,
804    },
805    DetachPartition {
806        child: QualifiedName,
807    },
808    SetStorage {
809        column: String,
810    },
811    SetAccessMethod,
812    ClusterOn {
813        index: String,
814    },
815    InheritTable {
816        parent: QualifiedName,
817    },
818    NoInheritTable {
819        parent: QualifiedName,
820    },
821    MergePartitions {
822        parent: QualifiedName,
823    },
824    SplitPartition,
825    SetSchema {
826        new_schema: String,
827    },
828    SetTablespace {
829        tablespace: String,
830    },
831    SetLogged,
832    SetUnlogged,
833    OwnerTo {
834        new_owner: RoleFact,
835    },
836    ReplicaIdentity {
837        option: String,
838    },
839    ForceRls,
840    EnableRls,
841    DisableRls,
842    EnableAlwaysTrigger {
843        trigger_name: Option<String>,
844    },
845    EnableReplicaTrigger {
846        trigger_name: Option<String>,
847    },
848}