1use crate::analysis::facts::{SearchPathTarget, TableConstraintFact};
3use crate::analysis::graph::{
4 DependencyGraph, FkEdge, IndexEdge, PartitionEdge, PublicationEdge, RenameEdge, SequenceEdge,
5 ViewEdge,
6};
7use crate::analysis::mutations::{
8 AlterTableActionMutation, AlterTypeActionMutation, Mutation, PersistenceMutation,
9};
10use crate::analysis::transaction::{StateChange, TransactionFrame};
11use crate::ast::identifiers::ObjectId;
12use crate::db::cache::DbCache;
13pub use crate::model::relation::RelationOverlay;
14use crate::model::relation::{ColumnAction, Persistence, Privilege, RelationKind, RelationState};
15use crate::model::sequence::{SequenceOverlay, SequenceState};
16use crate::model::trigger::TriggerOverlay;
17use crate::model::types::{TypeKind, TypeOverlay, TypeState};
18use std::collections::{HashMap, HashSet};
19
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum Confidence {
22 Exact,
23 Tainted,
24}
25
26#[derive(Debug, PartialEq, Eq)]
27pub enum MutationResult {
28 Applied,
29 Skipped,
30 Conflict { reason: String },
31}
32
33#[derive(Debug, Default, Clone)]
34pub struct CascadeResult {
35 pub dropped_relations: HashSet<ObjectId>,
36 pub dropped_indexes: HashSet<ObjectId>,
37 pub dropped_constraints: HashSet<(ObjectId, String)>,
38}
39
40pub struct LocalState {
41 pub relations: HashMap<ObjectId, RelationOverlay>,
42 pub types: HashMap<ObjectId, TypeOverlay>,
43 pub functions: HashMap<ObjectId, crate::model::function::FunctionOverlay>,
44 pub sequences: HashMap<ObjectId, SequenceOverlay>,
45 pub publications: HashMap<String, crate::model::replication::PublicationOverlay>,
46 pub subscriptions: HashMap<String, crate::model::replication::SubscriptionOverlay>,
47 pub roles: HashMap<ObjectId, crate::model::role::RoleOverlay>,
48 pub triggers: HashMap<ObjectId, TriggerOverlay>,
49 pub graph: DependencyGraph,
50 pub search_path: Vec<String>,
51 pub current_role: String,
52 pub confidence: Confidence,
53 pub transactions: Vec<TransactionFrame>,
54 pub pending_validation: HashSet<(ObjectId, String)>,
55 pub generation_counter: u64,
56}
57
58#[derive(Clone, Debug)]
59pub struct PreState {
60 pub relations: HashMap<ObjectId, crate::model::relation::RelationState>,
61 pub functions: HashMap<ObjectId, crate::model::function::FunctionState>,
62 pub roles: HashMap<ObjectId, crate::model::role::RoleState>,
63 pub publications: HashMap<String, crate::model::replication::PublicationState>,
64 pub subscriptions: HashMap<String, crate::model::replication::SubscriptionState>,
65 pub sequences: HashMap<ObjectId, crate::model::sequence::SequenceState>,
66 pub types: HashMap<ObjectId, crate::model::types::TypeState>,
67 pub indexes: Vec<crate::analysis::graph::IndexEdge>,
68}
69
70pub struct AnalysisState {
71 pub pg_version_num: Option<u32>,
72 pub baseline_relations: HashSet<ObjectId>,
73 pub baseline_indexes: HashSet<ObjectId>,
74 pub baseline_foreign_keys: HashSet<(ObjectId, String)>,
75 pub local: LocalState,
76}
77
78impl AnalysisState {
79 pub fn new(cache: DbCache) -> Self {
80 let mut relations: HashMap<ObjectId, RelationOverlay> = HashMap::new();
81 let mut baseline_relations = HashSet::new();
82 let mut baseline_indexes = HashSet::new();
83 let mut baseline_foreign_keys = HashSet::new();
84 let mut graph = DependencyGraph::new();
85
86 for (id, rel_state) in cache.baseline_relations() {
87 relations.insert(id.clone(), RelationOverlay::Present(rel_state.clone()));
88 baseline_relations.insert(id.clone());
89 }
90
91 for fk in cache.foreign_keys {
92 baseline_foreign_keys.insert((fk.from_table.clone(), fk.constraint_name.clone()));
93 graph.foreign_keys.push(FkEdge {
94 constraint_name: Some(fk.constraint_name),
95 from_table: fk.from_table,
96 from_columns: Vec::new(),
97 to_table: fk.to_table,
98 to_columns: Vec::new(),
99 from_generation: 0,
100 });
101 }
102
103 for idx in cache.indexes {
104 baseline_indexes.insert(idx.index_id.clone());
106 graph.indexes.push(IndexEdge {
107 index_id: idx.index_id,
108 relation_id: idx.table_id,
109 using_method: None,
110 has_predicate: false,
111 is_concurrent: false,
112 is_unique: false,
113 });
114 }
115
116 Self {
117 pg_version_num: cache.pg_version_num,
118 baseline_relations,
119 baseline_indexes,
120 baseline_foreign_keys,
121 local: LocalState {
122 relations,
123 types: HashMap::new(),
124 functions: HashMap::new(),
125 sequences: HashMap::new(),
126 publications: HashMap::new(),
127 subscriptions: HashMap::new(),
128 roles: HashMap::new(),
129 triggers: HashMap::new(),
130 graph,
131 search_path: vec!["public".to_string()],
132 current_role: "postgres".to_string(),
133 confidence: Confidence::Exact,
134 transactions: Vec::new(),
135 pending_validation: HashSet::new(),
136 generation_counter: 0,
137 },
138 }
139 }
140
141 pub fn get_relation(&self, id: &ObjectId) -> Option<&RelationOverlay> {
142 self.local.relations.get(id)
143 }
144
145 pub fn resolve_function_schema(
146 &self,
147 name: &crate::ast::identifiers::QualifiedName,
148 sig_str: &str,
149 ) -> String {
150 if let Some(schema) = &name.schema {
151 return schema.resolve();
152 }
153 for schema in &self.local.search_path {
154 let candidate = ObjectId::new(schema.clone(), sig_str.to_string());
155 if self.local.functions.contains_key(&candidate) {
156 return schema.clone();
157 }
158 }
159 self.local
160 .search_path
161 .first()
162 .cloned()
163 .unwrap_or_else(|| "public".to_string())
164 }
165
166 pub fn resolve_relation_id(&self, name: &crate::ast::identifiers::QualifiedName) -> ObjectId {
167 if let Some(schema) = &name.schema {
168 return ObjectId::new(schema.resolve(), name.name.resolve());
169 }
170 let resolved_name = name.name.resolve();
171 for schema in &self.local.search_path {
172 let mut candidate = ObjectId::new(schema.clone(), resolved_name.clone());
173 if self.local.relations.contains_key(&candidate) {
174 candidate.inferred_schema = true;
175 return candidate;
176 }
177 }
178 let schema = self
179 .local
180 .search_path
181 .first()
182 .cloned()
183 .unwrap_or_else(|| "public".to_string());
184 let mut id = ObjectId::new(schema, resolved_name);
185 id.inferred_schema = true;
186 id
187 }
188
189 pub fn relation_is_present(&self, id: &ObjectId) -> bool {
190 matches!(
191 self.local.relations.get(id),
192 Some(RelationOverlay::Present(_))
193 )
194 }
195
196 pub fn column_was_added_in_transaction(&self, table_id: &ObjectId, column: &str) -> bool {
197 if self.local.transactions.is_empty() {
198 return false;
199 }
200
201 for frame in &self.local.transactions {
203 for change in &frame.undo_log {
204 if let StateChange::RelationSnapshot { id, previous } = change
205 && id == table_id
206 {
207 match previous.as_ref() {
208 None | Some(RelationOverlay::Dropped) => {
209 return true;
210 }
211 Some(RelationOverlay::Present(r)) => {
212 let col_existed = r.columns.iter().any(|c| c.name == column);
213 return !col_existed;
214 }
215 }
216 }
217 }
218 }
219 false
220 }
221
222 pub fn capture_pre_state(&self) -> PreState {
223 let mut relations = HashMap::new();
224 for (id, overlay) in &self.local.relations {
225 if let RelationOverlay::Present(s) = overlay {
226 relations.insert(id.clone(), s.clone());
227 }
228 }
229
230 let mut functions = HashMap::new();
231 for (id, overlay) in &self.local.functions {
232 if let crate::model::function::FunctionOverlay::Present(s) = overlay {
233 functions.insert(id.clone(), s.clone());
234 }
235 }
236
237 let mut roles = HashMap::new();
238 for (name, overlay) in &self.local.roles {
239 if let crate::model::role::RoleOverlay::Present(s) = overlay {
240 roles.insert(name.clone(), s.clone());
241 }
242 }
243
244 let mut publications = HashMap::new();
245 for (name, overlay) in &self.local.publications {
246 if let crate::model::replication::PublicationOverlay::Present(s) = overlay {
247 publications.insert(name.clone(), s.clone());
248 }
249 }
250
251 let mut subscriptions = HashMap::new();
252 for (name, overlay) in &self.local.subscriptions {
253 if let crate::model::replication::SubscriptionOverlay::Present(s) = overlay {
254 subscriptions.insert(name.clone(), s.clone());
255 }
256 }
257
258 let mut sequences = HashMap::new();
259 for (id, overlay) in &self.local.sequences {
260 if let SequenceOverlay::Present(s) = overlay {
261 sequences.insert(id.clone(), s.clone());
262 }
263 }
264
265 let mut types = HashMap::new();
266 for (id, overlay) in &self.local.types {
267 if let TypeOverlay::Present(s) = overlay {
268 types.insert(id.clone(), s.clone());
269 }
270 }
271
272 let indexes = self.local.graph.indexes.clone();
273
274 PreState {
275 relations,
276 functions,
277 roles,
278 publications,
279 subscriptions,
280 sequences,
281 types,
282 indexes,
283 }
284 }
285
286 pub fn get_cascade_closure(&self, target_oid: &ObjectId) -> CascadeResult {
287 let mut result = CascadeResult::default();
288 let mut visited = HashSet::new();
289 self.walk_cascade(target_oid, &mut visited, &mut result);
290 result
291 }
292
293 fn walk_cascade(
294 &self,
295 current: &ObjectId,
296 visited: &mut HashSet<ObjectId>,
297 result: &mut CascadeResult,
298 ) {
299 let resolved_current = self.local.graph.resolve_rename(current).clone();
300
301 if !visited.insert(resolved_current.clone()) {
302 return;
303 }
304
305 result.dropped_relations.insert(resolved_current.clone());
306
307 for view_edge in &self.local.graph.views {
308 if view_edge
309 .depends_on
310 .iter()
311 .any(|dep| self.local.graph.resolve_rename(dep) == &resolved_current)
312 {
313 let resolved_view_id = self.local.graph.resolve_rename(&view_edge.view_id).clone();
314 if !visited.contains(&resolved_view_id) {
315 self.walk_cascade(&resolved_view_id, visited, result);
316 }
317 }
318 }
319
320 for index_edge in &self.local.graph.indexes {
321 if self.local.graph.resolve_rename(&index_edge.relation_id) == &resolved_current {
322 result.dropped_indexes.insert(
323 self.local
324 .graph
325 .resolve_rename(&index_edge.index_id)
326 .clone(),
327 );
328 }
329 }
330
331 for fk_edge in &self.local.graph.foreign_keys {
332 if self.local.graph.resolve_rename(&fk_edge.to_table) == &resolved_current
333 && let Some(cname) = &fk_edge.constraint_name
334 {
335 result.dropped_constraints.insert((
336 self.local.graph.resolve_rename(&fk_edge.from_table).clone(),
337 cname.clone(),
338 ));
339 }
340 }
341
342 for partition_edge in &self.local.graph.partitions {
343 if self.local.graph.resolve_rename(&partition_edge.parent) == &resolved_current {
344 let resolved_child = self
345 .local
346 .graph
347 .resolve_rename(&partition_edge.child)
348 .clone();
349 if !visited.contains(&resolved_child) {
350 self.walk_cascade(&resolved_child, visited, result);
351 }
352 }
353 }
354 }
355
356 fn resolve_grant_privileges(
357 spec: &crate::analysis::facts::PrivilegeSpec,
358 ) -> HashSet<Privilege> {
359 match spec {
360 crate::analysis::facts::PrivilegeSpec::All => vec![
361 Privilege::Select,
362 Privilege::Insert,
363 Privilege::Update,
364 Privilege::Delete,
365 Privilege::Truncate,
366 Privilege::References,
367 Privilege::Trigger,
368 ]
369 .into_iter()
370 .collect(),
371 crate::analysis::facts::PrivilegeSpec::List(list) => list
372 .iter()
373 .filter_map(|p| match p {
374 crate::analysis::facts::PrivilegeFact::Select => Some(Privilege::Select),
375 crate::analysis::facts::PrivilegeFact::Insert => Some(Privilege::Insert),
376 crate::analysis::facts::PrivilegeFact::Update => Some(Privilege::Update),
377 crate::analysis::facts::PrivilegeFact::Delete => Some(Privilege::Delete),
378 crate::analysis::facts::PrivilegeFact::Truncate => Some(Privilege::Truncate),
379 crate::analysis::facts::PrivilegeFact::References => {
380 Some(Privilege::References)
381 }
382 crate::analysis::facts::PrivilegeFact::Trigger => Some(Privilege::Trigger),
383 _ => None,
384 })
385 .collect(),
386 }
387 }
388
389 fn resolve_role_name(
390 role: &crate::analysis::facts::RoleFact,
391 current_role: &str,
392 ) -> Option<ObjectId> {
393 let name = match role {
394 crate::analysis::facts::RoleFact::Named { name, .. } => Some(name.clone()),
395 crate::analysis::facts::RoleFact::CurrentUser
396 | crate::analysis::facts::RoleFact::CurrentRole => Some(current_role.to_string()),
397 crate::analysis::facts::RoleFact::SessionUser => Some("postgres".to_string()),
398 crate::analysis::facts::RoleFact::Unknown => None,
399 }?;
400 Some(ObjectId::new("", name))
401 }
402
403 fn apply_grant_to_relation(
404 &mut self,
405 id: &ObjectId,
406 privileges: &HashSet<Privilege>,
407 grantees: &[crate::analysis::facts::RoleFact],
408 ) {
409 self.snapshot_relation(id);
410 if let Some(RelationOverlay::Present(rel)) = self.local.relations.get_mut(id) {
411 for grantee in grantees {
412 if let Some(role_id) = Self::resolve_role_name(grantee, &self.local.current_role) {
413 rel.privileges.grant(role_id, privileges.clone());
414 }
415 }
416 }
417 }
418
419 fn apply_revoke_to_relation(
420 &mut self,
421 id: &ObjectId,
422 privileges: &HashSet<Privilege>,
423 revokees: &[crate::analysis::facts::RoleFact],
424 ) {
425 self.snapshot_relation(id);
426 if let Some(RelationOverlay::Present(rel)) = self.local.relations.get_mut(id) {
427 for revokee in revokees {
428 if let Some(role_id) = Self::resolve_role_name(revokee, &self.local.current_role) {
429 rel.privileges.revoke(&role_id, privileges);
430 }
431 }
432 }
433 }
434
435 pub fn apply(
436 &mut self,
437 mutation: &Mutation,
438 precomputed_cascade: Option<&CascadeResult>,
439 ) -> MutationResult {
440 match mutation {
441 Mutation::CreateSchema(_) => MutationResult::Applied,
442 Mutation::DropSchema(drop_schema) => {
443 if drop_schema.cascade {
444 let mut relations_to_drop = Vec::new();
445 for id in self.local.relations.keys() {
446 if drop_schema.names.contains(&id.schema) {
447 relations_to_drop.push(id.clone());
448 }
449 }
450 for id in relations_to_drop {
451 self.snapshot_relation(&id);
452 self.local.relations.insert(id, RelationOverlay::Dropped);
453 }
454
455 let mut types_to_drop = Vec::new();
456 for id in self.local.types.keys() {
457 if drop_schema.names.contains(&id.schema) {
458 types_to_drop.push(id.clone());
459 }
460 }
461 for id in types_to_drop {
462 self.snapshot_type(&id);
463 self.local.types.insert(id, TypeOverlay::Dropped);
464 }
465
466 let mut seqs_to_drop = Vec::new();
467 for id in self.local.sequences.keys() {
468 if drop_schema.names.contains(&id.schema) {
469 seqs_to_drop.push(id.clone());
470 }
471 }
472 for id in seqs_to_drop {
473 self.snapshot_sequence(&id);
474 self.local.sequences.insert(id, SequenceOverlay::Dropped);
475 }
476
477 self.snapshot_fk_graph_full();
478 self.snapshot_view_graph_full();
479 self.snapshot_index_graph_full();
480 self.snapshot_partition_graph_full();
481 self.snapshot_sequence_graph_full();
482 self.snapshot_rename_graph_full();
483 self.snapshot_trigger_graph_full();
484 self.snapshot_publication_graph_full();
485
486 let g = &mut self.local.graph;
487 g.foreign_keys.retain(|fk| {
488 !drop_schema.names.contains(&fk.from_table.schema)
489 && !drop_schema.names.contains(&fk.to_table.schema)
490 });
491 g.views
492 .retain(|v| !drop_schema.names.contains(&v.view_id.schema));
493 g.indexes
494 .retain(|idx| !drop_schema.names.contains(&idx.index_id.schema));
495 g.partitions.retain(|p| {
496 !drop_schema.names.contains(&p.parent.schema)
497 && !drop_schema.names.contains(&p.child.schema)
498 });
499 g.sequences
500 .retain(|s| !drop_schema.names.contains(&s.sequence_id.schema));
501 g.renames.retain(|r| {
502 !drop_schema.names.contains(&r.from.schema)
503 && !drop_schema.names.contains(&r.to.schema)
504 });
505 g.trigger_dependencies.retain(|t| {
506 !drop_schema.names.contains(&t.trigger_id.schema)
507 && !drop_schema.names.contains(&t.table_id.schema)
508 && !drop_schema.names.contains(&t.function_id.schema)
509 });
510 g.publication_dependencies
511 .retain(|p| !drop_schema.names.contains(&p.table_id.schema));
512 }
513 MutationResult::Applied
514 }
515 Mutation::DropTable(drop_table) => {
516 if !self.relation_is_present(&drop_table.id) {
517 if drop_table.if_exists {
518 return MutationResult::Skipped;
519 } else {
520 self.local.confidence = Confidence::Tainted;
521 return MutationResult::Skipped;
522 }
523 }
524
525 self.snapshot_trigger_graph_full();
527 self.local
528 .graph
529 .trigger_dependencies
530 .retain(|t| t.table_id != drop_table.id);
531 if drop_table.cascade {
534 let triggers_to_drop: Vec<ObjectId> = self
535 .local
536 .triggers
537 .iter()
538 .filter_map(|(id, overlay)| {
539 if let TriggerOverlay::Present(t) = overlay {
540 if t.table_id == drop_table.id {
541 Some(id.clone())
542 } else {
543 None
544 }
545 } else {
546 None
547 }
548 })
549 .collect();
550 for tid in triggers_to_drop {
551 self.snapshot_trigger(&tid);
552 self.local.triggers.insert(tid, TriggerOverlay::Dropped);
553 }
554 }
555
556 let renames = self.local.graph.renames.clone();
557 let resolve = |id: &ObjectId| -> ObjectId {
558 let mut current = id;
559 loop {
560 match renames.iter().find(|r| &r.from == current) {
561 Some(edge) => current = &edge.to,
562 None => return current.clone(),
563 }
564 }
565 };
566
567 let resolved_drop = resolve(&drop_table.id);
568
569 if drop_table.cascade {
570 let local_closure;
571 let closure = match precomputed_cascade {
572 Some(c) => c,
573 None => {
574 local_closure = self.get_cascade_closure(&drop_table.id);
575 &local_closure
576 }
577 };
578
579 for dropped_rel_id in &closure.dropped_relations {
580 self.snapshot_relation(dropped_rel_id);
581 self.local
582 .relations
583 .insert(dropped_rel_id.clone(), RelationOverlay::Dropped);
584 }
585
586 self.snapshot_index_graph_full();
587 self.local
588 .graph
589 .indexes
590 .retain(|idx| !closure.dropped_indexes.contains(&resolve(&idx.index_id)));
591
592 self.snapshot_fk_graph_full();
593 self.local.graph.foreign_keys.retain(|fk| {
594 let from_dropped =
595 closure.dropped_relations.contains(&resolve(&fk.from_table));
596 let to_dropped = closure.dropped_relations.contains(&resolve(&fk.to_table));
597 let constraint_explicitly_dropped = if let Some(cname) = &fk.constraint_name
598 {
599 closure
600 .dropped_constraints
601 .contains(&(resolve(&fk.from_table), cname.clone()))
602 } else {
603 false
604 };
605 !(from_dropped || to_dropped || constraint_explicitly_dropped)
606 });
607
608 self.snapshot_view_graph_full();
609 self.local
610 .graph
611 .views
612 .retain(|v| !closure.dropped_relations.contains(&resolve(&v.view_id)));
613 } else {
614 let has_view_deps = self
615 .local
616 .graph
617 .views
618 .iter()
619 .any(|v| v.depends_on.iter().any(|dep| resolve(dep) == resolved_drop));
620 let has_fk_deps = self.local.graph.foreign_keys.iter().any(|fk| {
621 resolve(&fk.to_table) == resolved_drop
622 && resolve(&fk.from_table) != resolved_drop
623 });
624 let has_partition_deps = self
625 .local
626 .graph
627 .partitions
628 .iter()
629 .any(|p| resolve(&p.parent) == resolved_drop);
630
631 if has_view_deps || has_fk_deps || has_partition_deps {
632 self.local.confidence = Confidence::Tainted;
633 return MutationResult::Skipped;
634 }
635
636 self.snapshot_relation(&drop_table.id);
637 self.local
638 .relations
639 .insert(drop_table.id.clone(), RelationOverlay::Dropped);
640 }
641
642 self.snapshot_partition_graph_full();
643 self.local.graph.partitions.retain(|p| {
644 resolve(&p.parent) != resolved_drop && resolve(&p.child) != resolved_drop
645 });
646
647 MutationResult::Applied
648 }
649 Mutation::CreateTable(create) => {
650 if create.if_not_exists && self.relation_is_present(&create.id) {
651 return MutationResult::Skipped;
652 }
653
654 self.snapshot_relation(&create.id);
655
656 self.snapshot_generation_counter();
657 self.local.generation_counter += 1;
658 let generation = self.local.generation_counter;
659
660 let resolved_persistence = match create.persistence {
661 PersistenceMutation::Permanent => {
662 crate::model::relation::Persistence::Permanent
663 }
664 PersistenceMutation::Temporary => {
665 crate::model::relation::Persistence::Temporary
666 }
667 PersistenceMutation::Unlogged => crate::model::relation::Persistence::Unlogged,
668 };
669
670 let mut rel_state = RelationState::new(
671 create.id.clone(),
672 ObjectId::new("public", &self.local.current_role),
673 generation,
674 if create.as_select { None } else { Some(0) },
675 RelationKind::Table,
676 resolved_persistence,
677 self.local.transactions.len(),
678 );
679
680 rel_state.partition_type = create
682 .partition_by
683 .as_ref()
684 .and_then(|pb| pb.split_whitespace().nth(2).map(|s| s.to_uppercase()))
685 .or_else(|| {
686 create.partition_of.as_ref().and_then(|parent_id| {
687 self.local.relations.get(parent_id).and_then(|r| {
688 if let RelationOverlay::Present(rel) = r {
689 rel.partition_type.clone()
690 } else {
691 None
692 }
693 })
694 })
695 });
696 rel_state.partition_by = create.partition_by.clone();
697
698 let pk_columns: HashSet<&str> = create
699 .table_constraints
700 .iter()
701 .filter_map(|tc| {
702 if let TableConstraintFact::PrimaryKey { columns } = tc {
703 Some(columns.iter().map(|s| s.as_str()))
704 } else {
705 None
706 }
707 })
708 .flatten()
709 .collect();
710
711 for col in &create.columns {
712 let is_pk = col.is_primary_key || pk_columns.contains(col.name.as_str());
713 rel_state.apply_column_action(&ColumnAction::Add {
714 name: col.name.clone(),
715 data_type: col.ty.clone(),
716 not_null: col.not_null || is_pk,
717 default: col.default.clone(),
718 });
719 }
720
721 self.local
722 .relations
723 .insert(create.id.clone(), RelationOverlay::Present(rel_state));
724
725 if let Some(parent_id) = &create.partition_of {
726 self.snapshot_partition_graph();
727 self.local.graph.partitions.push(PartitionEdge {
728 parent: parent_id.clone(),
729 child: create.id.clone(),
730 });
731 }
732
733 if !create.foreign_keys.is_empty() {
734 self.snapshot_fk_graph();
735 }
736
737 for fk in &create.foreign_keys {
738 self.local.graph.foreign_keys.push(FkEdge {
739 constraint_name: fk.constraint_name.clone(),
740 from_table: create.id.clone(),
741 from_columns: fk.from_columns.clone(),
742 to_table: fk.to_table.clone(),
743 to_columns: fk.to_columns.clone(),
744 from_generation: generation,
745 });
746 }
747 MutationResult::Applied
748 }
749 Mutation::CreateView(create_view) => {
750 self.snapshot_relation(&create_view.id);
751 self.snapshot_generation_counter();
752 self.local.generation_counter += 1;
753 let generation = self.local.generation_counter;
754
755 self.local.relations.insert(
756 create_view.id.clone(),
757 RelationOverlay::Present(RelationState::new(
758 create_view.id.clone(),
759 ObjectId::new("public", &self.local.current_role),
760 generation,
761 None,
762 RelationKind::View,
763 Persistence::Permanent,
764 self.local.transactions.len(),
765 )),
766 );
767
768 self.snapshot_view_graph();
769 self.local.graph.views.push(ViewEdge {
770 view_id: create_view.id.clone(),
771 depends_on: create_view.depends_on.clone(),
772 view_generation: generation,
773 });
774 MutationResult::Applied
775 }
776 Mutation::CreateMaterializedView(create_mv) => {
777 self.snapshot_relation(&create_mv.id);
778 self.snapshot_generation_counter();
779 self.local.generation_counter += 1;
780 let generation = self.local.generation_counter;
781
782 self.local.relations.insert(
783 create_mv.id.clone(),
784 RelationOverlay::Present(RelationState::new(
785 create_mv.id.clone(),
786 ObjectId::new("public", &self.local.current_role),
787 generation,
788 None,
789 RelationKind::MaterializedView,
790 Persistence::Permanent,
791 self.local.transactions.len(),
792 )),
793 );
794
795 self.snapshot_view_graph();
796 self.local.graph.views.push(ViewEdge {
797 view_id: create_mv.id.clone(),
798 depends_on: create_mv.depends_on.clone(),
799 view_generation: generation,
800 });
801 MutationResult::Applied
802 }
803 Mutation::RefreshMaterializedView(_) => MutationResult::Applied,
804 Mutation::CreateIndex(create_idx) => {
805 if create_idx.if_not_exists
806 && self
807 .local
808 .graph
809 .indexes
810 .iter()
811 .any(|ix| ix.index_id == create_idx.id)
812 {
813 return MutationResult::Skipped;
814 }
815 self.snapshot_index_graph();
816 self.local.graph.indexes.push(IndexEdge {
817 index_id: create_idx.id.clone(),
818 relation_id: create_idx.table.clone(),
819 using_method: create_idx.using_method.clone(),
820 has_predicate: create_idx.has_predicate,
821 is_concurrent: create_idx.concurrently,
822 is_unique: create_idx.unique,
823 });
824 MutationResult::Applied
825 }
826 Mutation::CreatePolicy(create_policy) => {
827 self.snapshot_relation(&create_policy.table);
828 if let Some(RelationOverlay::Present(rel)) =
829 self.local.relations.get_mut(&create_policy.table)
830 {
831 rel.policies.insert(create_policy.name.clone());
832 }
833 MutationResult::Applied
834 }
835 Mutation::DropPolicy(drop_policy) => {
836 self.snapshot_relation(&drop_policy.table);
837 if let Some(RelationOverlay::Present(rel)) =
838 self.local.relations.get_mut(&drop_policy.table)
839 {
840 rel.policies.remove(&drop_policy.name);
841 }
842 MutationResult::Applied
843 }
844 Mutation::CreateTrigger(create_trigger) => {
845 let trigger_id = ObjectId::new(
846 create_trigger.table.schema.clone(),
847 create_trigger.name.clone(),
848 );
849 self.snapshot_trigger(&trigger_id);
850 self.local.triggers.insert(
851 trigger_id.clone(),
852 TriggerOverlay::Present(crate::model::trigger::TriggerState {
853 id: trigger_id.clone(),
854 table_id: create_trigger.table.clone(),
855 generation: self.local.generation_counter,
856 }),
857 );
858
859 self.snapshot_relation(&create_trigger.table);
860 if let Some(RelationOverlay::Present(rel)) =
861 self.local.relations.get_mut(&create_trigger.table)
862 {
863 rel.triggers.insert(create_trigger.name.clone());
864 }
865
866 self.snapshot_trigger_graph_full();
867 self.local
868 .graph
869 .trigger_dependencies
870 .push(crate::analysis::graph::TriggerEdge {
871 trigger_id,
872 table_id: create_trigger.table.clone(),
873 function_id: create_trigger.function_id.clone(),
874 });
875
876 MutationResult::Applied
877 }
878 Mutation::DropTrigger(drop_trigger) => {
879 let trigger_id =
880 ObjectId::new(drop_trigger.table.schema.clone(), drop_trigger.name.clone());
881 self.snapshot_trigger(&trigger_id);
882 self.local
883 .triggers
884 .insert(trigger_id.clone(), TriggerOverlay::Dropped);
885
886 self.snapshot_relation(&drop_trigger.table);
887 if let Some(RelationOverlay::Present(rel)) =
888 self.local.relations.get_mut(&drop_trigger.table)
889 {
890 rel.triggers.remove(&drop_trigger.name);
891 }
892
893 self.snapshot_trigger_graph_full();
894 self.local
895 .graph
896 .trigger_dependencies
897 .retain(|t| t.trigger_id != trigger_id);
898
899 MutationResult::Applied
900 }
901 Mutation::AlterTable(alter) => {
902 self.snapshot_relation(&alter.id);
903 let rel_overlay = self.local.relations.get_mut(&alter.id);
904 if let Some(RelationOverlay::Present(rel)) = rel_overlay {
905 let generation = rel.generation;
906 match &alter.action {
907 AlterTableActionMutation::AddColumn {
908 name,
909 ty,
910 if_not_exists,
911 not_null,
912 default,
913 depends_on,
914 } => {
915 if !(*if_not_exists && rel.has_column(name)) {
916 if let Some(existing_col) =
917 rel.columns.iter().find(|c| c.name == *name)
918 && existing_col.data_type.as_deref() != ty.as_deref()
919 {
920 return MutationResult::Conflict {
921 reason: format!(
922 "column '{}' already added with type {} (likely an earlier file in this chain), this file adds it again with type {}",
923 name,
924 existing_col.data_type.as_deref().unwrap_or("unknown"),
925 ty.as_deref().unwrap_or("unknown")
926 ),
927 };
928 }
929 rel.apply_column_action(&ColumnAction::Add {
930 name: name.clone(),
931 data_type: ty.clone(),
932 not_null: *not_null,
933 default: default.clone(),
934 });
935
936 if let Some((source_table, source_col)) = depends_on {
937 self.snapshot_column_graph();
938 self.local.graph.column_dependencies.push(
939 crate::analysis::graph::ColumnDependencyEdge {
940 table_id: alter.id.clone(),
941 column: name.clone(),
942 depends_on_table: source_table.clone(),
943 depends_on_column: source_col.clone(),
944 },
945 );
946 }
947 }
948 }
949 AlterTableActionMutation::DropColumn { name, if_exists } => {
950 if !rel.has_column(name) {
951 if *if_exists {
952 return MutationResult::Skipped;
954 }
955 self.local.confidence = Confidence::Tainted;
957 return MutationResult::Skipped;
958 }
959 rel.apply_column_action(&ColumnAction::Drop { name: name.clone() });
960 }
961 AlterTableActionMutation::RenameColumn { from, to } => {
962 rel.apply_column_action(&ColumnAction::Rename {
963 from: from.clone(),
964 to: to.clone(),
965 });
966 }
967 AlterTableActionMutation::SetNotNull { column } => {
968 rel.apply_column_action(&ColumnAction::SetNotNull {
969 name: column.clone(),
970 });
971 }
972 AlterTableActionMutation::DropNotNull { column } => {
973 rel.apply_column_action(&ColumnAction::DropNotNull {
974 name: column.clone(),
975 });
976 }
977 AlterTableActionMutation::SetType { column, ty, .. } => {
978 rel.apply_column_action(&ColumnAction::SetType {
979 name: column.clone(),
980 data_type: ty.clone(),
981 });
982 }
983 AlterTableActionMutation::SetDefault { column, default } => {
984 rel.apply_column_action(&ColumnAction::SetDefault {
985 name: column.clone(),
986 default: default.clone(),
987 });
988 }
989 AlterTableActionMutation::AddForeignKey {
990 constraint_name,
991 to_table,
992 from_columns,
993 to_columns,
994 ..
995 } => {
996 self.snapshot_fk_graph();
997 self.local.graph.foreign_keys.push(FkEdge {
998 constraint_name: constraint_name.clone(),
999 from_table: alter.id.clone(),
1000 from_columns: from_columns.clone(),
1001 to_table: to_table.clone(),
1002 to_columns: to_columns.clone(),
1003 from_generation: generation,
1004 });
1005 }
1006 AlterTableActionMutation::DropConstraint { name } => {
1007 self.snapshot_fk_graph();
1008 self.local.graph.foreign_keys.retain(|fk| {
1009 !(fk.from_table == alter.id
1010 && fk.constraint_name.as_ref() == Some(name))
1011 });
1012 }
1013 AlterTableActionMutation::AttachPartition { child } => {
1014 self.snapshot_partition_graph();
1015 self.local.graph.partitions.push(PartitionEdge {
1016 parent: alter.id.clone(),
1017 child: child.clone(),
1018 });
1019 }
1020 AlterTableActionMutation::DetachPartition { child } => {
1021 self.snapshot_partition_graph();
1022 self.local
1023 .graph
1024 .partitions
1025 .retain(|p| !(p.parent == alter.id && p.child == *child));
1026 }
1027 _ => {}
1028 }
1029 }
1030 MutationResult::Applied
1031 }
1032 Mutation::CreateType(create_type) => {
1033 self.snapshot_type(&create_type.id);
1034 self.snapshot_generation_counter();
1035 self.local.generation_counter += 1;
1036 let generation = self.local.generation_counter;
1037
1038 self.local.types.insert(
1039 create_type.id.clone(),
1040 TypeOverlay::Present(TypeState {
1041 id: create_type.id.clone(),
1042 generation,
1043 kind: create_type.kind.clone(),
1044 }),
1045 );
1046 MutationResult::Applied
1047 }
1048 Mutation::AlterType(alter_type) => {
1049 self.snapshot_type(&alter_type.id);
1050 if let Some(TypeOverlay::Present(t)) = self.local.types.get_mut(&alter_type.id) {
1051 match &alter_type.action {
1052 AlterTypeActionMutation::AddValue { new_value } => {
1053 if let TypeKind::Enum { variants } = &mut t.kind {
1054 variants.push(new_value.clone());
1055 }
1056 }
1057 }
1058 }
1059 MutationResult::Applied
1060 }
1061 Mutation::CreateDomain(create_domain) => {
1062 self.snapshot_type(&create_domain.id);
1063 self.snapshot_generation_counter();
1064 self.local.generation_counter += 1;
1065 let generation = self.local.generation_counter;
1066
1067 self.local.types.insert(
1068 create_domain.id.clone(),
1069 TypeOverlay::Present(TypeState {
1070 id: create_domain.id.clone(),
1071 generation,
1072 kind: TypeKind::Domain {
1073 base_type: create_domain.base_type.clone(),
1074 },
1075 }),
1076 );
1077 MutationResult::Applied
1078 }
1079 Mutation::AlterDomain(_) => MutationResult::Applied,
1080 Mutation::DropDomain(drop_domain) => {
1081 for id in &drop_domain.ids {
1082 self.snapshot_type(id);
1083 self.local.types.insert(id.clone(), TypeOverlay::Dropped);
1084 }
1085 MutationResult::Applied
1086 }
1087 Mutation::CreateSequence(create_seq) => {
1088 if create_seq.if_not_exists && self.local.sequences.contains_key(&create_seq.id) {
1089 return MutationResult::Skipped;
1090 }
1091 self.snapshot_sequence(&create_seq.id);
1092 self.snapshot_generation_counter();
1093 self.local.generation_counter += 1;
1094 let generation = self.local.generation_counter;
1095
1096 self.local.sequences.insert(
1097 create_seq.id.clone(),
1098 SequenceOverlay::Present(SequenceState {
1099 id: create_seq.id.clone(),
1100 generation,
1101 }),
1102 );
1103
1104 if let Some((table_id, col)) = &create_seq.owned_by {
1105 self.snapshot_sequence_graph();
1106 self.local.graph.sequences.push(SequenceEdge {
1107 sequence_id: create_seq.id.clone(),
1108 table_id: table_id.clone(),
1109 column: col.clone(),
1110 });
1111 }
1112 MutationResult::Applied
1113 }
1114 Mutation::AlterSequence(alter_seq) => {
1115 self.snapshot_sequence(&alter_seq.id);
1116 if let Some((table_id, col)) = &alter_seq.owned_by {
1117 self.snapshot_sequence_graph();
1118 self.local
1119 .graph
1120 .sequences
1121 .retain(|s| s.sequence_id != alter_seq.id);
1122 self.local.graph.sequences.push(SequenceEdge {
1123 sequence_id: alter_seq.id.clone(),
1124 table_id: table_id.clone(),
1125 column: col.clone(),
1126 });
1127 }
1128 MutationResult::Applied
1129 }
1130 Mutation::DropSequence(drop_seq) => {
1131 for id in &drop_seq.ids {
1132 self.snapshot_sequence(id);
1133 self.local
1134 .sequences
1135 .insert(id.clone(), SequenceOverlay::Dropped);
1136 }
1137 self.snapshot_sequence_graph_full();
1138 self.local
1139 .graph
1140 .sequences
1141 .retain(|s| !drop_seq.ids.contains(&s.sequence_id));
1142 MutationResult::Applied
1143 }
1144 Mutation::Rename(rename) => {
1145 self.snapshot_relation(&rename.old_id);
1146 self.snapshot_relation(&rename.new_id);
1147 if let Some(RelationOverlay::Present(mut state)) =
1148 self.local.relations.remove(&rename.old_id)
1149 {
1150 state.id = rename.new_id.clone();
1151 self.local
1152 .relations
1153 .insert(rename.new_id.clone(), RelationOverlay::Present(state));
1154 }
1155 self.snapshot_rename_graph();
1156 self.local.graph.renames.push(RenameEdge {
1157 from: rename.old_id.clone(),
1158 to: rename.new_id.clone(),
1159 });
1160
1161 self.snapshot_fk_graph_full();
1163 self.snapshot_view_graph_full();
1164 self.snapshot_index_graph_full();
1165 self.snapshot_partition_graph_full();
1166 self.snapshot_sequence_graph_full();
1167 self.snapshot_column_graph_full();
1168 self.snapshot_trigger_graph_full();
1169 self.snapshot_publication_graph_full();
1170
1171 self.local
1172 .graph
1173 .propagate_rename(&rename.old_id, &rename.new_id);
1174
1175 MutationResult::Applied
1176 }
1177 Mutation::DropView(drop_view) => {
1178 for id in &drop_view.ids {
1179 self.snapshot_relation(id);
1180 self.local
1181 .relations
1182 .insert(id.clone(), RelationOverlay::Dropped);
1183 }
1184 self.snapshot_view_graph_full();
1185 self.local
1186 .graph
1187 .views
1188 .retain(|v| !drop_view.ids.contains(&v.view_id));
1189 MutationResult::Applied
1190 }
1191 Mutation::DropMaterializedView(drop_mv) => {
1192 for id in &drop_mv.ids {
1193 self.snapshot_relation(id);
1194 self.local
1195 .relations
1196 .insert(id.clone(), RelationOverlay::Dropped);
1197 }
1198 self.snapshot_view_graph_full();
1199 self.local
1200 .graph
1201 .views
1202 .retain(|v| !drop_mv.ids.contains(&v.view_id));
1203 MutationResult::Applied
1204 }
1205 Mutation::DropIndex(drop_idx) => {
1206 self.snapshot_index_graph();
1207 self.local
1208 .graph
1209 .indexes
1210 .retain(|idx| idx.index_id != drop_idx.id);
1211 MutationResult::Applied
1212 }
1213 Mutation::SearchPath(sp) => {
1214 self.snapshot_search_path();
1215 match &sp.target {
1216 SearchPathTarget::Default => {
1217 self.local.search_path = vec!["public".to_string()];
1218 }
1219 SearchPathTarget::Schemas(schemas) => {
1220 self.local.search_path = schemas.clone();
1221 }
1222 }
1223 MutationResult::Applied
1224 }
1225 Mutation::BeginTransaction => {
1226 self.local
1227 .transactions
1228 .push(TransactionFrame::new("transaction"));
1229 MutationResult::Applied
1230 }
1231 Mutation::CommitTransaction => {
1232 while self.local.transactions.pop().is_some() {}
1233 MutationResult::Applied
1234 }
1235 Mutation::RollbackTransaction => {
1236 while let Some(frame) = self.local.transactions.pop() {
1237 self.rollback_frame(frame);
1238 }
1239 MutationResult::Applied
1240 }
1241 Mutation::RollbackToSavepoint(rts) => {
1242 let mut rolled_back = Vec::new();
1243 while let Some(frame) = self.local.transactions.last() {
1244 if frame.name == rts.name {
1245 break;
1246 }
1247 rolled_back.push(self.local.transactions.pop().unwrap());
1248 }
1249 if let Some(frame) = self.local.transactions.last_mut() {
1250 let mut temp_frame = TransactionFrame::new(&frame.name);
1251 while let Some(change) = frame.undo_log.pop() {
1252 temp_frame.undo_log.push(change);
1253 }
1254 self.rollback_frame(temp_frame);
1255 }
1256 for frame in rolled_back.into_iter().rev() {
1257 self.rollback_frame(frame);
1258 }
1259 MutationResult::Applied
1260 }
1261 Mutation::Savepoint(sp) => {
1262 self.local
1263 .transactions
1264 .push(TransactionFrame::new(sp.name.clone()));
1265 MutationResult::Applied
1266 }
1267 Mutation::ReleaseSavepoint(rsp) => {
1268 let mut rolled_back = Vec::new();
1269 while let Some(frame) = self.local.transactions.last() {
1270 if frame.name == rsp.name {
1271 break;
1272 }
1273 rolled_back.push(self.local.transactions.pop().unwrap());
1274 }
1275 if let Some(frame) = self.local.transactions.pop()
1276 && let Some(outer) = self.local.transactions.last_mut()
1277 {
1278 outer.undo_log.extend(frame.undo_log);
1279 }
1280 for frame in rolled_back.into_iter().rev() {
1281 self.local.transactions.push(frame);
1282 }
1283 MutationResult::Applied
1284 }
1285 Mutation::Opaque(_) => {
1286 self.snapshot_confidence();
1287 self.local.confidence = Confidence::Tainted;
1288 MutationResult::Applied
1289 }
1290 Mutation::CreateFunction(f) => {
1291 self.snapshot_function(&f.id);
1292 self.snapshot_generation_counter();
1293 self.local.generation_counter += 1;
1294 let _generation = self.local.generation_counter;
1295
1296 let volatility = f
1297 .options
1298 .iter()
1299 .find_map(|opt| {
1300 if let crate::analysis::facts::FuncOptionFact::Volatility(v) = opt {
1301 Some(match v {
1302 crate::analysis::facts::VolatilityKind::Volatile => {
1303 crate::model::function::Volatility::Volatile
1304 }
1305 crate::analysis::facts::VolatilityKind::Stable => {
1306 crate::model::function::Volatility::Stable
1307 }
1308 crate::analysis::facts::VolatilityKind::Immutable => {
1309 crate::model::function::Volatility::Immutable
1310 }
1311 })
1312 } else {
1313 None
1314 }
1315 })
1316 .unwrap_or(crate::model::function::Volatility::Volatile);
1317
1318 let security = f
1319 .options
1320 .iter()
1321 .find_map(|opt| {
1322 if let crate::analysis::facts::FuncOptionFact::Security(s) = opt {
1323 Some(match s {
1324 crate::analysis::facts::SecurityKind::Invoker => {
1325 crate::model::function::SecurityMode::Invoker
1326 }
1327 crate::analysis::facts::SecurityKind::Definer => {
1328 crate::model::function::SecurityMode::Definer
1329 }
1330 })
1331 } else {
1332 None
1333 }
1334 })
1335 .unwrap_or(crate::model::function::SecurityMode::Invoker);
1336
1337 let language = f
1338 .options
1339 .iter()
1340 .find_map(|opt| {
1341 if let crate::analysis::facts::FuncOptionFact::Language(l) = opt {
1342 Some(l.clone())
1343 } else {
1344 None
1345 }
1346 })
1347 .unwrap_or_else(|| "sql".to_string());
1348
1349 self.local.functions.insert(
1350 f.id.clone(),
1351 crate::model::function::FunctionOverlay::Present(
1352 crate::model::function::FunctionState {
1353 id: f.id.clone(),
1354 arg_types: f.params.iter().map(|p| p.ty.clone()).collect(),
1355 return_type: f
1356 .return_type
1357 .as_ref()
1358 .map(|rt| format!("{:?}", rt))
1359 .unwrap_or_default(),
1360 volatility,
1361 language,
1362 security,
1363 },
1364 ),
1365 );
1366 MutationResult::Applied
1367 }
1368 Mutation::AlterFunction(f) => {
1369 self.snapshot_function(&f.id);
1370 MutationResult::Applied
1372 }
1373 Mutation::DropFunction(f) => {
1374 let mut any_applied = false;
1375 for sig in &f.signatures {
1376 let sig_str = format!("{}({})", sig.name.name.resolve(), sig.params.join(","));
1377 let schema = self.resolve_function_schema(&sig.name, &sig_str);
1378 let id = ObjectId::new(schema, sig_str);
1379 if !matches!(
1380 self.local.functions.get(&id),
1381 Some(crate::model::function::FunctionOverlay::Present(_))
1382 ) {
1383 if !f.if_exists {
1384 self.local.confidence = Confidence::Tainted;
1385 return MutationResult::Skipped;
1386 }
1387 } else {
1388 any_applied = true;
1389 self.snapshot_function(&id);
1390 self.local
1391 .functions
1392 .insert(id, crate::model::function::FunctionOverlay::Dropped);
1393 }
1394 }
1395 if any_applied {
1396 MutationResult::Applied
1397 } else {
1398 MutationResult::Skipped
1399 }
1400 }
1401 Mutation::CreateProcedure(p) => {
1402 self.snapshot_function(&p.id);
1403 self.snapshot_generation_counter();
1404 self.local.generation_counter += 1;
1405 let _generation = self.local.generation_counter;
1406
1407 self.local.functions.insert(
1408 p.id.clone(),
1409 crate::model::function::FunctionOverlay::Present(
1410 crate::model::function::FunctionState {
1411 id: p.id.clone(),
1412 arg_types: p.params.iter().map(|p| p.ty.clone()).collect(),
1413 return_type: "void".to_string(),
1414 volatility: crate::model::function::Volatility::Volatile,
1415 language: "sql".to_string(),
1416 security: crate::model::function::SecurityMode::Invoker,
1417 },
1418 ),
1419 );
1420 MutationResult::Applied
1421 }
1422 Mutation::AlterProcedure(p) => {
1423 self.snapshot_function(&p.id);
1424 MutationResult::Applied
1426 }
1427 Mutation::DropProcedure(p) => {
1428 let mut any_applied = false;
1429 for sig in &p.signatures {
1430 let sig_str = format!("{}({})", sig.name.name.resolve(), sig.params.join(","));
1431 let schema = self.resolve_function_schema(&sig.name, &sig_str);
1432 let id = ObjectId::new(schema, sig_str);
1433 if !matches!(
1434 self.local.functions.get(&id),
1435 Some(crate::model::function::FunctionOverlay::Present(_))
1436 ) {
1437 if !p.if_exists {
1438 self.local.confidence = Confidence::Tainted;
1439 return MutationResult::Skipped;
1440 }
1441 } else {
1442 any_applied = true;
1443 self.snapshot_function(&id);
1444 self.local
1445 .functions
1446 .insert(id, crate::model::function::FunctionOverlay::Dropped);
1447 }
1448 }
1449 if any_applied {
1450 MutationResult::Applied
1451 } else {
1452 MutationResult::Skipped
1453 }
1454 }
1455 Mutation::CreatePublication(p) => {
1456 self.snapshot_publication(&p.name);
1457 self.snapshot_generation_counter();
1458 self.local.generation_counter += 1;
1459 let generation = self.local.generation_counter;
1460
1461 self.local.publications.insert(
1462 p.name.clone(),
1463 crate::model::replication::PublicationOverlay::Present(
1464 crate::model::replication::PublicationState {
1465 name: p.name.clone(),
1466 scope: p.scope.clone(),
1467 params: p.params.clone(),
1468 generation,
1469 },
1470 ),
1471 );
1472
1473 if let crate::analysis::facts::PublicationScope::Explicit(objects) = &p.scope {
1474 self.snapshot_publication_graph_full();
1475 for obj in objects {
1476 if let crate::analysis::facts::PublicationObjectFact::Table {
1477 name, ..
1478 } = obj
1479 {
1480 let table_id = self.resolve_relation_id(name);
1481 self.local
1482 .graph
1483 .publication_dependencies
1484 .push(PublicationEdge {
1485 publication_name: p.name.clone(),
1486 table_id,
1487 });
1488 }
1489 }
1490 }
1491 MutationResult::Applied
1492 }
1493 Mutation::AlterPublication(p) => {
1494 self.snapshot_publication(&p.name);
1495 if !self.local.publications.contains_key(&p.name) {
1496 self.local.confidence = Confidence::Tainted;
1497 return MutationResult::Skipped;
1498 }
1499 self.snapshot_generation_counter();
1500 self.local.generation_counter += 1;
1501 let new_gen = self.local.generation_counter;
1502
1503 if let Some(crate::model::replication::PublicationOverlay::Present(publ)) =
1504 self.local.publications.get_mut(&p.name)
1505 {
1506 publ.generation = new_gen;
1507 }
1508 MutationResult::Applied
1509 }
1510 Mutation::DropPublication(p) => {
1511 for name in &p.names {
1512 self.snapshot_publication(name);
1513 if !p.if_exists && !self.local.publications.contains_key(name) {
1514 self.local.confidence = Confidence::Tainted;
1515 return MutationResult::Skipped;
1516 }
1517 self.local.publications.insert(
1518 name.clone(),
1519 crate::model::replication::PublicationOverlay::Dropped,
1520 );
1521 }
1522 self.snapshot_publication_graph_full();
1523 self.local
1524 .graph
1525 .publication_dependencies
1526 .retain(|edge| !p.names.contains(&edge.publication_name));
1527 MutationResult::Applied
1528 }
1529 Mutation::CreateSubscription(s) => {
1530 let name = s.name.clone().unwrap_or_else(|| "unnamed_sub".into());
1531 self.snapshot_subscription(&name);
1532 self.snapshot_generation_counter();
1533 self.local.generation_counter += 1;
1534 let generation = self.local.generation_counter;
1535
1536 self.local.subscriptions.insert(
1537 name.clone(),
1538 crate::model::replication::SubscriptionOverlay::Present(
1539 crate::model::replication::SubscriptionState {
1540 name,
1541 connection: s.connection.clone(),
1542 publications: s.publications.clone(),
1543 params: s.params.clone(),
1544 generation,
1545 },
1546 ),
1547 );
1548 MutationResult::Applied
1549 }
1550 Mutation::AlterSubscription(s) => {
1551 self.snapshot_subscription(&s.name);
1552 if !self.local.subscriptions.contains_key(&s.name) {
1553 self.local.confidence = Confidence::Tainted;
1554 return MutationResult::Skipped;
1555 }
1556 self.snapshot_generation_counter();
1557 self.local.generation_counter += 1;
1558 let new_gen = self.local.generation_counter;
1559
1560 if let Some(crate::model::replication::SubscriptionOverlay::Present(sub)) =
1561 self.local.subscriptions.get_mut(&s.name)
1562 {
1563 sub.generation = new_gen;
1564 }
1565 MutationResult::Applied
1566 }
1567 Mutation::DropSubscription(s) => {
1568 self.snapshot_subscription(&s.name);
1569 if !s.if_exists && !self.local.subscriptions.contains_key(&s.name) {
1570 self.local.confidence = Confidence::Tainted;
1571 return MutationResult::Skipped;
1572 }
1573 self.local.subscriptions.insert(
1574 s.name.clone(),
1575 crate::model::replication::SubscriptionOverlay::Dropped,
1576 );
1577 MutationResult::Applied
1578 }
1579 Mutation::CreateRole(r) => {
1580 let role_id = ObjectId::new("", &r.name);
1581 self.snapshot_role(&role_id);
1582 self.snapshot_generation_counter();
1583 self.local.generation_counter += 1;
1584 let _generation = self.local.generation_counter;
1585
1586 self.local.roles.insert(
1587 role_id.clone(),
1588 crate::model::role::RoleOverlay::Present(crate::model::role::RoleState {
1589 id: role_id,
1590 can_login: true,
1591 is_superuser: false,
1592 member_of: Vec::new(),
1593 granted_privileges: Vec::new(),
1594 }),
1595 );
1596 MutationResult::Applied
1597 }
1598 Mutation::AlterRole(r) => {
1599 if let Some(role_id) = Self::resolve_role_name(&r.name, &self.local.current_role) {
1600 self.snapshot_role(&role_id);
1601 if !self.local.roles.contains_key(&role_id) {
1602 self.local.confidence = Confidence::Tainted;
1603 return MutationResult::Skipped;
1604 }
1605 self.snapshot_generation_counter();
1606 self.local.generation_counter += 1;
1607 let _new_gen = self.local.generation_counter;
1608
1609 if let Some(crate::model::role::RoleOverlay::Present(_role)) =
1610 self.local.roles.get_mut(&role_id)
1611 {
1612 }
1614 MutationResult::Applied
1615 } else {
1616 MutationResult::Skipped
1617 }
1618 }
1619 Mutation::DropRole(r) => {
1620 for name in &r.names {
1621 if let Some(role_id) = Self::resolve_role_name(
1622 &crate::analysis::facts::RoleFact::Named {
1623 name: name.clone(),
1624 via_legacy_group_syntax: false,
1625 },
1626 &self.local.current_role,
1627 ) {
1628 self.snapshot_role(&role_id);
1629 if !r.if_exists && !self.local.roles.contains_key(&role_id) {
1630 self.local.confidence = Confidence::Tainted;
1631
1632 return MutationResult::Skipped;
1633 }
1634 self.local
1635 .roles
1636 .insert(role_id, crate::model::role::RoleOverlay::Dropped);
1637 }
1638 }
1639 MutationResult::Applied
1640 }
1641 Mutation::Grant(grant) => {
1642 let privileges = Self::resolve_grant_privileges(&grant.privileges);
1643 let grantees = &grant.grantees;
1644 match &grant.target {
1645 crate::analysis::mutations::ResolvedGrantTarget::Tables(ids) => {
1646 for id in ids {
1647 self.apply_grant_to_relation(id, &privileges, grantees);
1648 }
1649 }
1650 crate::analysis::mutations::ResolvedGrantTarget::AllTablesInSchema(schemas) => {
1651 let target_ids: Vec<ObjectId> = self
1652 .local
1653 .relations
1654 .keys()
1655 .filter(|id| schemas.contains(&id.schema))
1656 .cloned()
1657 .collect();
1658 for id in &target_ids {
1659 self.apply_grant_to_relation(id, &privileges, grantees);
1660 }
1661 }
1662 }
1663 MutationResult::Applied
1664 }
1665 Mutation::Revoke(revoke) => {
1666 let privileges = Self::resolve_grant_privileges(&revoke.privileges);
1667 let revokees = &revoke.revokees;
1668 match &revoke.target {
1669 crate::analysis::mutations::ResolvedGrantTarget::Tables(ids) => {
1670 for id in ids {
1671 self.apply_revoke_to_relation(id, &privileges, revokees);
1672 }
1673 }
1674 crate::analysis::mutations::ResolvedGrantTarget::AllTablesInSchema(schemas) => {
1675 let target_ids: Vec<ObjectId> = self
1676 .local
1677 .relations
1678 .keys()
1679 .filter(|id| schemas.contains(&id.schema))
1680 .cloned()
1681 .collect();
1682 for id in &target_ids {
1683 self.apply_revoke_to_relation(id, &privileges, revokees);
1684 }
1685 }
1686 }
1687 MutationResult::Applied
1688 }
1689 Mutation::CreateDatabase(_) => MutationResult::Applied,
1690 Mutation::AlterDatabase(_) => MutationResult::Applied,
1691 Mutation::DropDatabase(_) => MutationResult::Applied,
1692 Mutation::Vacuum { .. } => MutationResult::Applied,
1693 }
1694 }
1695
1696 fn snapshot_relation(&mut self, id: &ObjectId) {
1697 if let Some(frame) = self.local.transactions.last_mut() {
1698 let previous = self.local.relations.get(id).cloned();
1699 frame.undo_log.push(StateChange::RelationSnapshot {
1700 id: id.clone(),
1701 previous: Box::new(previous),
1702 });
1703 }
1704 }
1705
1706 fn snapshot_type(&mut self, id: &ObjectId) {
1707 if let Some(frame) = self.local.transactions.last_mut() {
1708 let previous = self.local.types.get(id).cloned();
1709 frame.undo_log.push(StateChange::TypeSnapshot {
1710 id: id.clone(),
1711 previous,
1712 });
1713 }
1714 }
1715
1716 fn snapshot_sequence(&mut self, id: &ObjectId) {
1717 if let Some(frame) = self.local.transactions.last_mut() {
1718 let previous = self.local.sequences.get(id).cloned();
1719 frame.undo_log.push(StateChange::SequenceSnapshot {
1720 id: id.clone(),
1721 previous,
1722 });
1723 }
1724 }
1725
1726 fn snapshot_function(&mut self, id: &ObjectId) {
1727 if let Some(frame) = self.local.transactions.last_mut() {
1728 let previous = self.local.functions.get(id).cloned();
1729 frame.undo_log.push(StateChange::FunctionSnapshot {
1730 id: id.clone(),
1731 previous,
1732 });
1733 }
1734 }
1735
1736 fn snapshot_publication(&mut self, name: &str) {
1737 if let Some(frame) = self.local.transactions.last_mut() {
1738 let previous = self.local.publications.get(name).cloned();
1739 frame.undo_log.push(StateChange::PublicationSnapshot {
1740 id: ObjectId::new("", name),
1741 previous,
1742 });
1743 }
1744 }
1745
1746 fn snapshot_subscription(&mut self, name: &str) {
1747 if let Some(frame) = self.local.transactions.last_mut() {
1748 let previous = self.local.subscriptions.get(name).cloned();
1749 frame.undo_log.push(StateChange::SubscriptionSnapshot {
1750 id: ObjectId::new("", name),
1751 previous,
1752 });
1753 }
1754 }
1755
1756 fn snapshot_role(&mut self, id: &ObjectId) {
1757 if let Some(frame) = self.local.transactions.last_mut() {
1758 let previous = self.local.roles.get(id).cloned();
1759 frame.undo_log.push(StateChange::RoleSnapshot {
1760 id: id.clone(),
1761 previous,
1762 });
1763 }
1764 }
1765
1766 fn snapshot_trigger(&mut self, id: &ObjectId) {
1767 if let Some(frame) = self.local.transactions.last_mut() {
1768 let previous = self.local.triggers.get(id).cloned();
1769 frame.undo_log.push(StateChange::TriggerSnapshot {
1770 id: id.clone(),
1771 previous,
1772 });
1773 }
1774 }
1775
1776 fn snapshot_trigger_graph_full(&mut self) {
1777 if let Some(frame) = self.local.transactions.last_mut() {
1778 frame.undo_log.push(StateChange::TriggerGraphSnapshot {
1779 previous: self.local.graph.trigger_dependencies.clone(),
1780 });
1781 }
1782 }
1783
1784 fn snapshot_publication_graph_full(&mut self) {
1785 if let Some(frame) = self.local.transactions.last_mut() {
1786 frame.undo_log.push(StateChange::PublicationGraphSnapshot {
1787 previous: self.local.graph.publication_dependencies.clone(),
1788 });
1789 }
1790 }
1791
1792 #[allow(dead_code)]
1793 fn snapshot_current_role(&mut self) {
1794 if let Some(frame) = self.local.transactions.last_mut() {
1795 frame.undo_log.push(StateChange::CurrentRoleSnapshot {
1796 previous: self.local.current_role.clone(),
1797 });
1798 }
1799 }
1800
1801 fn snapshot_search_path(&mut self) {
1802 if let Some(frame) = self.local.transactions.last_mut() {
1803 frame.undo_log.push(StateChange::SearchPathSnapshot {
1804 previous: self.local.search_path.clone(),
1805 });
1806 }
1807 }
1808
1809 fn snapshot_generation_counter(&mut self) {
1810 if let Some(frame) = self.local.transactions.last_mut() {
1811 frame.undo_log.push(StateChange::GenerationCounterSnapshot {
1812 previous: self.local.generation_counter,
1813 });
1814 }
1815 }
1816
1817 #[allow(dead_code)]
1818 fn snapshot_pending_validation(&mut self) {
1819 if let Some(frame) = self.local.transactions.last_mut() {
1820 frame.undo_log.push(StateChange::PendingValidationSnapshot {
1821 previous: self.local.pending_validation.clone(),
1822 });
1823 }
1824 }
1825
1826 fn snapshot_confidence(&mut self) {
1827 if let Some(frame) = self.local.transactions.last_mut() {
1828 frame.undo_log.push(StateChange::ConfidenceSnapshot {
1829 previous: self.local.confidence.clone(),
1830 });
1831 }
1832 }
1833
1834 fn snapshot_fk_graph(&mut self) {
1835 if let Some(frame) = self.local.transactions.last_mut() {
1836 frame.undo_log.push(StateChange::FkGraphLengthMarker {
1837 len: self.local.graph.foreign_keys.len(),
1838 });
1839 }
1840 }
1841
1842 fn snapshot_fk_graph_full(&mut self) {
1843 if let Some(frame) = self.local.transactions.last_mut() {
1844 frame.undo_log.push(StateChange::FkGraphSnapshot {
1845 previous: self.local.graph.foreign_keys.clone(),
1846 });
1847 }
1848 }
1849
1850 fn snapshot_view_graph(&mut self) {
1851 if let Some(frame) = self.local.transactions.last_mut() {
1852 frame.undo_log.push(StateChange::ViewGraphLengthMarker {
1853 len: self.local.graph.views.len(),
1854 });
1855 }
1856 }
1857
1858 fn snapshot_view_graph_full(&mut self) {
1859 if let Some(frame) = self.local.transactions.last_mut() {
1860 frame.undo_log.push(StateChange::ViewGraphSnapshot {
1861 previous: self.local.graph.views.clone(),
1862 });
1863 }
1864 }
1865
1866 fn snapshot_index_graph(&mut self) {
1867 if let Some(frame) = self.local.transactions.last_mut() {
1868 frame.undo_log.push(StateChange::IndexGraphLengthMarker {
1869 len: self.local.graph.indexes.len(),
1870 });
1871 }
1872 }
1873
1874 fn snapshot_index_graph_full(&mut self) {
1875 if let Some(frame) = self.local.transactions.last_mut() {
1876 frame.undo_log.push(StateChange::IndexGraphSnapshot {
1877 previous: self.local.graph.indexes.clone(),
1878 });
1879 }
1880 }
1881
1882 fn snapshot_partition_graph(&mut self) {
1883 if let Some(frame) = self.local.transactions.last_mut() {
1884 frame.undo_log.push(StateChange::PartitionGraphMarker {
1885 len: self.local.graph.partitions.len(),
1886 });
1887 }
1888 }
1889
1890 fn snapshot_partition_graph_full(&mut self) {
1891 if let Some(frame) = self.local.transactions.last_mut() {
1892 frame.undo_log.push(StateChange::PartitionGraphSnapshot {
1893 previous: self.local.graph.partitions.clone(),
1894 });
1895 }
1896 }
1897
1898 fn snapshot_sequence_graph(&mut self) {
1899 if let Some(frame) = self.local.transactions.last_mut() {
1900 frame.undo_log.push(StateChange::SequenceGraphLengthMarker {
1901 len: self.local.graph.sequences.len(),
1902 });
1903 }
1904 }
1905
1906 fn snapshot_sequence_graph_full(&mut self) {
1907 if let Some(frame) = self.local.transactions.last_mut() {
1908 frame.undo_log.push(StateChange::SequenceGraphSnapshot {
1909 previous: self.local.graph.sequences.clone(),
1910 });
1911 }
1912 }
1913
1914 fn snapshot_column_graph(&mut self) {
1915 if let Some(frame) = self.local.transactions.last_mut() {
1916 frame.undo_log.push(StateChange::ColumnGraphLengthMarker {
1917 len: self.local.graph.column_dependencies.len(),
1918 });
1919 }
1920 }
1921
1922 fn snapshot_column_graph_full(&mut self) {
1923 if let Some(frame) = self.local.transactions.last_mut() {
1924 frame.undo_log.push(StateChange::ColumnGraphSnapshot {
1925 previous: self.local.graph.column_dependencies.clone(),
1926 });
1927 }
1928 }
1929
1930 fn snapshot_rename_graph(&mut self) {
1931 if let Some(frame) = self.local.transactions.last_mut() {
1932 frame.undo_log.push(StateChange::RenameGraphLengthMarker {
1933 len: self.local.graph.renames.len(),
1934 });
1935 }
1936 }
1937
1938 fn snapshot_rename_graph_full(&mut self) {
1939 if let Some(frame) = self.local.transactions.last_mut() {
1940 frame.undo_log.push(StateChange::RenameGraphSnapshot {
1941 previous: self.local.graph.renames.clone(),
1942 });
1943 }
1944 }
1945
1946 fn rollback_frame(&mut self, mut frame: TransactionFrame) {
1947 while let Some(change) = frame.undo_log.pop() {
1948 match change {
1949 StateChange::RelationSnapshot { id, previous } => {
1950 if let Some(prev) = *previous {
1951 self.local.relations.insert(id, prev);
1952 } else {
1953 self.local.relations.remove(&id);
1954 }
1955 }
1956 StateChange::TypeSnapshot { id, previous } => {
1957 if let Some(prev) = previous {
1958 self.local.types.insert(id, prev);
1959 } else {
1960 self.local.types.remove(&id);
1961 }
1962 }
1963 StateChange::SequenceSnapshot { id, previous } => {
1964 if let Some(prev) = previous {
1965 self.local.sequences.insert(id, prev);
1966 } else {
1967 self.local.sequences.remove(&id);
1968 }
1969 }
1970 StateChange::FunctionSnapshot { id, previous } => {
1971 if let Some(prev) = previous {
1972 self.local.functions.insert(id, prev);
1973 } else {
1974 self.local.functions.remove(&id);
1975 }
1976 }
1977 StateChange::PublicationSnapshot { id, previous } => {
1978 if let Some(prev) = previous {
1979 self.local.publications.insert(id.name, prev);
1980 } else {
1981 self.local.publications.remove(&id.name);
1982 }
1983 }
1984 StateChange::SubscriptionSnapshot { id, previous } => {
1985 if let Some(prev) = previous {
1986 self.local.subscriptions.insert(id.name, prev);
1987 } else {
1988 self.local.subscriptions.remove(&id.name);
1989 }
1990 }
1991 StateChange::RoleSnapshot { id, previous } => {
1992 if let Some(prev) = previous {
1993 self.local.roles.insert(id, prev);
1994 } else {
1995 self.local.roles.remove(&id);
1996 }
1997 }
1998 StateChange::TriggerSnapshot { id, previous } => {
1999 if let Some(prev) = previous {
2000 self.local.triggers.insert(id, prev);
2001 } else {
2002 self.local.triggers.remove(&id);
2003 }
2004 }
2005 StateChange::TriggerGraphSnapshot { previous } => {
2006 self.local.graph.trigger_dependencies = previous;
2007 }
2008 StateChange::PublicationGraphSnapshot { previous } => {
2009 self.local.graph.publication_dependencies = previous;
2010 }
2011 StateChange::CurrentRoleSnapshot { previous } => {
2012 self.local.current_role = previous;
2013 }
2014 StateChange::SearchPathSnapshot { previous } => {
2015 self.local.search_path = previous;
2016 }
2017 StateChange::GenerationCounterSnapshot { previous } => {
2018 self.local.generation_counter = previous;
2019 }
2020 StateChange::PendingValidationSnapshot { previous } => {
2021 self.local.pending_validation = previous;
2022 }
2023 StateChange::ConfidenceSnapshot { previous } => {
2024 self.local.confidence = previous;
2025 }
2026 StateChange::FkGraphLengthMarker { len } => {
2027 self.local.graph.foreign_keys.truncate(len);
2028 }
2029 StateChange::FkGraphSnapshot { previous } => {
2030 self.local.graph.foreign_keys = previous;
2031 }
2032 StateChange::ViewGraphLengthMarker { len } => {
2033 self.local.graph.views.truncate(len);
2034 }
2035 StateChange::ViewGraphSnapshot { previous } => {
2036 self.local.graph.views = previous;
2037 }
2038 StateChange::IndexGraphLengthMarker { len } => {
2039 self.local.graph.indexes.truncate(len);
2040 }
2041 StateChange::IndexGraphSnapshot { previous } => {
2042 self.local.graph.indexes = previous;
2043 }
2044 StateChange::PartitionGraphMarker { len } => {
2045 self.local.graph.partitions.truncate(len);
2046 }
2047 StateChange::PartitionGraphSnapshot { previous } => {
2048 self.local.graph.partitions = previous;
2049 }
2050 StateChange::SequenceGraphLengthMarker { len } => {
2051 self.local.graph.sequences.truncate(len);
2052 }
2053 StateChange::SequenceGraphSnapshot { previous } => {
2054 self.local.graph.sequences = previous;
2055 }
2056 StateChange::RenameGraphLengthMarker { len } => {
2057 self.local.graph.renames.truncate(len);
2058 }
2059 StateChange::RenameGraphSnapshot { previous } => {
2060 self.local.graph.renames = previous;
2061 }
2062 StateChange::ColumnGraphLengthMarker { len } => {
2063 self.local.graph.column_dependencies.truncate(len);
2064 }
2065 StateChange::ColumnGraphSnapshot { previous } => {
2066 self.local.graph.column_dependencies = previous;
2067 }
2068 }
2069 }
2070 }
2071}