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