Skip to main content

uqa_sql/schema/sequences/dependencies/
analysis.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Inspect stored sequence dependencies under borrowed catalog identity guards.
8use super::super::{dependents::SequenceSchemaDependent, implicit_ownership::StoredSequenceNames};
9use crate::schema::dependencies::rewrites::rewrite_sequence_function_references;
10use std::{collections::BTreeMap, ops::Deref};
11use uqa_core::RelationIdentity;
12pub type SequenceExpressionObjectIdsRead<'a> =
13    Box<dyn Deref<Target = BTreeMap<RelationIdentity, [u8; 16]>> + 'a>;
14pub trait SequenceExpressionCatalog: StoredSequenceNames {
15    fn object_ids(&self) -> SequenceExpressionObjectIdsRead<'_>;
16}
17fn expression_references_sequence(
18    catalog: &dyn SequenceExpressionCatalog,
19    expression: Option<&crate::ast::Expr>,
20    sequence: &str,
21) -> Result<bool, String> {
22    expression.map_or(Ok(false), |expression| {
23        Ok(stored_sequence_targets_in_loaded_expr(catalog, expression)?.contains(sequence))
24    })
25}
26pub fn append_sequence_schema_expression_dependents(
27    catalog: &dyn SequenceExpressionCatalog,
28    table_name: &str,
29    columns: &[crate::ast::ColumnDef],
30    checks: &[crate::ast::TableCheck],
31    sequence: &str,
32    foreign: bool,
33    dependents: &mut Vec<SequenceSchemaDependent>,
34) -> Result<(), String> {
35    let relation = if foreign {
36        format!("foreign table `{table_name}`")
37    } else {
38        format!("`{table_name}`")
39    };
40    for column in columns {
41        if expression_references_sequence(catalog, column.default.as_ref(), sequence)? {
42            dependents.push(SequenceSchemaDependent::Default {
43                table: table_name.to_string(),
44                column: column.name.clone(),
45                foreign,
46            });
47        }
48        if expression_references_sequence(
49            catalog,
50            column
51                .generated
52                .as_ref()
53                .map(|generated| generated.expression.as_ref()),
54            sequence,
55        )? {
56            dependents.push(SequenceSchemaDependent::GeneratedColumn {
57                table: table_name.to_string(),
58                column: column.name.clone(),
59                foreign,
60            });
61        }
62        if expression_references_sequence(catalog, column.check.as_ref(), sequence)? {
63            dependents.push(SequenceSchemaDependent::CheckConstraint {
64                table: table_name.to_string(),
65                constraint: column.check_name.clone().ok_or_else(|| {
66                    format!(
67                        "CHECK constraint on {relation}.`{}` has no catalog name",
68                        column.name
69                    )
70                })?,
71                foreign,
72            });
73        }
74    }
75    for check in checks {
76        if expression_references_sequence(catalog, Some(&check.expr), sequence)? {
77            dependents.push(SequenceSchemaDependent::CheckConstraint {
78                table: table_name.to_string(),
79                constraint: check.name.clone().ok_or_else(|| {
80                    format!("table CHECK constraint on {relation} has no catalog name")
81                })?,
82                foreign,
83            });
84        }
85    }
86    Ok(())
87}
88pub fn stored_sequence_targets_in_loaded_expr(
89    catalog: &dyn SequenceExpressionCatalog,
90    expression: &crate::ast::Expr,
91) -> Result<std::collections::BTreeSet<String>, String> {
92    let mut expression = expression.clone();
93    let mut targets = std::collections::BTreeSet::new();
94    rewrite_sequence_function_references(&mut expression, &mut |reference| {
95        let canonical = catalog.stored_sequence_name(reference)?;
96        targets.insert(canonical.clone());
97        *reference = canonical;
98        Ok(())
99    })?;
100    let identities = catalog
101        .object_ids()
102        .iter()
103        .map(|(name, id)| {
104            (
105                crate::catalog::oids::stable_object_oid("relation", id),
106                name.qualified_name(),
107            )
108        })
109        .collect::<std::collections::BTreeMap<_, _>>();
110    crate::schema::dependencies::walk_schema_expr_mut(&mut expression, &mut |node| {
111        if let Some(name) = crate::schema::dependencies::regclass::regclass_constant_oid(node)
112            .and_then(|oid| identities.get(&oid))
113        {
114            targets.insert(name.clone());
115        }
116        Ok(())
117    })?;
118    Ok(targets)
119}
120
121#[cfg(test)]
122mod tests;