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.take().is_some()
19}
20
21pub fn remove_foreign_check(
22    columns: &mut [ColumnDef],
23    checks: &mut Vec<TableCheck>,
24    constraint_name: &str,
25) -> bool {
26    for column in columns {
27        if column.check.is_some() && column.check_name.as_deref() == Some(constraint_name) {
28            column.check = None;
29            column.check_name = None;
30            column.check_object_id = None;
31            column.check_catalog_oid = None;
32            column.check_is_local = true;
33            column.check_enforced = true;
34            column.check_validated = true;
35            column.check_no_inherit = false;
36            return true;
37        }
38    }
39    let Some(index) = checks
40        .iter()
41        .position(|check| check.name.as_deref() == Some(constraint_name))
42    else {
43        return false;
44    };
45    checks.remove(index);
46    true
47}
48
49pub fn validate_foreign_column_removal(
50    columns: &[ColumnDef],
51    table_name: &str,
52    column_name: &str,
53) -> Result<(), String> {
54    for column in columns {
55        if column.name == column_name {
56            continue;
57        }
58        if column
59            .default
60            .as_ref()
61            .is_some_and(|expression| schema_expr_references_column(expression, column_name))
62            || column.generated.as_ref().is_some_and(|generated| {
63                schema_expr_references_column(&generated.expression, column_name)
64            })
65        {
66            return Err(format!(
67                    "cannot drop generated column `{table_name}`.`{column_name}` because column `{}` depends on it",
68                    column.name
69                ));
70        }
71    }
72    Ok(())
73}
74
75pub fn remove_foreign_column(
76    columns: &mut Vec<ColumnDef>,
77    checks: &mut Vec<TableCheck>,
78    column_index: usize,
79    column_name: &str,
80) {
81    for column in columns.iter_mut() {
82        if column.name != column_name
83            && column
84                .check
85                .as_ref()
86                .is_some_and(|expression| schema_expr_references_column(expression, column_name))
87        {
88            column.check = None;
89            column.check_name = None;
90            column.check_object_id = None;
91            column.check_catalog_oid = None;
92            column.check_is_local = true;
93            column.check_enforced = true;
94            column.check_validated = true;
95            column.check_no_inherit = false;
96        }
97    }
98    columns.remove(column_index);
99    checks.retain(|check| !schema_expr_references_column(&check.expr, column_name));
100}
101
102pub use crate::schema::sequences::dependencies::detach_sequence_provenance as detach_foreign_sequence_provenance;