Skip to main content

uqa_sql/schema/foreign_tables/
dependencies.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Foreign-column dependency validation and pure declaration rewrites.
8
9#[cfg(test)]
10mod tests;
11use crate::ast::{ColumnDef, TableCheck};
12use crate::schema::dependencies::schema_expr_references_column;
13
14pub fn clear_foreign_column_default(columns: &mut [ColumnDef], column_name: &str) -> bool {
15    let Some(column) = columns.iter_mut().find(|column| column.name == column_name) else {
16        return false;
17    };
18    column.default_catalog_oid = None;
19    column.default.take().is_some()
20}
21
22pub fn remove_foreign_check(
23    columns: &mut [ColumnDef],
24    checks: &mut Vec<TableCheck>,
25    constraint_name: &str,
26) -> bool {
27    for column in columns {
28        if column.check.is_some() && column.check_name.as_deref() == Some(constraint_name) {
29            column.check = None;
30            column.check_name = None;
31            column.check_object_id = None;
32            column.check_catalog_oid = None;
33            column.check_is_local = true;
34            column.check_enforced = true;
35            column.check_validated = true;
36            column.check_no_inherit = false;
37            return true;
38        }
39    }
40    let Some(index) = checks
41        .iter()
42        .position(|check| check.name.as_deref() == Some(constraint_name))
43    else {
44        return false;
45    };
46    checks.remove(index);
47    true
48}
49
50pub fn validate_foreign_column_removal(
51    columns: &[ColumnDef],
52    table_name: &str,
53    column_name: &str,
54) -> Result<(), String> {
55    for column in columns {
56        if column.name == column_name {
57            continue;
58        }
59        if column
60            .default
61            .as_ref()
62            .is_some_and(|expression| schema_expr_references_column(expression, column_name))
63            || column.generated.as_ref().is_some_and(|generated| {
64                schema_expr_references_column(&generated.expression, column_name)
65            })
66        {
67            return Err(format!(
68                    "cannot drop generated column `{table_name}`.`{column_name}` because column `{}` depends on it",
69                    column.name
70                ));
71        }
72    }
73    Ok(())
74}
75
76pub fn remove_foreign_column(
77    columns: &mut Vec<ColumnDef>,
78    checks: &mut Vec<TableCheck>,
79    column_index: usize,
80    column_name: &str,
81) {
82    for column in columns.iter_mut() {
83        if column.name != column_name
84            && column
85                .check
86                .as_ref()
87                .is_some_and(|expression| schema_expr_references_column(expression, column_name))
88        {
89            column.check = None;
90            column.check_name = None;
91            column.check_object_id = None;
92            column.check_catalog_oid = None;
93            column.check_is_local = true;
94            column.check_enforced = true;
95            column.check_validated = true;
96            column.check_no_inherit = false;
97        }
98    }
99    columns.remove(column_index);
100    checks.retain(|check| !schema_expr_references_column(&check.expr, column_name));
101}
102
103pub use crate::schema::sequences::dependencies::detach_sequence_provenance as detach_foreign_sequence_provenance;