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