1use crate::analysis::facts::{
3 AlterIndexActionFact, AlterTableActionFact, PersistenceFact, StatementFact, TypeCreationKind,
4};
5use crate::analysis::mutations::{
6 AlterDomainMutation, AlterSequenceMutation, AlterTable, AlterTableActionMutation,
7 AlterTypeActionMutation, AlterTypeMutation, ColumnMutation, CreateDomainMutation, CreateIndex,
8 CreateMaterializedView, CreatePolicyMutation, CreateSchemaMutation, CreateSequenceMutation,
9 CreateTable, CreateTriggerMutation, CreateTypeMutation, CreateView, DropDomainMutation,
10 DropIndex, DropMaterializedViewMutation, DropPolicyMutation, DropSchemaMutation,
11 DropSequenceMutation, DropTable, DropTriggerMutation, DropViewMutation, FkMutation, Mutation,
12 OpaqueMutation, PersistenceMutation, RefreshMaterializedViewMutation, ReleaseSavepointMutation,
13 Rename, RollbackToSavepointMutation, SavepointMutation, SearchPathChange,
14};
15use crate::analysis::state::AnalysisState;
16use crate::ast::identifiers::{ObjectId, QualifiedName};
17use crate::model::types::TypeKind;
18
19pub struct Resolver;
20
21impl Resolver {
22 fn resolve_creation_name(name: &QualifiedName, state: &AnalysisState) -> ObjectId {
23 let schema = name
24 .schema
25 .as_ref()
26 .map(|i| i.resolve())
27 .unwrap_or_else(|| {
28 state
29 .local
30 .search_path
31 .first()
32 .map(|s| s.as_str())
33 .unwrap_or("public")
34 .to_string()
35 });
36
37 ObjectId {
38 schema,
39 name: name.name.resolve(),
40 }
41 }
42
43 fn resolve_lookup_name(name: &QualifiedName, state: &AnalysisState) -> ObjectId {
44 if let Some(schema_ident) = &name.schema {
45 return ObjectId {
46 schema: schema_ident.resolve(),
47 name: name.name.resolve(),
48 };
49 }
50
51 let resolved_name = name.name.resolve();
52
53 for schema in &state.local.search_path {
54 let candidate = ObjectId {
55 schema: schema.clone(),
56 name: resolved_name.clone(),
57 };
58 if state.local.relations.contains_key(&candidate)
59 || state.local.types.contains_key(&candidate)
60 || state.local.sequences.contains_key(&candidate)
61 {
62 return candidate;
63 }
64 }
65
66 let schema = state
67 .local
68 .search_path
69 .first()
70 .map(|s| s.as_str())
71 .unwrap_or("public")
72 .to_string();
73 ObjectId {
74 schema,
75 name: resolved_name,
76 }
77 }
78
79 pub fn resolve(fact: &StatementFact, state: &AnalysisState) -> Vec<Mutation> {
80 let mut mutations = Vec::new();
81 match fact {
82 StatementFact::CreateSchema {
83 name,
84 if_not_exists,
85 } => {
86 mutations.push(Mutation::CreateSchema(CreateSchemaMutation {
87 name: name.name.resolve(),
88 if_not_exists: *if_not_exists,
89 }));
90 }
91 StatementFact::AlterSchema { .. } => {
92 mutations.push(Mutation::Opaque(OpaqueMutation::DynamicSql));
93 }
94 StatementFact::DropSchema {
95 names,
96 if_exists,
97 cascade,
98 } => {
99 mutations.push(Mutation::DropSchema(DropSchemaMutation {
100 names: names.iter().map(|n| n.name.resolve()).collect(),
101 if_exists: *if_exists,
102 cascade: *cascade,
103 }));
104 }
105 StatementFact::CreateTable {
106 name,
107 if_not_exists,
108 as_select,
109 persistence,
110 columns,
111 foreign_keys,
112 table_constraints,
113 partition_by,
114 partition_of,
115 } => {
116 let id = Self::resolve_creation_name(name, state);
117
118 if !*if_not_exists
119 && (state.relation_is_present(&id)
120 || matches!(
121 state.local.types.get(&id),
122 Some(crate::model::types::TypeOverlay::Present(_))
123 )
124 || matches!(
125 state.local.sequences.get(&id),
126 Some(crate::model::sequence::SequenceOverlay::Present(_))
127 ))
128 {
129 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
130 }
131
132 let resolved_persistence = match persistence {
133 PersistenceFact::Permanent => PersistenceMutation::Permanent,
134 PersistenceFact::Temporary => PersistenceMutation::Temporary,
135 PersistenceFact::Unlogged => PersistenceMutation::Unlogged,
136 };
137
138 let col_mutations: Vec<ColumnMutation> = columns
139 .iter()
140 .map(|c| ColumnMutation {
141 name: c.name.clone(),
142 ty: c.ty.clone(),
143 not_null: c.not_null,
144 is_primary_key: c.is_primary_key,
145 default: c.default.clone(),
146 })
147 .collect();
148
149 let mut fk_mutations = Vec::new();
150 for fk in foreign_keys {
151 let to_table = Self::resolve_lookup_name(&fk.references, state);
152 if !state.relation_is_present(&to_table) {
153 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
154 }
155 fk_mutations.push(FkMutation {
156 constraint_name: fk.constraint_name.clone(),
157 to_table,
158 from_columns: fk.from_columns.clone(),
159 to_columns: fk.to_columns.clone(),
160 });
161 }
162
163 let partition_of_id = partition_of
164 .as_ref()
165 .map(|n| Self::resolve_lookup_name(n, state));
166
167 if let Some(p_id) = &partition_of_id
168 && !state.relation_is_present(p_id)
169 {
170 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
171 }
172
173 mutations.push(Mutation::CreateTable(CreateTable {
174 id,
175 if_not_exists: *if_not_exists,
176 as_select: *as_select,
177 persistence: resolved_persistence,
178 columns: col_mutations,
179 foreign_keys: fk_mutations,
180 table_constraints: table_constraints.clone(),
181 partition_by: partition_by.clone(),
182 partition_of: partition_of_id,
183 }));
184 }
185 StatementFact::CreateView {
186 name,
187 or_replace,
188 depends_on,
189 } => {
190 let id = Self::resolve_creation_name(name, state);
191
192 if !*or_replace && state.relation_is_present(&id) {
193 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
194 }
195
196 let resolved_depends = depends_on
197 .iter()
198 .map(|n| Self::resolve_lookup_name(n, state))
199 .collect();
200
201 mutations.push(Mutation::CreateView(CreateView {
202 id,
203 or_replace: *or_replace,
204 depends_on: resolved_depends,
205 }));
206 }
207 StatementFact::AlterView { name, new_name } => {
208 if let Some(new_name) = new_name {
209 let id = Self::resolve_lookup_name(name, state);
210 let new_id = ObjectId {
211 schema: id.schema.clone(),
212 name: new_name.resolve(),
213 };
214 mutations.push(Mutation::Rename(Rename { old_id: id, new_id }));
215 }
216 }
217 StatementFact::CreateMaterializedView { name, depends_on } => {
218 let id = Self::resolve_creation_name(name, state);
219
220 if state.relation_is_present(&id) {
221 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
222 }
223
224 let resolved_depends = depends_on
225 .iter()
226 .map(|n| Self::resolve_lookup_name(n, state))
227 .collect();
228
229 mutations.push(Mutation::CreateMaterializedView(CreateMaterializedView {
230 id,
231 depends_on: resolved_depends,
232 }));
233 }
234 StatementFact::AlterMaterializedView { name, new_name } => {
235 if let Some(new_name) = new_name {
236 let id = Self::resolve_lookup_name(name, state);
237 let new_id = ObjectId {
238 schema: id.schema.clone(),
239 name: new_name.resolve(),
240 };
241 mutations.push(Mutation::Rename(Rename { old_id: id, new_id }));
242 }
243 }
244 StatementFact::RefreshMaterializedView { name, concurrently } => {
245 mutations.push(Mutation::RefreshMaterializedView(
246 RefreshMaterializedViewMutation {
247 id: Self::resolve_lookup_name(name, state),
248 concurrently: *concurrently,
249 },
250 ));
251 }
252 StatementFact::CreateIndex {
253 name,
254 relation,
255 if_not_exists,
256 concurrently,
257 using_method,
258 has_predicate,
259 } => {
260 let id = Self::resolve_creation_name(name, state);
261
262 if !*if_not_exists && state.local.graph.indexes.iter().any(|ix| ix.index_id == id) {
263 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
264 }
265
266 mutations.push(Mutation::CreateIndex(CreateIndex {
267 id,
268 table: Self::resolve_lookup_name(relation, state),
269 if_not_exists: *if_not_exists,
270 concurrently: *concurrently,
271 using_method: using_method.clone(),
272 has_predicate: *has_predicate,
273 }));
274 }
275 StatementFact::CreatePolicy { name, table } => {
276 mutations.push(Mutation::CreatePolicy(CreatePolicyMutation {
277 name: name.clone(),
278 table: Self::resolve_lookup_name(table, state),
279 }));
280 }
281 StatementFact::DropPolicy {
282 name,
283 table,
284 if_exists,
285 } => {
286 mutations.push(Mutation::DropPolicy(DropPolicyMutation {
287 name: name.clone(),
288 table: Self::resolve_lookup_name(table, state),
289 if_exists: *if_exists,
290 }));
291 }
292 StatementFact::CreateTrigger { name, table } => {
293 mutations.push(Mutation::CreateTrigger(CreateTriggerMutation {
294 name: name.clone(),
295 table: Self::resolve_lookup_name(table, state),
296 }));
297 }
298 StatementFact::DropTrigger {
299 name,
300 table,
301 if_exists,
302 } => {
303 mutations.push(Mutation::DropTrigger(DropTriggerMutation {
304 name: name.clone(),
305 table: Self::resolve_lookup_name(table, state),
306 if_exists: *if_exists,
307 }));
308 }
309 StatementFact::AlterIndex { name, actions } => {
310 let id = Self::resolve_lookup_name(name, state);
311 for action in actions {
312 match action {
313 AlterIndexActionFact::RenameTo { new_name } => {
314 let new_id = ObjectId {
315 schema: id.schema.clone(),
316 name: new_name.resolve(),
317 };
318 mutations.push(Mutation::Rename(Rename {
319 old_id: id.clone(),
320 new_id,
321 }));
322 }
323 }
324 }
325 }
326 StatementFact::CreateType(create_type) => {
327 let id = Self::resolve_creation_name(&create_type.name, state);
328
329 if matches!(
330 state.local.types.get(&id),
331 Some(crate::model::types::TypeOverlay::Present(_))
332 ) || state.relation_is_present(&id)
333 {
334 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
335 }
336
337 let mapped_kind = match create_type.kind {
338 TypeCreationKind::Enum => TypeKind::Enum { variants: vec![] },
339 TypeCreationKind::Range => TypeKind::Range,
340 TypeCreationKind::Composite => TypeKind::Composite,
341 TypeCreationKind::Base => TypeKind::Base,
342 };
343
344 mutations.push(Mutation::CreateType(CreateTypeMutation {
345 id,
346 kind: mapped_kind,
347 }));
348 }
349 StatementFact::AlterType(alter_type) => {
350 let id = Self::resolve_lookup_name(&alter_type.name, state);
351 for action_fact in &alter_type.actions {
352 match action_fact {
353 crate::analysis::facts::AlterTypeActionFact::AddValue { new_value } => {
354 mutations.push(Mutation::AlterType(AlterTypeMutation {
355 id: id.clone(),
356 action: AlterTypeActionMutation::AddValue {
357 new_value: new_value.clone(),
358 },
359 }));
360 }
361 }
362 }
363 }
364 StatementFact::CreateDomain { name, base_type } => {
365 let id = Self::resolve_creation_name(name, state);
366
367 if matches!(
368 state.local.types.get(&id),
369 Some(crate::model::types::TypeOverlay::Present(_))
370 ) || state.relation_is_present(&id)
371 {
372 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
373 }
374
375 mutations.push(Mutation::CreateDomain(CreateDomainMutation {
376 id,
377 base_type: base_type.clone(),
378 }));
379 }
380 StatementFact::AlterDomain { name } => {
381 mutations.push(Mutation::AlterDomain(AlterDomainMutation {
382 id: Self::resolve_lookup_name(name, state),
383 }));
384 }
385 StatementFact::DropDomain { names, if_exists } => {
386 let ids = names
387 .iter()
388 .map(|n| Self::resolve_lookup_name(n, state))
389 .collect();
390 mutations.push(Mutation::DropDomain(DropDomainMutation {
391 ids,
392 if_exists: *if_exists,
393 }));
394 }
395 StatementFact::CreateSequence {
396 name,
397 if_not_exists,
398 owned_by,
399 } => {
400 let id = Self::resolve_creation_name(name, state);
401
402 if !*if_not_exists
403 && matches!(
404 state.local.sequences.get(&id),
405 Some(crate::model::sequence::SequenceOverlay::Present(_))
406 )
407 {
408 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
409 }
410
411 let resolved_owned_by = owned_by.as_ref().map(|(table_name, col)| {
412 (Self::resolve_lookup_name(table_name, state), col.clone())
413 });
414 mutations.push(Mutation::CreateSequence(CreateSequenceMutation {
415 id,
416 if_not_exists: *if_not_exists,
417 owned_by: resolved_owned_by,
418 }));
419 }
420 StatementFact::AlterSequence { name, owned_by } => {
421 let resolved_owned_by = owned_by.as_ref().map(|(table_name, col)| {
422 (Self::resolve_lookup_name(table_name, state), col.clone())
423 });
424 mutations.push(Mutation::AlterSequence(AlterSequenceMutation {
425 id: Self::resolve_lookup_name(name, state),
426 owned_by: resolved_owned_by,
427 }));
428 }
429 StatementFact::DropSequence { names, if_exists } => {
430 let ids = names
431 .iter()
432 .map(|n| Self::resolve_lookup_name(n, state))
433 .collect();
434 mutations.push(Mutation::DropSequence(DropSequenceMutation {
435 ids,
436 if_exists: *if_exists,
437 }));
438 }
439 StatementFact::AlterTable { name, actions } => {
440 let id = Self::resolve_lookup_name(name, state);
441 for action_fact in actions {
442 let action = match action_fact {
443 AlterTableActionFact::AddColumn {
444 name: col_name,
445 ty,
446 if_not_exists,
447 not_null,
448 default,
449 } => AlterTableActionMutation::AddColumn {
450 name: col_name.clone(),
451 ty: ty.clone(),
452 if_not_exists: *if_not_exists,
453 not_null: *not_null,
454 default: default.clone(),
455 },
456 AlterTableActionFact::DropColumn {
457 name: col_name,
458 if_exists,
459 } => AlterTableActionMutation::DropColumn {
460 name: col_name.clone(),
461 if_exists: *if_exists,
462 },
463 AlterTableActionFact::RenameColumn { from, to } => {
464 AlterTableActionMutation::RenameColumn {
465 from: from.resolve(),
466 to: to.resolve(),
467 }
468 }
469 AlterTableActionFact::RenameTo { new_name } => {
470 let new_id = ObjectId {
471 schema: id.schema.clone(),
472 name: new_name.resolve(),
473 };
474 mutations.push(Mutation::Rename(Rename {
475 old_id: id.clone(),
476 new_id,
477 }));
478 continue;
479 }
480 AlterTableActionFact::AddForeignKey {
481 constraint_name,
482 references,
483 from_columns,
484 to_columns,
485 not_valid,
486 } => {
487 let to_table = Self::resolve_lookup_name(references, state);
488 if !state.relation_is_present(&to_table) {
489 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
490 }
491 AlterTableActionMutation::AddForeignKey {
492 constraint_name: constraint_name.clone(),
493 to_table,
494 from_columns: from_columns.clone(),
495 to_columns: to_columns.clone(),
496 not_valid: *not_valid,
497 }
498 }
499 AlterTableActionFact::AlterConstraint {
500 name: c_name,
501 deferrable,
502 } => AlterTableActionMutation::AlterConstraint {
503 name: c_name.clone(),
504 deferrable: *deferrable,
505 },
506 AlterTableActionFact::RenameConstraint { old_name, new_name } => {
507 AlterTableActionMutation::RenameConstraint {
508 old_name: old_name.clone(),
509 new_name: new_name.clone(),
510 }
511 }
512 AlterTableActionFact::DropConstraint { name: c_name } => {
513 AlterTableActionMutation::DropConstraint {
514 name: c_name.clone(),
515 }
516 }
517 AlterTableActionFact::AddCheckConstraint {
518 constraint_name,
519 not_valid,
520 } => AlterTableActionMutation::AddCheckConstraint {
521 constraint_name: constraint_name.clone(),
522 not_valid: *not_valid,
523 },
524 AlterTableActionFact::AddUniqueConstraint => {
525 AlterTableActionMutation::AddUniqueConstraint
526 }
527 AlterTableActionFact::AddPrimaryKeyConstraint => {
528 AlterTableActionMutation::AddPrimaryKeyConstraint
529 }
530 AlterTableActionFact::AddExcludeConstraint => {
531 AlterTableActionMutation::AddExcludeConstraint
532 }
533 AlterTableActionFact::SetNotNull { column } => {
534 AlterTableActionMutation::SetNotNull {
535 column: column.clone(),
536 }
537 }
538 AlterTableActionFact::DropNotNull { column } => {
539 AlterTableActionMutation::DropNotNull {
540 column: column.clone(),
541 }
542 }
543 AlterTableActionFact::SetType {
544 column,
545 ty,
546 has_using,
547 } => AlterTableActionMutation::SetType {
548 column: column.clone(),
549 ty: ty.clone(),
550 has_using: *has_using,
551 },
552 AlterTableActionFact::SetDefault { column, default } => {
553 AlterTableActionMutation::SetDefault {
554 column: column.clone(),
555 default: default.clone(),
556 }
557 }
558 AlterTableActionFact::ValidateConstraint { constraint_name } => {
559 AlterTableActionMutation::ValidateConstraint {
560 constraint_name: constraint_name.clone(),
561 }
562 }
563 AlterTableActionFact::AttachPartition { child } => {
564 let child_id = Self::resolve_lookup_name(child, state);
565 if state.local.graph.check_partition_cycle(&id, &child_id) {
566 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
567 }
568 AlterTableActionMutation::AttachPartition { child: child_id }
569 }
570 AlterTableActionFact::DetachPartition { child } => {
571 AlterTableActionMutation::DetachPartition {
572 child: Self::resolve_lookup_name(child, state),
573 }
574 }
575 AlterTableActionFact::SetStorage { column } => {
576 AlterTableActionMutation::SetStorage {
577 column: column.clone(),
578 }
579 }
580 AlterTableActionFact::SetAccessMethod => {
581 AlterTableActionMutation::SetAccessMethod
582 }
583 };
584 mutations.push(Mutation::AlterTable(AlterTable {
585 id: id.clone(),
586 action,
587 }));
588 }
589 }
590 StatementFact::DropTable {
591 name,
592 if_exists,
593 cascade,
594 } => {
595 mutations.push(Mutation::DropTable(DropTable {
596 id: Self::resolve_lookup_name(name, state),
597 if_exists: *if_exists,
598 cascade: *cascade,
599 }));
600 }
601 StatementFact::DropView { names, if_exists } => {
602 let ids = names
603 .iter()
604 .map(|n| Self::resolve_lookup_name(n, state))
605 .collect();
606 mutations.push(Mutation::DropView(DropViewMutation {
607 ids,
608 if_exists: *if_exists,
609 }));
610 }
611 StatementFact::DropMaterializedView { names, if_exists } => {
612 let ids = names
613 .iter()
614 .map(|n| Self::resolve_lookup_name(n, state))
615 .collect();
616 mutations.push(Mutation::DropMaterializedView(
617 DropMaterializedViewMutation {
618 ids,
619 if_exists: *if_exists,
620 },
621 ));
622 }
623 StatementFact::DropIndex {
624 names,
625 if_exists,
626 concurrently,
627 } => {
628 for name in names {
629 mutations.push(Mutation::DropIndex(DropIndex {
630 id: Self::resolve_lookup_name(name, state),
631 if_exists: *if_exists,
632 concurrently: *concurrently,
633 }));
634 }
635 }
636 StatementFact::SetSearchPath { target } => {
637 mutations.push(Mutation::SearchPath(SearchPathChange {
638 target: target.clone(),
639 }))
640 }
641 StatementFact::BeginTransaction => mutations.push(Mutation::BeginTransaction),
642 StatementFact::CommitTransaction => mutations.push(Mutation::CommitTransaction),
643 StatementFact::RollbackTransaction => mutations.push(Mutation::RollbackTransaction),
644 StatementFact::RollbackToSavepoint { name } => {
645 if !state.local.transactions.iter().any(|t| t.name == *name) {
646 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
647 }
648 mutations.push(Mutation::RollbackToSavepoint(RollbackToSavepointMutation {
649 name: name.clone(),
650 }))
651 }
652 StatementFact::Savepoint { name } => {
653 mutations.push(Mutation::Savepoint(SavepointMutation {
654 name: name.clone(),
655 }))
656 }
657 StatementFact::ReleaseSavepoint { name } => {
658 if !state.local.transactions.iter().any(|t| t.name == *name) {
659 return vec![Mutation::Opaque(OpaqueMutation::DynamicSql)];
660 }
661 mutations.push(Mutation::ReleaseSavepoint(ReleaseSavepointMutation {
662 name: name.clone(),
663 }))
664 }
665 StatementFact::PrepareTransaction { .. } => {
666 mutations.push(Mutation::Opaque(OpaqueMutation::PrepareTransaction))
667 }
668 StatementFact::SetTransaction => {
669 mutations.push(Mutation::Opaque(OpaqueMutation::SetTransaction))
670 }
671 StatementFact::SetConstraints => {
672 mutations.push(Mutation::Opaque(OpaqueMutation::SetConstraints))
673 }
674 StatementFact::OpaqueBlock => mutations.push(Mutation::Opaque(OpaqueMutation::DoBlock)),
675 StatementFact::Execute => mutations.push(Mutation::Opaque(OpaqueMutation::Execute)),
676 StatementFact::Vacuum { is_full } => {
677 mutations.push(Mutation::Vacuum { is_full: *is_full })
678 }
679 }
680 mutations
681 }
682}