uqa_sql/schema/table_creation/
foreign_keys.rs1use super::declaration::CreateTableAnalysisContext;
10use crate::ast::{CreateTable, DeclaredForeignKey, ForeignKey};
11use crate::schema::constraint_changes::ForeignKeyLocation;
12use crate::schema::constraint_metadata::{
13 assign_constraint_name, materialize_foreign_key_identity, CatalogIdentityAllocator,
14 ConstraintMetadataError,
15};
16use crate::schema::foreign_keys::{
17 column_foreign_key, validate_bound_foreign_key_definition_with_local_state,
18};
19use crate::SQLError;
20use std::collections::BTreeSet;
21
22pub fn define_foreign_keys(
23 context: &CreateTableAnalysisContext<'_>,
24 table: &mut CreateTable,
25 inherited: usize,
26 allocate: &mut CatalogIdentityAllocator<'_>,
27) -> Result<(), SQLError> {
28 let relation =
29 uqa_core::RelationIdentity::from_legacy_name(&table.name).map_err(SQLError::Internal)?;
30 let local_columns = table.columns.clone();
31 let mut held = held_names(table, inherited);
32 let mut used = context
33 .index_names
34 .automatic_constraint_names(&table.name)?;
35 used.extend(held.iter().cloned());
36 for foreign_key in &mut table.foreign_keys[..inherited] {
38 validate_bound_foreign_key_definition_with_local_state(
39 &context.foreign_keys,
40 &table.name,
41 Some(&local_columns),
42 Some(&table.key_constraints),
43 foreign_key,
44 )?;
45 }
46 let mut order = std::mem::take(&mut table.foreign_key_order);
47 if order.is_empty() {
49 order.extend(
50 table
51 .columns
52 .iter()
53 .filter(|column| column.references.is_some())
54 .map(|column| DeclaredForeignKey::Column(column.name.clone())),
55 );
56 order.extend((0..table.foreign_keys.len() - inherited).map(DeclaredForeignKey::Table));
57 }
58 for declared in order {
59 let (mut foreign_key, location) = declaration(table, declared, inherited)?;
60 if let Some(name) = &foreign_key.name {
61 if !held.insert(name.clone()) {
62 return Err(crate::schema::check_inheritance::duplicate_check(
63 &relation.name,
64 name,
65 ));
66 }
67 used.insert(name.clone());
68 }
69 assign_constraint_name(
70 &mut foreign_key.name,
71 (&relation.name, &foreign_key.local_columns.join("_"), "fkey"),
72 &mut used,
73 )
74 .map_err(ConstraintMetadataError::into_sql_error)?;
75 held.extend(foreign_key.name.iter().cloned());
76 bind_reference(context, &table.name, &table.qualifier, &mut foreign_key)?;
77 validate_bound_foreign_key_definition_with_local_state(
78 &context.foreign_keys,
79 &table.name,
80 Some(&local_columns),
81 Some(&table.key_constraints),
82 &mut foreign_key,
83 )?;
84 materialize_foreign_key_identity(
85 &mut foreign_key.object_id,
86 &mut foreign_key.catalog_identity,
87 allocate,
88 )
89 .map_err(ConstraintMetadataError::into_sql_error)?;
90 store_definition(table, location, foreign_key)?;
91 }
92 Ok(())
93}
94
95fn bind_reference(
96 context: &CreateTableAnalysisContext<'_>,
97 table: &str,
98 qualifier: &str,
99 key: &mut ForeignKey,
100) -> Result<(), SQLError> {
101 let reference = &mut key.ref_table;
102 let self_reference = reference == table
103 || reference == qualifier
104 || table
105 .rsplit_once('.')
106 .is_some_and(|(_, local)| local == reference);
107 if self_reference {
108 table.clone_into(reference);
109 } else {
110 *reference = context
111 .foreign_keys
112 .catalog
113 .resolve_table_reference(reference)?;
114 }
115 Ok(())
116}
117
118fn lost_foreign_key(name: &str) -> SQLError {
119 SQLError::Internal(format!("declared foreign key `{name}` disappeared"))
120}
121
122fn held_names(table: &CreateTable, inherited: usize) -> BTreeSet<String> {
123 table
124 .columns
125 .iter()
126 .flat_map(|column| {
127 [
128 column.not_null_name.clone().filter(|_| column.not_null),
129 column.check_name.clone().filter(|_| column.check.is_some()),
130 ]
131 })
132 .chain(table.checks.iter().map(|check| check.name.clone()))
133 .chain(table.key_constraints.iter().map(|key| key.name.clone()))
134 .chain(
135 table.foreign_keys[..inherited]
136 .iter()
137 .map(|key| key.name.clone()),
138 )
139 .flatten()
140 .collect()
141}
142
143fn declaration(
144 table: &CreateTable,
145 declared: DeclaredForeignKey,
146 inherited: usize,
147) -> Result<(ForeignKey, ForeignKeyLocation), SQLError> {
148 Ok(match declared {
149 DeclaredForeignKey::Column(name) => {
150 let position = table
151 .columns
152 .iter()
153 .position(|column| column.name == name)
154 .ok_or_else(|| lost_foreign_key(&name))?;
155 let column = &table.columns[position];
156 let reference = column
157 .references
158 .as_ref()
159 .ok_or_else(|| lost_foreign_key(&name))?;
160 (
161 column_foreign_key(column, reference),
162 ForeignKeyLocation::Column(position),
163 )
164 }
165 DeclaredForeignKey::Table(position) => {
166 let position = position + inherited;
167 let key = table
168 .foreign_keys
169 .get(position)
170 .ok_or_else(|| lost_foreign_key(&position.to_string()))?;
171 (key.clone(), ForeignKeyLocation::Table(position))
172 }
173 })
174}
175
176fn store_definition(
177 table: &mut CreateTable,
178 location: ForeignKeyLocation,
179 foreign_key: ForeignKey,
180) -> Result<(), SQLError> {
181 match location {
182 ForeignKeyLocation::Column(position) => {
183 let reference = table.columns[position]
184 .references
185 .as_mut()
186 .ok_or_else(|| lost_foreign_key(&position.to_string()))?;
187 let [referenced_column] = foreign_key.ref_columns.as_slice() else {
188 return Err(SQLError::Internal(
189 "column FOREIGN KEY did not resolve one referenced column".into(),
190 ));
191 };
192 reference.name = foreign_key.name;
193 reference.object_id = foreign_key.object_id;
194 reference.catalog_identity = foreign_key.catalog_identity;
195 reference.referenced_key = foreign_key.referenced_key;
196 reference.referenced_index = foreign_key.referenced_index;
197 reference.table = foreign_key.ref_table;
198 reference.column = Some(referenced_column.clone());
199 }
200 ForeignKeyLocation::Table(position) => table.foreign_keys[position] = foreign_key,
201 }
202 Ok(())
203}