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 type KeyConstraintsRead<'a> = Box<dyn Deref<Target = Vec<TableKeyConstraint>> + 'a>;
17pub trait ColumnDependencyState {
18 fn columns(&self) -> ColumnsRead<'_>;
19 fn foreign_keys(&self) -> ForeignKeysRead<'_>;
20 fn key_constraints(&self) -> KeyConstraintsRead<'_>;
21}
22pub type ColumnDependencyEntries<'a> = Vec<(String, Box<dyn ColumnDependencyState + 'a>)>;
23pub fn validate_column_dependencies(
24 target: &RelationIdentity,
25 table_name: &str,
26 column: &str,
27 entries: &[(String, Box<dyn ColumnDependencyState + '_>)],
28) -> Result<(), String> {
29 let target_state = entries
30 .iter()
31 .find(|(name, _)| name == table_name)
32 .map(|(_, state)| state)
33 .ok_or_else(|| format!("table `{table_name}` does not exist"))?;
34 for candidate in target_state.columns().iter() {
35 if candidate.name == column {
36 continue;
37 }
38 if candidate
39 .default
40 .as_ref()
41 .is_some_and(|expr| schema_expr_references_column(expr, column))
42 || candidate.generated.as_ref().is_some_and(|generated| {
43 schema_expr_references_column(&generated.expression, column)
44 })
45 {
46 return Err(format!(
47 "ALTER TABLE DROP COLUMN `{table_name}`.`{column}` rejected: column `{}` has a dependent DEFAULT/generation expression",
48 candidate.name
49 ));
50 }
51 }
52
53 let included_keys = target_state
55 .key_constraints()
56 .iter()
57 .filter(|constraint| {
58 constraint
59 .included_columns
60 .iter()
61 .any(|name| name == column)
62 })
63 .map(|constraint| (constraint.kind, constraint.columns.clone()))
64 .collect::<Vec<_>>();
65 let references_included_key = |referenced: Option<&[String]>| {
66 included_keys.iter().any(|(kind, key)| match referenced {
67 Some(referenced) => key.as_slice() == referenced,
68 None => *kind == crate::ast::TableKeyConstraintKind::PrimaryKey,
70 })
71 };
72 let mut inbound = Vec::new();
73 for (candidate_name, table) in entries {
74 for foreign_key in table.foreign_keys().iter() {
75 let local_dependency = candidate_name == table_name
76 && (foreign_key.local_columns.iter().any(|name| name == column)
77 || foreign_key
78 .on_delete_set_columns
79 .iter()
80 .any(|name| name == column));
81 let referenced_dependency =
82 stored_relation_reference_matches(&foreign_key.ref_table, target)
83 && (foreign_key.ref_columns.iter().any(|name| name == column)
84 || references_included_key(
85 (!foreign_key.ref_columns.is_empty())
86 .then_some(foreign_key.ref_columns.as_slice()),
87 ));
88 if referenced_dependency && !local_dependency {
89 inbound.push(candidate_name.clone());
90 }
91 }
92 for candidate in table.columns().iter() {
93 if candidate_name == table_name && candidate.name == column {
94 continue;
95 }
96 if candidate.references.as_ref().is_some_and(|reference| {
97 stored_relation_reference_matches(&reference.table, target)
98 && (reference.column.as_deref() == Some(column)
99 || references_included_key(
100 reference.column.as_ref().map(std::slice::from_ref),
101 ))
102 }) {
103 inbound.push(candidate_name.clone());
104 }
105 }
106 }
107 inbound.sort_unstable();
108 inbound.dedup();
109 if !inbound.is_empty() {
110 return Err(format!(
111 "ALTER TABLE DROP COLUMN `{table_name}`.`{column}` rejected: referenced by foreign key(s) on `{}`",
112 inbound.join("`, `")
113 ));
114 }
115 Ok(())
116}
117pub fn remove_column_declarations(columns: &mut Vec<ColumnDef>, column: &str) {
118 for definition in columns.iter_mut() {
119 if definition
120 .check
121 .as_ref()
122 .is_some_and(|expression| schema_expr_references_column(expression, column))
123 {
124 definition.check = None;
125 definition.check_name = None;
126 definition.check_object_id = None;
127 definition.check_catalog_oid = None;
128 definition.check_is_local = true;
129 definition.check_enforced = true;
130 definition.check_validated = true;
131 definition.check_no_inherit = false;
132 }
133 }
134 columns.retain(|c| c.name != column);
135}
136pub fn remove_column_checks(checks: &mut Vec<TableCheck>, column: &str) {
137 checks.retain(|constraint| !schema_expr_references_column(&constraint.expr, column));
138}
139pub fn remove_column_keys(
141 columns: &mut [ColumnDef],
142 keys: &mut Vec<TableKeyConstraint>,
143 column: &str,
144) {
145 let mut removed = Vec::new();
146 keys.retain(|constraint| {
147 let goes = constraint
148 .columns
149 .iter()
150 .chain(&constraint.included_columns)
151 .any(|name| name == column);
152 if goes {
153 removed.push(constraint.clone());
154 }
155 !goes
156 });
157 for constraint in removed {
158 let [key] = constraint.columns.as_slice() else {
159 continue;
160 };
161 if keys
162 .iter()
163 .any(|kept| kept.kind == constraint.kind && kept.columns.as_slice() == [key.as_str()])
164 {
165 continue;
166 }
167 if let Some(definition) = columns
168 .iter_mut()
169 .find(|definition| definition.name == *key)
170 {
171 match constraint.kind {
172 crate::ast::TableKeyConstraintKind::PrimaryKey => definition.primary_key = false,
173 crate::ast::TableKeyConstraintKind::Unique => definition.unique = false,
174 }
175 }
176 }
177}
178pub fn remove_column_foreign_keys(keys: &mut Vec<ForeignKey>, column: &str) {
179 keys.retain(|foreign_key| {
180 !foreign_key.local_columns.iter().any(|name| name == column)
181 && !foreign_key
182 .on_delete_set_columns
183 .iter()
184 .any(|name| name == column)
185 });
186}