1use crate::ast::identifiers::ObjectId;
2use std::cell::OnceCell;
3use std::collections::{HashMap, HashSet};
4
5#[derive(Debug, Clone, PartialEq)]
6pub enum DependencyKind {
7 ForeignKey {
8 constraint_name: Option<String>,
9 from_columns: Vec<String>,
10 to_columns: Vec<String>,
11 from_generation: u64,
12 },
13 ViewDependency {
14 view_generation: u64,
15 },
16 IndexOnRelation {
17 using_method: Option<String>,
18 has_predicate: bool,
19 is_concurrent: bool,
20 is_unique: bool,
21 eligibility_known: bool,
22 },
23 ConstraintOnRelation {
28 constraint_name: String,
29 columns: Vec<String>,
30 is_primary: bool,
31 },
32 RenameTo,
33 PartitionOf,
34 SequenceOwnedBy {
35 column: String,
36 },
37 ColumnGeneratedFrom {
38 column: String,
39 depends_on_column: String,
40 },
41 TriggerOnTable {
42 trigger_id: ObjectId,
43 function_id: ObjectId,
44 },
45 PublicationIncludes {
46 publication_name: String,
47 },
48}
49
50#[derive(Debug, Clone, PartialEq)]
51pub struct DependencyEdge {
52 pub dependent: ObjectId,
53 pub referenced: ObjectId,
54 pub kind: DependencyKind,
55}
56
57impl DependencyEdge {
58 pub fn new(dependent: ObjectId, referenced: ObjectId, kind: DependencyKind) -> Self {
59 Self {
60 dependent,
61 referenced,
62 kind,
63 }
64 }
65}
66
67#[derive(Debug, Default)]
68pub struct DependencyGraph {
69 edges: Vec<DependencyEdge>,
70 indexes: OnceCell<GraphIndexes>,
71}
72
73#[derive(Debug, Clone, Default, PartialEq, Eq)]
74struct GraphIndexes {
75 rename_by_source: HashMap<ObjectId, usize>,
76 by_resolved_referenced: HashMap<ObjectId, Vec<usize>>,
77}
78
79impl Clone for DependencyGraph {
80 fn clone(&self) -> Self {
81 Self {
82 edges: self.edges.clone(),
83 indexes: OnceCell::new(),
86 }
87 }
88}
89
90impl DependencyGraph {
91 const CASCADE_INDEX_MIN_EDGES: usize = 1_024;
92
93 pub fn new() -> Self {
94 Self::default()
95 }
96
97 pub fn edges(&self) -> &[DependencyEdge] {
98 &self.edges
99 }
100
101 pub fn add_edge(&mut self, edge: DependencyEdge) {
102 self.edges.push(edge);
103 self.invalidate_indexes();
104 }
105
106 pub(crate) fn retain_edges(&mut self, mut keep: impl FnMut(&DependencyEdge) -> bool) {
107 self.edges.retain(|edge| keep(edge));
108 self.invalidate_indexes();
109 }
110
111 pub(crate) fn edge_count(&self) -> usize {
112 self.edges.len()
113 }
114
115 pub(crate) fn truncate(&mut self, len: usize) {
116 self.edges.truncate(len);
117 self.invalidate_indexes();
118 }
119
120 pub(crate) fn replace_edges(&mut self, edges: Vec<DependencyEdge>) {
121 self.edges = edges;
122 self.invalidate_indexes();
123 }
124
125 pub(crate) fn mutate_edges(&mut self, mutate: impl FnOnce(&mut [DependencyEdge])) {
126 mutate(&mut self.edges);
127 self.invalidate_indexes();
128 }
129
130 pub fn indexes_are_valid(&self) -> bool {
133 self.indexes() == &Self::build_indexes(&self.edges)
134 }
135
136 fn invalidate_indexes(&mut self) {
137 self.indexes.take();
138 }
139
140 fn indexes(&self) -> &GraphIndexes {
141 self.indexes
142 .get_or_init(|| Self::build_indexes(&self.edges))
143 }
144
145 fn build_indexes(edges: &[DependencyEdge]) -> GraphIndexes {
146 let mut indexes = GraphIndexes::default();
147 for (index, edge) in edges.iter().enumerate() {
148 if matches!(edge.kind, DependencyKind::RenameTo) {
149 indexes
150 .rename_by_source
151 .entry(edge.dependent.clone())
152 .or_insert(index);
153 }
154 }
155
156 for (index, edge) in edges.iter().enumerate() {
157 let referenced = Self::resolve_rename_with(edges, &indexes, &edge.referenced).clone();
158 indexes
159 .by_resolved_referenced
160 .entry(referenced)
161 .or_default()
162 .push(index);
163 }
164 indexes
165 }
166
167 fn resolve_rename_with<'a>(
168 edges: &'a [DependencyEdge],
169 indexes: &GraphIndexes,
170 id: &'a ObjectId,
171 ) -> &'a ObjectId {
172 let mut current = id;
173 let mut visited = HashSet::new();
174 loop {
175 if !visited.insert(current.clone()) {
176 return id;
177 }
178 match indexes.rename_by_source.get(current) {
179 Some(index) => current = &edges[*index].referenced,
180 None => return current,
181 }
182 }
183 }
184
185 fn resolved_referenced_edges(&self, id: &ObjectId) -> impl Iterator<Item = &DependencyEdge> {
186 let target = self.resolve_rename(id);
187 self.indexes()
188 .by_resolved_referenced
189 .get(target)
190 .into_iter()
191 .flatten()
192 .map(|index| &self.edges[*index])
193 }
194
195 pub fn cascade_edges(&self, id: &ObjectId) -> Vec<&DependencyEdge> {
196 if self.edges.len() < Self::CASCADE_INDEX_MIN_EDGES {
197 let target = self.resolve_rename(id);
198 return self
199 .edges
200 .iter()
201 .filter(|edge| {
202 matches!(
203 edge.kind,
204 DependencyKind::ViewDependency { .. }
205 | DependencyKind::IndexOnRelation { .. }
206 | DependencyKind::ForeignKey { .. }
207 | DependencyKind::PartitionOf
208 ) && self.resolve_rename(&edge.referenced) == target
209 })
210 .collect();
211 }
212 self.resolved_referenced_edges(id)
213 .filter(|edge| {
214 matches!(
215 edge.kind,
216 DependencyKind::ViewDependency { .. }
217 | DependencyKind::IndexOnRelation { .. }
218 | DependencyKind::ForeignKey { .. }
219 | DependencyKind::PartitionOf
220 )
221 })
222 .collect()
223 }
224
225 pub(crate) fn cascade_index_is_worthwhile(&self) -> bool {
226 self.edges.len() >= Self::CASCADE_INDEX_MIN_EDGES
227 }
228
229 pub fn is_referenced_by_view(&self, id: &ObjectId) -> Vec<&ObjectId> {
231 let target = self.resolve_rename(id);
232 self.edges
233 .iter()
234 .filter(|e| {
235 matches!(e.kind, DependencyKind::ViewDependency { .. })
236 && (self.resolve_rename(&e.referenced) == target || &e.referenced == id)
237 })
238 .map(|e| self.resolve_rename(&e.dependent))
239 .collect()
240 }
241
242 pub fn is_referenced_by_fk(&self, id: &ObjectId) -> Vec<(&ObjectId, u64)> {
243 let target = self.resolve_rename(id);
244 self.edges
245 .iter()
246 .filter_map(|e| {
247 if let DependencyKind::ForeignKey {
248 from_generation, ..
249 } = &e.kind
250 && (self.resolve_rename(&e.referenced) == target || &e.referenced == id)
251 {
252 Some((self.resolve_rename(&e.dependent), *from_generation))
253 } else {
254 None
255 }
256 })
257 .collect()
258 }
259
260 pub fn is_referenced_by_index(&self, id: &ObjectId) -> Vec<&ObjectId> {
261 let target = self.resolve_rename(id);
262 self.edges
263 .iter()
264 .filter(|e| {
265 matches!(e.kind, DependencyKind::IndexOnRelation { .. })
266 && (self.resolve_rename(&e.referenced) == target || &e.referenced == id)
267 })
268 .map(|e| self.resolve_rename(&e.dependent))
269 .collect()
270 }
271
272 pub fn partitions_of(&self, id: &ObjectId) -> Vec<&ObjectId> {
273 let target = self.resolve_rename(id);
274 self.edges
275 .iter()
276 .filter(|e| {
277 matches!(e.kind, DependencyKind::PartitionOf)
278 && (self.resolve_rename(&e.referenced) == target || &e.referenced == id)
279 })
280 .map(|e| self.resolve_rename(&e.dependent))
281 .collect()
282 }
283
284 pub fn resolve_rename<'a>(&'a self, id: &'a ObjectId) -> &'a ObjectId {
285 let mut current = id;
286 let mut visited = HashSet::new();
287 loop {
288 if !visited.insert(current.clone()) {
293 return id;
294 }
295 match self.edges.iter().find(|edge| {
296 matches!(edge.kind, DependencyKind::RenameTo) && &edge.dependent == current
297 }) {
298 Some(edge) => current = &edge.referenced,
299 None => return current,
300 }
301 }
302 }
303
304 pub fn check_partition_cycle(&self, parent: &ObjectId, child: &ObjectId) -> bool {
306 let resolved_parent = self.resolve_rename(parent);
307 let resolved_child = self.resolve_rename(child);
308 if resolved_parent == resolved_child {
309 return true;
310 }
311
312 let mut current_parent = resolved_parent;
313 let mut visited = HashSet::new();
314 loop {
315 if !visited.insert(current_parent.clone()) {
316 return true;
319 }
320 let maybe_edge = self.edges.iter().find(|edge| {
321 matches!(edge.kind, DependencyKind::PartitionOf)
322 && self.resolve_rename(&edge.dependent) == current_parent
323 });
324 if let Some(edge) = maybe_edge {
325 let p = self.resolve_rename(&edge.referenced);
326 if p == resolved_child {
327 return true;
328 }
329 current_parent = p;
330 } else {
331 break;
332 }
333 }
334 false
335 }
336
337 pub fn propagate_relation_rename(&mut self, old_id: &ObjectId, new_id: &ObjectId) {
345 for edge in &mut self.edges {
346 match &mut edge.kind {
347 DependencyKind::RenameTo => {}
348 DependencyKind::ForeignKey { .. }
349 | DependencyKind::ViewDependency { .. }
350 | DependencyKind::PartitionOf
351 | DependencyKind::ColumnGeneratedFrom { .. } => {
352 if edge.dependent == *old_id {
353 edge.dependent = new_id.clone();
354 }
355 if edge.referenced == *old_id {
356 edge.referenced = new_id.clone();
357 }
358 }
359 DependencyKind::IndexOnRelation { .. }
360 | DependencyKind::SequenceOwnedBy { .. }
361 | DependencyKind::TriggerOnTable { .. } => {
362 if edge.referenced == *old_id {
363 edge.referenced = new_id.clone();
364 }
365 }
366 DependencyKind::ConstraintOnRelation { .. } => {
367 if edge.dependent == *old_id {
368 edge.dependent = new_id.clone();
369 }
370 if edge.referenced == *old_id {
371 edge.referenced = new_id.clone();
372 }
373 }
374 DependencyKind::PublicationIncludes { .. } => {
375 if edge.dependent == *old_id {
376 edge.dependent = new_id.clone();
377 }
378 }
379 }
380 }
381 self.invalidate_indexes();
382 }
383
384 pub fn propagate_index_rename(&mut self, old_id: &ObjectId, new_id: &ObjectId) {
387 for edge in &mut self.edges {
388 if matches!(edge.kind, DependencyKind::IndexOnRelation { .. })
389 && edge.dependent == *old_id
390 {
391 edge.dependent = new_id.clone();
392 }
393 }
394 self.invalidate_indexes();
395 }
396
397 pub fn propagate_sequence_rename(&mut self, old_id: &ObjectId, new_id: &ObjectId) {
399 for edge in &mut self.edges {
400 if matches!(edge.kind, DependencyKind::SequenceOwnedBy { .. })
401 && edge.dependent == *old_id
402 {
403 edge.dependent = new_id.clone();
404 }
405 }
406 self.invalidate_indexes();
407 }
408
409 pub fn propagate_trigger_rename(&mut self, old_id: &ObjectId, new_id: &ObjectId) {
411 for edge in &mut self.edges {
412 if let DependencyKind::TriggerOnTable { trigger_id, .. } = &mut edge.kind
413 && *trigger_id == *old_id
414 {
415 *trigger_id = new_id.clone();
416 if edge.dependent == *old_id {
417 edge.dependent = new_id.clone();
418 }
419 }
420 }
421 self.invalidate_indexes();
422 }
423
424 pub fn propagate_function_rename(&mut self, old_id: &ObjectId, new_id: &ObjectId) {
426 for edge in &mut self.edges {
427 if let DependencyKind::TriggerOnTable { function_id, .. } = &mut edge.kind
428 && *function_id == *old_id
429 {
430 *function_id = new_id.clone();
431 }
432 }
433 self.invalidate_indexes();
434 }
435
436 pub fn propagate_rename(&mut self, old_id: &ObjectId, new_id: &ObjectId) {
442 self.propagate_relation_rename(old_id, new_id);
443 }
444
445 pub fn triggers_on(&self, table_id: &ObjectId) -> Vec<&DependencyEdge> {
446 self.edges
447 .iter()
448 .filter(|e| {
449 matches!(e.kind, DependencyKind::TriggerOnTable { .. }) && &e.referenced == table_id
450 })
451 .collect()
452 }
453
454 pub fn triggers_for_function(&self, function_id: &ObjectId) -> Vec<&DependencyEdge> {
455 let normalize = |id: &ObjectId| -> ObjectId {
456 let name = if let Some(idx) = id.name.find('(') {
457 format!("{}()", &id.name[..idx])
458 } else {
459 id.name.clone()
460 };
461 ObjectId {
462 schema: id.schema.clone(),
463 name,
464 inferred_schema: id.inferred_schema,
465 }
466 };
467 let target_id = normalize(function_id);
468 self.edges
469 .iter()
470 .filter(|e| {
471 if let DependencyKind::TriggerOnTable {
472 function_id: fid, ..
473 } = &e.kind
474 {
475 normalize(fid) == target_id
476 } else {
477 false
478 }
479 })
480 .collect()
481 }
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487
488 fn id(name: &str) -> ObjectId {
489 ObjectId::new("public", name)
490 }
491
492 fn view_edge(dependent: &str, referenced: &str) -> DependencyEdge {
493 DependencyEdge::new(
494 id(dependent),
495 id(referenced),
496 DependencyKind::ViewDependency { view_generation: 1 },
497 )
498 }
499
500 fn canonical_views<'a>(graph: &'a DependencyGraph, target: &ObjectId) -> Vec<&'a ObjectId> {
501 let resolved_target = graph.resolve_rename(target);
502 graph
503 .edges()
504 .iter()
505 .filter(|edge| {
506 matches!(edge.kind, DependencyKind::ViewDependency { .. })
507 && (graph.resolve_rename(&edge.referenced) == resolved_target
508 || &edge.referenced == target)
509 })
510 .map(|edge| graph.resolve_rename(&edge.dependent))
511 .collect()
512 }
513
514 fn assert_indexed_views_match_scan(graph: &DependencyGraph, targets: &[ObjectId]) {
515 assert!(graph.indexes_are_valid());
516 for target in targets {
517 let indexed = graph
518 .cascade_edges(target)
519 .into_iter()
520 .filter(|edge| matches!(edge.kind, DependencyKind::ViewDependency { .. }))
521 .map(|edge| graph.resolve_rename(&edge.dependent))
522 .collect::<Vec<_>>();
523 assert_eq!(indexed, canonical_views(graph, target));
524 }
525 }
526
527 #[test]
528 fn indexes_track_every_graph_mutation_and_alias_cycle() {
529 let a = id("a");
530 let b = id("b");
531 let c = id("c");
532 let d = id("d");
533 let targets = [a.clone(), b.clone(), c.clone(), d.clone()];
534 let mut graph = DependencyGraph::new();
535
536 graph.add_edge(view_edge("view_a", "a"));
537 graph.add_edge(view_edge("view_b", "b"));
538 for index in 0..DependencyGraph::CASCADE_INDEX_MIN_EDGES {
539 graph.add_edge(view_edge(
540 &format!("unrelated_view_{index}"),
541 &format!("unrelated_table_{index}"),
542 ));
543 }
544 assert_indexed_views_match_scan(&graph, &targets);
545
546 graph.add_edge(DependencyEdge::new(
547 a.clone(),
548 b.clone(),
549 DependencyKind::RenameTo,
550 ));
551 assert_indexed_views_match_scan(&graph, &targets);
552
553 graph.propagate_rename(&b, &c);
554 graph.add_edge(DependencyEdge::new(
555 b.clone(),
556 c.clone(),
557 DependencyKind::RenameTo,
558 ));
559 assert_indexed_views_match_scan(&graph, &targets);
560
561 graph.mutate_edges(|edges| {
562 for edge in edges {
563 if edge.dependent == id("view_b") {
564 edge.dependent = id("view_c");
565 }
566 }
567 });
568 assert_indexed_views_match_scan(&graph, &targets);
569
570 let snapshot = graph.edges().to_vec();
571 graph.retain_edges(|edge| edge.dependent != id("view_a"));
572 assert_indexed_views_match_scan(&graph, &targets);
573 graph.replace_edges(snapshot);
574 assert_indexed_views_match_scan(&graph, &targets);
575
576 let checkpoint = graph.edge_count();
577 graph.add_edge(view_edge("temporary", "c"));
578 graph.truncate(checkpoint);
579 assert_indexed_views_match_scan(&graph, &targets);
580
581 graph.add_edge(DependencyEdge::new(
582 c.clone(),
583 a.clone(),
584 DependencyKind::RenameTo,
585 ));
586 assert_eq!(graph.resolve_rename(&a), &a);
587 assert_eq!(graph.resolve_rename(&b), &b);
588 assert_eq!(graph.resolve_rename(&c), &c);
589 assert_indexed_views_match_scan(&graph, &targets);
590 }
591}