uqa_sql/schema/inheritance/
foreign_keys.rs1use std::collections::{BTreeMap, BTreeSet};
10
11use crate::ast::{ColumnDef, ForeignKey, TableConstraintSet};
12use crate::SQLError;
13
14#[cfg(test)]
15mod tests;
16
17fn local_name(table: &str) -> Result<String, SQLError> {
18 uqa_core::RelationIdentity::from_legacy_name(table)
19 .map(|relation| relation.name)
20 .map_err(SQLError::Internal)
21}
22
23pub fn attachable_foreign_key<'a>(
25 partition: &str,
26 parent_key: &ForeignKey,
27 candidates: &'a [ForeignKey],
28) -> Result<Option<&'a ForeignKey>, SQLError> {
29 let mut ordered = candidates.iter().collect::<Vec<_>>();
30 ordered.sort_by(|left, right| left.name.cmp(&right.name));
31 for candidate in ordered {
32 if candidate.ref_table != parent_key.ref_table
33 || candidate.local_columns != parent_key.local_columns
34 || candidate.ref_columns != parent_key.ref_columns
35 || candidate.period != parent_key.period
36 {
37 continue;
38 }
39 if candidate.enforced != parent_key.enforced {
40 return Err(SQLError::Diagnostic {
41 sqlstate: "42P16".into(),
42 message: format!(
43 "constraint \"{}\" enforceability conflicts with constraint \"{}\" on relation \"{}\"",
44 parent_key.name.as_deref().unwrap_or_default(),
45 candidate.name.as_deref().unwrap_or_default(),
46 local_name(partition)?
47 ),
48 detail: None,
49 hint: None,
50 });
51 }
52 if candidate.deferrable == parent_key.deferrable
53 && candidate.initially_deferred == parent_key.initially_deferred
54 && candidate.on_update == parent_key.on_update
55 && candidate.on_delete == parent_key.on_delete
56 && candidate.match_type == parent_key.match_type
57 {
58 return Ok(Some(candidate));
59 }
60 }
61 Ok(None)
62}
63
64pub fn partition_foreign_key_copy(
66 parent_key: &ForeignKey,
67 used: &BTreeSet<String>,
68 schema: &mut BTreeSet<String>,
69) -> Result<ForeignKey, SQLError> {
70 let mut copy = parent_key.clone();
71 copy.catalog_identity = None;
72 copy.referenced_partitions.clear();
74 if let Some(name) = copy.name.as_ref().filter(|name| used.contains(*name)) {
75 copy.name = Some(
76 crate::schema::constraint_metadata::choose_suffixed_constraint_name(name, schema)
77 .map_err(|error| SQLError::Internal(error.to_string()))?,
78 );
79 }
80 Ok(copy)
81}
82
83pub fn declared_foreign_key_families(
85 columns: &[ColumnDef],
86 constraints: &TableConstraintSet,
87) -> BTreeSet<[u8; 16]> {
88 columns
89 .iter()
90 .filter_map(|column| column.references.as_ref()?.object_id)
91 .chain(
92 constraints
93 .foreign_keys
94 .iter()
95 .filter_map(|foreign_key| foreign_key.object_id),
96 )
97 .collect()
98}
99
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
102pub enum DeclaredForeignKey {
103 Column(usize),
104 Table(usize),
105}
106
107impl DeclaredForeignKey {
108 pub fn by_catalog_identity(
110 columns: &[ColumnDef],
111 constraints: &TableConstraintSet,
112 identity: crate::ast::ConstraintCatalogIdentity,
113 ) -> Option<Self> {
114 columns
115 .iter()
116 .position(|column| {
117 column
118 .references
119 .as_ref()
120 .is_some_and(|reference| reference.catalog_identity == Some(identity))
121 })
122 .map(Self::Column)
123 .or_else(|| {
124 constraints
125 .foreign_keys
126 .iter()
127 .position(|foreign_key| foreign_key.catalog_identity == Some(identity))
128 .map(Self::Table)
129 })
130 }
131
132 pub fn by_family(
134 columns: &[ColumnDef],
135 constraints: &TableConstraintSet,
136 object_id: [u8; 16],
137 ) -> Option<Self> {
138 columns
139 .iter()
140 .position(|column| {
141 column
142 .references
143 .as_ref()
144 .is_some_and(|reference| reference.object_id == Some(object_id))
145 })
146 .map(Self::Column)
147 .or_else(|| {
148 constraints
149 .foreign_keys
150 .iter()
151 .position(|foreign_key| foreign_key.object_id == Some(object_id))
152 .map(Self::Table)
153 })
154 }
155
156 pub fn foreign_key(
158 self,
159 columns: &[ColumnDef],
160 constraints: &TableConstraintSet,
161 ) -> Option<ForeignKey> {
162 match self {
163 Self::Column(index) => {
164 let column = columns.get(index)?;
165 column.references.as_ref().map(|reference| {
166 crate::schema::foreign_keys::column_foreign_key(column, reference)
167 })
168 }
169 Self::Table(index) => constraints.foreign_keys.get(index).cloned(),
170 }
171 }
172
173 pub fn validated(self, columns: &[ColumnDef], constraints: &TableConstraintSet) -> bool {
174 match self {
175 Self::Column(index) => columns[index]
176 .references
177 .as_ref()
178 .is_some_and(|reference| reference.validated),
179 Self::Table(index) => constraints.foreign_keys[index].validated,
180 }
181 }
182
183 pub fn set_validated(
184 self,
185 columns: &mut [ColumnDef],
186 constraints: &mut TableConstraintSet,
187 validated: bool,
188 ) {
189 match self {
190 Self::Column(index) => {
191 if let Some(reference) = columns[index].references.as_mut() {
192 reference.validated = validated;
193 }
194 }
195 Self::Table(index) => constraints.foreign_keys[index].validated = validated,
196 }
197 }
198
199 pub fn alter(
201 self,
202 columns: &mut [ColumnDef],
203 constraints: &mut TableConstraintSet,
204 enforceability: Option<bool>,
205 deferrability: Option<(bool, bool)>,
206 ) -> bool {
207 let (enforced, validated, deferrable, initially_deferred) = match self {
208 Self::Column(index) => {
209 let Some(reference) = columns[index].references.as_mut() else {
210 return false;
211 };
212 (
213 &mut reference.enforced,
214 &mut reference.validated,
215 &mut reference.deferrable,
216 &mut reference.initially_deferred,
217 )
218 }
219 Self::Table(index) => {
220 let foreign_key = &mut constraints.foreign_keys[index];
221 (
222 &mut foreign_key.enforced,
223 &mut foreign_key.validated,
224 &mut foreign_key.deferrable,
225 &mut foreign_key.initially_deferred,
226 )
227 }
228 };
229 let mut became_enforced = false;
230 match enforceability {
231 Some(false) => {
232 *enforced = false;
233 *validated = false;
234 }
235 Some(true) if !*enforced => {
236 *enforced = true;
237 *validated = false;
238 became_enforced = true;
239 }
240 Some(true) | None => {}
241 }
242 if let Some((deferrable_value, initially_deferred_value)) = deferrability {
243 *deferrable = deferrable_value;
244 *initially_deferred = initially_deferred_value;
245 }
246 became_enforced
247 }
248
249 pub fn set_family(
250 self,
251 columns: &mut [ColumnDef],
252 constraints: &mut TableConstraintSet,
253 object_id: [u8; 16],
254 ) {
255 match self {
256 Self::Column(index) => {
257 if let Some(reference) = columns[index].references.as_mut() {
258 reference.object_id = Some(object_id);
259 }
260 }
261 Self::Table(index) => constraints.foreign_keys[index].object_id = Some(object_id),
262 }
263 }
264}
265
266pub fn rejoin_foreign_key_families(
268 columns: &mut [ColumnDef],
269 constraints: &mut TableConstraintSet,
270 joined: &BTreeMap<[u8; 16], [u8; 16]>,
271) -> bool {
272 let mut changed = false;
273 for object_id in columns
274 .iter_mut()
275 .filter_map(|column| column.references.as_mut())
276 .map(|reference| &mut reference.object_id)
277 .chain(
278 constraints
279 .foreign_keys
280 .iter_mut()
281 .map(|foreign_key| &mut foreign_key.object_id),
282 )
283 {
284 if let Some(target) = object_id.and_then(|current| joined.get(¤t)) {
285 *object_id = Some(*target);
286 changed = true;
287 }
288 }
289 changed
290}