Skip to main content

safe_migrate/analysis/
resolver.rs

1use crate::analysis::facts::StatementFact;
2use crate::analysis::mutations::{Mutation, OpaqueMutation};
3use crate::analysis::state::AnalysisState;
4use crate::ast::identifiers::{ObjectId, QualifiedName};
5
6mod relation;
7mod relation_aux;
8mod replication;
9mod routine;
10mod schema;
11mod security;
12mod sequence;
13mod session;
14mod types;
15
16pub struct Resolver;
17
18impl Resolver {
19    fn resolve_creation_name(name: &QualifiedName, state: &AnalysisState) -> ObjectId {
20        let schema = name
21            .schema
22            .as_ref()
23            .map(|i| i.resolve())
24            .unwrap_or_else(|| {
25                state
26                    .local
27                    .search_path
28                    .first()
29                    .map(|s| s.as_str())
30                    .unwrap_or("public")
31                    .to_string()
32            });
33
34        ObjectId::new(schema, name.name.resolve())
35    }
36
37    fn resolve_in_namespace(
38        name: &QualifiedName,
39        object_name: String,
40        state: &AnalysisState,
41        present: impl Fn(&AnalysisState, &ObjectId) -> bool,
42    ) -> ObjectId {
43        if let Some(schema_ident) = &name.schema {
44            return ObjectId::new(schema_ident.resolve(), object_name);
45        }
46
47        for schema in &state.local.search_path {
48            let mut candidate = ObjectId::new(schema.clone(), object_name.clone());
49            if present(state, &candidate) {
50                candidate.inferred_schema = true;
51                return candidate;
52            }
53        }
54
55        let schema = state
56            .local
57            .search_path
58            .first()
59            .cloned()
60            .unwrap_or_else(|| "public".to_string());
61        let mut id = ObjectId::new(schema, object_name);
62        id.inferred_schema = true;
63        id
64    }
65
66    fn resolve_relation_lookup_name(name: &QualifiedName, state: &AnalysisState) -> ObjectId {
67        Self::resolve_in_namespace(
68            name,
69            name.name.resolve(),
70            state,
71            AnalysisState::relation_namespace_object_is_present,
72        )
73    }
74
75    fn resolve_type_lookup_name(name: &QualifiedName, state: &AnalysisState) -> ObjectId {
76        Self::resolve_in_namespace(
77            name,
78            name.name.resolve(),
79            state,
80            AnalysisState::type_is_present,
81        )
82    }
83
84    fn resolve_routine_lookup_name(
85        name: &QualifiedName,
86        params: &[String],
87        state: &AnalysisState,
88    ) -> ObjectId {
89        let signature = params
90            .iter()
91            .map(|param| Self::normalize_function_arg_type(param))
92            .collect::<Vec<_>>()
93            .join(",");
94        let object_name = format!("{}({signature})", name.name.resolve());
95        Self::resolve_in_namespace(name, object_name, state, AnalysisState::routine_is_present)
96    }
97
98    fn resolve_constraint_index_name(name: &QualifiedName, table: &ObjectId) -> ObjectId {
99        let schema = name
100            .schema
101            .as_ref()
102            .map(|schema| schema.resolve())
103            .unwrap_or_else(|| table.schema.clone());
104        ObjectId::new(schema, name.name.resolve())
105    }
106
107    fn resolve_function_id(
108        name: &QualifiedName,
109        params: &[crate::analysis::facts::ParamFact],
110        state: &AnalysisState,
111    ) -> ObjectId {
112        let base_id = Self::resolve_creation_name(name, state);
113        let sig = params
114            .iter()
115            .filter(|p| !matches!(&p.mode, crate::analysis::facts::ParamModeFact::Out))
116            .map(|p| p.ty.clone())
117            .collect::<Vec<_>>()
118            .join(",");
119        Self::resolve_function_id_by_sig(&base_id, &sig)
120    }
121
122    fn resolve_function_id_by_sig(base_id: &ObjectId, sig: &str) -> ObjectId {
123        // Normalize types in signature to match pg_proc standard names
124        let normalized_sig = sig
125            .split(',')
126            .map(Self::normalize_function_arg_type)
127            .collect::<Vec<_>>()
128            .join(",");
129
130        let mut id = ObjectId::new(
131            base_id.schema.clone(),
132            format!("{}({})", base_id.name, normalized_sig),
133        );
134        id.inferred_schema = base_id.inferred_schema;
135        id
136    }
137
138    pub(crate) fn normalize_function_arg_type(raw: &str) -> String {
139        let normalized = Self::fold_unquoted_identifier_case(raw.trim());
140        if let Some(element_type) = normalized.strip_suffix("[]") {
141            return format!("{}[]", Self::normalize_function_arg_type(element_type));
142        }
143        match normalized.as_str() {
144            "int" | "int4" => "integer".to_string(),
145            "int8" => "bigint".to_string(),
146            "int2" => "smallint".to_string(),
147            "float8" => "double precision".to_string(),
148            "float4" => "real".to_string(),
149            "bool" => "boolean".to_string(),
150            "varchar" => "character varying".to_string(),
151            "char" => "character".to_string(),
152            "time" => "time without time zone".to_string(),
153            "timestamp" => "timestamp without time zone".to_string(),
154            "timestamptz" => "timestamp with time zone".to_string(),
155            "decimal" => "numeric".to_string(),
156            _ => normalized,
157        }
158    }
159
160    fn fold_unquoted_identifier_case(raw: &str) -> String {
161        let mut folded = String::with_capacity(raw.len());
162        let mut quoted = false;
163        let mut chars = raw.chars().peekable();
164        while let Some(character) = chars.next() {
165            match character {
166                '"' if quoted && chars.peek() == Some(&'"') => {
167                    folded.push('"');
168                    folded.push('"');
169                    chars.next();
170                }
171                '"' => {
172                    quoted = !quoted;
173                    folded.push(character);
174                }
175                character if quoted => folded.push(character),
176                character => folded.extend(character.to_lowercase()),
177            }
178        }
179        folded
180    }
181
182    pub fn resolve(fact: &StatementFact, state: &AnalysisState) -> Vec<Mutation> {
183        let mut mutations = Vec::new();
184        match fact {
185            StatementFact::CreateSchema {
186                name,
187                if_not_exists,
188                authorization,
189            } => {
190                mutations.push(Self::resolve_create_schema(
191                    name,
192                    *if_not_exists,
193                    authorization,
194                ));
195            }
196            StatementFact::SchemaNeutralNoop => {}
197            StatementFact::AlterSchema { name, action } => {
198                mutations.push(Self::resolve_alter_schema(name, action));
199            }
200            StatementFact::DropSchema {
201                names,
202                if_exists,
203                cascade,
204            } => {
205                mutations.push(Self::resolve_drop_schema(names, *if_exists, *cascade));
206            }
207            StatementFact::CreateTable {
208                name,
209                if_not_exists,
210                as_select,
211                persistence,
212                columns,
213                foreign_keys,
214                table_constraints,
215                partition_by,
216                partition_of,
217                partition_type,
218            } => {
219                mutations.push(Self::resolve_create_table(
220                    name,
221                    *if_not_exists,
222                    *as_select,
223                    persistence,
224                    columns,
225                    foreign_keys,
226                    table_constraints,
227                    partition_by,
228                    partition_of,
229                    partition_type,
230                    state,
231                ));
232            }
233            StatementFact::CreateView {
234                name,
235                or_replace,
236                depends_on,
237            } => {
238                mutations.push(Self::resolve_create_view(
239                    name,
240                    *or_replace,
241                    depends_on,
242                    state,
243                ));
244            }
245            StatementFact::AlterView { name, action } => {
246                if let Some(mutation) = Self::resolve_alter_view(name, action, state) {
247                    mutations.push(mutation);
248                }
249            }
250            StatementFact::CreateMaterializedView { name, depends_on } => {
251                mutations.push(Self::resolve_create_materialized_view(
252                    name, depends_on, state,
253                ));
254            }
255            StatementFact::AlterMaterializedView { name, new_name } => {
256                if let Some(mutation) =
257                    Self::resolve_alter_materialized_view(name, new_name.as_ref(), state)
258                {
259                    mutations.push(mutation);
260                }
261            }
262            StatementFact::RefreshMaterializedView { name, concurrently } => {
263                mutations.push(Self::resolve_refresh_materialized_view(
264                    name,
265                    *concurrently,
266                    state,
267                ));
268            }
269            StatementFact::CreateIndex {
270                name,
271                relation,
272                if_not_exists,
273                concurrently,
274                using_method,
275                has_predicate,
276                unique,
277            } => {
278                mutations.push(Self::resolve_create_index(
279                    name,
280                    relation,
281                    *if_not_exists,
282                    *concurrently,
283                    using_method,
284                    *has_predicate,
285                    *unique,
286                    state,
287                ));
288            }
289            StatementFact::CreatePolicy {
290                name,
291                table,
292                permissive,
293                command,
294                semantics_complete,
295            } => {
296                mutations.push(Self::resolve_create_policy(
297                    name,
298                    table,
299                    *permissive,
300                    command,
301                    *semantics_complete,
302                    state,
303                ));
304            }
305            StatementFact::DropPolicy {
306                name,
307                table,
308                if_exists,
309            } => {
310                mutations.push(Self::resolve_drop_policy(name, table, *if_exists, state));
311            }
312            StatementFact::CreateTrigger {
313                name,
314                table,
315                function,
316            } => {
317                mutations.push(Self::resolve_create_trigger(name, table, function, state));
318            }
319            StatementFact::DropTrigger {
320                name,
321                table,
322                if_exists,
323            } => {
324                mutations.push(Self::resolve_drop_trigger(name, table, *if_exists, state));
325            }
326            StatementFact::AlterTrigger {
327                name,
328                table,
329                new_name,
330            } => mutations.push(Self::resolve_alter_trigger(name, table, new_name, state)),
331            StatementFact::AlterIndex { name, actions } => {
332                mutations.extend(Self::resolve_alter_index(name, actions, state));
333            }
334            StatementFact::CreateType(create_type) => {
335                mutations.push(Self::resolve_create_type(create_type, state));
336            }
337            StatementFact::AlterType(alter_type) => {
338                mutations.extend(Self::resolve_alter_type(alter_type, state));
339            }
340            StatementFact::CreateDomain { name, base_type } => {
341                mutations.push(Self::resolve_create_domain(name, base_type, state));
342            }
343            StatementFact::AlterDomain { name, action } => {
344                mutations.push(Self::resolve_alter_domain(name, action, state));
345            }
346            StatementFact::DropDomain {
347                names,
348                if_exists,
349                cascade,
350            } => {
351                mutations.push(Self::resolve_drop_domain(
352                    names, *if_exists, *cascade, state,
353                ));
354            }
355            StatementFact::DropType {
356                names,
357                if_exists,
358                cascade,
359            } => {
360                mutations.push(Self::resolve_drop_type(names, *if_exists, *cascade, state));
361            }
362            StatementFact::CreateSequence {
363                name,
364                if_not_exists,
365                owned_by,
366            } => {
367                mutations.push(Self::resolve_create_sequence(
368                    name,
369                    *if_not_exists,
370                    owned_by,
371                    state,
372                ));
373            }
374            StatementFact::AlterSequence {
375                name,
376                if_exists,
377                action,
378            } => {
379                mutations.push(Self::resolve_alter_sequence(
380                    name, *if_exists, action, state,
381                ));
382            }
383            StatementFact::DropSequence {
384                names,
385                if_exists,
386                cascade,
387            } => {
388                mutations.push(Self::resolve_drop_sequence(
389                    names, *if_exists, *cascade, state,
390                ));
391            }
392            StatementFact::AlterTable { name, actions } => {
393                mutations.extend(Self::resolve_alter_table(name, actions, state));
394            }
395            StatementFact::DropTable {
396                names,
397                if_exists,
398                cascade,
399            } => {
400                mutations.push(Self::resolve_drop_table(names, *if_exists, *cascade, state));
401            }
402            StatementFact::DropView {
403                names,
404                if_exists,
405                cascade,
406            } => {
407                mutations.push(Self::resolve_drop_view(names, *if_exists, *cascade, state));
408            }
409            StatementFact::DropMaterializedView {
410                names,
411                if_exists,
412                cascade,
413            } => {
414                mutations.push(Self::resolve_drop_materialized_view(
415                    names, *if_exists, *cascade, state,
416                ));
417            }
418            StatementFact::DropIndex {
419                names,
420                if_exists,
421                concurrently,
422                cascade,
423            } => {
424                mutations.push(Self::resolve_drop_indexes(
425                    names,
426                    *if_exists,
427                    *concurrently,
428                    *cascade,
429                    state,
430                ));
431            }
432            StatementFact::SetSearchPath { target, local } => {
433                mutations.push(Self::resolve_search_path(target, *local))
434            }
435            StatementFact::SetTimeout {
436                setting,
437                value,
438                local,
439            } => mutations.push(Self::resolve_timeout(*setting, value, *local)),
440            StatementFact::ResetSettings { target } => {
441                mutations.push(Mutation::ResetSettings(*target))
442            }
443            StatementFact::BeginTransaction => mutations.push(Mutation::BeginTransaction),
444            StatementFact::CommitTransaction => mutations.push(Mutation::CommitTransaction),
445            StatementFact::CommitAndChain => mutations.push(Mutation::CommitAndChain),
446            StatementFact::RollbackTransaction => mutations.push(Mutation::RollbackTransaction),
447            StatementFact::RollbackAndChain => mutations.push(Mutation::RollbackAndChain),
448            StatementFact::RollbackToSavepoint { name } => {
449                mutations.push(Self::resolve_rollback_to_savepoint(name))
450            }
451            StatementFact::Savepoint { name } => mutations.push(Self::resolve_savepoint(name)),
452            StatementFact::ReleaseSavepoint { name } => {
453                mutations.push(Self::resolve_release_savepoint(name))
454            }
455            StatementFact::PrepareTransaction { .. } => {
456                mutations.push(Mutation::Opaque(OpaqueMutation::PrepareTransaction))
457            }
458            StatementFact::SetTransaction => {
459                mutations.push(Mutation::Opaque(OpaqueMutation::SetTransaction))
460            }
461            StatementFact::SetConstraints => {
462                mutations.push(Mutation::Opaque(OpaqueMutation::SetConstraints))
463            }
464            StatementFact::OpaqueBlock => mutations.push(Mutation::Opaque(OpaqueMutation::DoBlock)),
465            StatementFact::Execute => mutations.push(Mutation::Opaque(OpaqueMutation::Execute)),
466            StatementFact::Vacuum { relation, is_full } => {
467                mutations.push(Self::resolve_vacuum(relation.as_ref(), *is_full, state))
468            }
469            StatementFact::CreateFunction(f) => {
470                mutations.push(Self::resolve_create_function(f, state));
471            }
472            StatementFact::AlterFunction(f) => {
473                mutations.push(Self::resolve_alter_function(f, state));
474            }
475            StatementFact::DropFunction(f) => {
476                mutations.push(Self::resolve_drop_function(f));
477            }
478            StatementFact::CreateProcedure(p) => {
479                mutations.push(Self::resolve_create_procedure(p, state));
480            }
481            StatementFact::AlterProcedure(p) => {
482                mutations.push(Self::resolve_alter_procedure(p, state));
483            }
484            StatementFact::DropProcedure(p) => {
485                mutations.push(Self::resolve_drop_procedure(p));
486            }
487            StatementFact::CreateAggregate(a) => {
488                mutations.push(Self::resolve_create_aggregate(a, state));
489            }
490            StatementFact::AlterAggregate(a) => {
491                mutations.push(Self::resolve_alter_aggregate(a, state));
492            }
493            StatementFact::DropAggregate(a) => {
494                mutations.push(Self::resolve_drop_aggregate(a));
495            }
496            StatementFact::CreatePublication(p) => {
497                mutations.push(Self::resolve_create_publication(p, state));
498            }
499            StatementFact::AlterPublication(p) => {
500                mutations.push(Self::resolve_alter_publication(p, state));
501            }
502            StatementFact::DropPublication(p) => {
503                mutations.push(Self::resolve_drop_publication(p));
504            }
505            StatementFact::CreateSubscription(s) => {
506                mutations.push(Self::resolve_create_subscription(s));
507            }
508            StatementFact::AlterSubscription(s) => {
509                mutations.push(Self::resolve_alter_subscription(s));
510            }
511            StatementFact::DropSubscription(s) => {
512                mutations.push(Self::resolve_drop_subscription(s));
513            }
514            StatementFact::CreateRole(r) => {
515                mutations.push(Self::resolve_create_role(r));
516            }
517            StatementFact::AlterRole(r) => {
518                mutations.push(Self::resolve_alter_role(r));
519            }
520            StatementFact::DropRole(r) => {
521                mutations.push(Self::resolve_drop_role(r));
522            }
523            StatementFact::Grant(g) => {
524                mutations.push(Self::resolve_grant(g, state));
525            }
526            StatementFact::Revoke(r) => {
527                mutations.push(Self::resolve_revoke(r, state));
528            }
529            StatementFact::CreateDatabase(d) => {
530                mutations.push(Self::resolve_create_database(d));
531            }
532            StatementFact::AlterDatabase(d) => {
533                mutations.push(Self::resolve_alter_database(d));
534            }
535            StatementFact::DropDatabase(d) => {
536                mutations.push(Self::resolve_drop_database(d));
537            }
538            StatementFact::SetRole {
539                role,
540                local,
541                is_session_auth,
542            } => {
543                mutations.push(Self::resolve_set_role(role, *local, *is_session_auth));
544            }
545        }
546        mutations
547    }
548}