uqa_sql/schema/columns/
removal_metadata.rs1use crate::ast::{ColumnDef, ForeignKey, TableCheck, TableKeyConstraint};
9use crate::schema::dependencies::{
10 rewrites::stored_relation_reference_matches, schema_expr_references_column,
11};
12use std::ops::Deref;
13use uqa_core::RelationIdentity;
14pub type ColumnsRead<'a> = Box<dyn Deref<Target = Vec<ColumnDef>> + 'a>;
15pub type ForeignKeysRead<'a> = Box<dyn Deref<Target = Vec<ForeignKey>> + 'a>;
16pub trait ColumnDependencyState {
17 fn columns(&self) -> ColumnsRead<'_>;
18 fn foreign_keys(&self) -> ForeignKeysRead<'_>;
19}
20pub type ColumnDependencyEntries<'a> = Vec<(String, Box<dyn ColumnDependencyState + 'a>)>;
21pub fn validate_column_dependencies(
22 target: &RelationIdentity,
23 table_name: &str,
24 column: &str,
25 entries: &[(String, Box<dyn ColumnDependencyState + '_>)],
26) -> Result<(), String> {
27 let target_state = entries
28 .iter()
29 .find(|(name, _)| name == table_name)
30 .map(|(_, state)| state)
31 .ok_or_else(|| format!("table `{table_name}` does not exist"))?;
32 for candidate in target_state.columns().iter() {
33 if candidate.name == column {
34 continue;
35 }
36 if candidate
37 .default
38 .as_ref()
39 .is_some_and(|expr| schema_expr_references_column(expr, column))
40 || candidate.generated.as_ref().is_some_and(|generated| {
41 schema_expr_references_column(&generated.expression, column)
42 })
43 {
44 return Err(format!(
45 "ALTER TABLE DROP COLUMN `{table_name}`.`{column}` rejected: column `{}` has a dependent DEFAULT/generation expression",
46 candidate.name
47 ));
48 }
49 }
50
51 let mut inbound = Vec::new();
52 for (candidate_name, table) in entries {
53 for foreign_key in table.foreign_keys().iter() {
54 let local_dependency = candidate_name == table_name
55 && (foreign_key.local_columns.iter().any(|name| name == column)
56 || foreign_key
57 .on_delete_set_columns
58 .iter()
59 .any(|name| name == column));
60 let referenced_dependency =
61 stored_relation_reference_matches(&foreign_key.ref_table, target)
62 && foreign_key.ref_columns.iter().any(|name| name == column);
63 if referenced_dependency && !local_dependency {
64 inbound.push(candidate_name.clone());
65 }
66 }
67 for candidate in table.columns().iter() {
68 if candidate_name == table_name && candidate.name == column {
69 continue;
70 }
71 if candidate.references.as_ref().is_some_and(|reference| {
72 stored_relation_reference_matches(&reference.table, target)
73 && reference.column.as_deref() == Some(column)
74 }) {
75 inbound.push(candidate_name.clone());
76 }
77 }
78 }
79 inbound.sort_unstable();
80 inbound.dedup();
81 if !inbound.is_empty() {
82 return Err(format!(
83 "ALTER TABLE DROP COLUMN `{table_name}`.`{column}` rejected: referenced by foreign key(s) on `{}`",
84 inbound.join("`, `")
85 ));
86 }
87 Ok(())
88}
89pub fn remove_column_declarations(columns: &mut Vec<ColumnDef>, column: &str) {
90 for definition in columns.iter_mut() {
91 if definition
92 .check
93 .as_ref()
94 .is_some_and(|expression| schema_expr_references_column(expression, column))
95 {
96 definition.check = None;
97 definition.check_name = None;
98 definition.check_object_id = None;
99 definition.check_is_local = true;
100 definition.check_enforced = true;
101 definition.check_validated = true;
102 definition.check_no_inherit = false;
103 }
104 }
105 columns.retain(|c| c.name != column);
106}
107pub fn remove_column_checks(checks: &mut Vec<TableCheck>, column: &str) {
108 checks.retain(|constraint| !schema_expr_references_column(&constraint.expr, column));
109}
110pub fn remove_column_keys(keys: &mut Vec<TableKeyConstraint>, column: &str) {
111 keys.retain(|constraint| !constraint.columns.iter().any(|name| name == column));
112}
113pub fn remove_column_foreign_keys(keys: &mut Vec<ForeignKey>, column: &str) {
114 keys.retain(|foreign_key| {
115 !foreign_key.local_columns.iter().any(|name| name == column)
116 && !foreign_key
117 .on_delete_set_columns
118 .iter()
119 .any(|name| name == column)
120 });
121}