1use crate::schema::table_creation::not_nulls::InheritedNotNull;
10use crate::semantics::partition::{
11 transform_partition_bound, validate_new_partition_bound, PartitionContext,
12};
13use crate::{
14 ast::{CreateTable, TableCheck, TableConstraintSet},
15 SQLError,
16};
17pub trait InheritanceCatalog {
19 fn resolve_parent(&self, name: &str) -> Result<String, SQLError>;
20 fn declared_constraints(&self, table: &str) -> Result<TableConstraintSet, String>;
21 fn check_definitions(&self, table: &str) -> Result<Vec<TableCheck>, String>;
22 fn unique_index_keys(&self, table: &str) -> Result<Vec<Vec<crate::ast::IndexKey>>, String>;
24}
25pub struct InheritanceContext<'a> {
26 pub catalog: &'a dyn InheritanceCatalog,
27 pub partitions: PartitionContext<'a>,
28 pub roles: &'a dyn crate::expr::EngineHook,
29}
30
31pub fn inheritance_parent_target(
33 resolution: crate::catalog::resolution::RelationResolution,
34 requested: &str,
35) -> Result<String, SQLError> {
36 use crate::catalog::resolution::RelationResolution;
37 match resolution {
38 RelationResolution::Found(canonical, "table") => Ok(canonical),
39 RelationResolution::Found(canonical, kind @ ("index" | "composite type")) => {
40 Err(crate::catalog::analysis::UnopenableRelation {
41 name: local_relation_name(&canonical),
42 kinds: if kind == "index" {
43 "indexes"
44 } else {
45 "composite types"
46 },
47 }
48 .error())
49 }
50 RelationResolution::Found(canonical, "view" | "materialized view" | "sequence") => {
51 Err(SQLError::Routine {
52 sqlstate: "42809".into(),
53 message: format!(
54 "inherited relation \"{}\" is not a table or foreign table",
55 local_relation_name(&canonical)
56 ),
57 })
58 }
59 RelationResolution::MissingSchema(schema) => Err(SQLError::Routine {
60 sqlstate: "3F000".into(),
61 message: format!("schema \"{schema}\" does not exist"),
62 }),
63 RelationResolution::Found(_, _) | RelationResolution::MissingRelation => {
64 Err(SQLError::UnknownTable(requested.to_string()))
65 }
66 }
67}
68
69fn local_relation_name(canonical: &str) -> String {
70 uqa_core::RelationIdentity::from_legacy_name(canonical)
71 .map_or_else(|_| canonical.to_string(), |relation| relation.name)
72}
73
74#[expect(
76 clippy::too_many_lines,
77 reason = "preserves DDL dependency and action order"
78)]
79pub fn merge_create_table_hierarchy(
80 context: &InheritanceContext<'_>,
81 table: &mut CreateTable,
82 notices: &mut Vec<crate::SQLNotice>,
83) -> Result<MergedParents, SQLError> {
84 table.hierarchy.local_columns = table
85 .columns
86 .iter()
87 .map(|column| column.name.clone())
88 .collect();
89 if table.hierarchy.parents.is_empty() {
90 if table.hierarchy.partition_bound.is_some() {
91 return Err(SQLError::Internal(
92 "partition bound has no parent relation".into(),
93 ));
94 }
95 if let Some(column) = table.untyped_columns.first() {
96 return Err(SQLError::Internal(format!("column `{column}` has no type")));
97 }
98 column_merge::check_column_count(table.columns.len())?;
99 column_merge::reject_repeated_columns(&table.columns)?;
100 return Ok(MergedParents::default());
101 }
102 let is_partition = table.hierarchy.partition_bound.is_some();
103 if is_partition && table.hierarchy.parents.len() != 1 {
104 return Err(SQLError::Internal(
105 "a partition must have exactly one parent".into(),
106 ));
107 }
108 let mut canonical_parents = Vec::with_capacity(table.hierarchy.parents.len());
109 for requested_parent in &table.hierarchy.parents {
110 let parent = context.catalog.resolve_parent(requested_parent)?;
112 if canonical_parents.contains(&parent) {
113 return Err(SQLError::Routine {
114 sqlstate: "42P07".into(),
115 message: format!(
116 "relation \"{}\" would be inherited from more than once",
117 local_relation_name(&parent)
118 ),
119 });
120 }
121 canonical_parents.push(parent);
122 }
123 column_merge::check_column_count(table.columns.len())?;
124 column_merge::reject_repeated_columns(&table.columns)?;
125 let mut inherited = column_merge::InheritedColumns::default();
126 let mut inherited_not_nulls = Vec::new();
127 let mut inherited_checks = Vec::new();
128 let mut inherited_foreign_keys = Vec::new();
129 let mut inherited_keys = Vec::new();
130 for parent in &canonical_parents {
131 let parent_name = local_relation_name(parent);
132 let parent_hierarchy = context
133 .partitions
134 .catalog
135 .try_table_hierarchy(parent)
136 .map_err(|error| SQLError::Internal(format!("read parent hierarchy: {error}")))?;
137 if !is_partition {
139 if parent_hierarchy.partition_spec.is_some() {
140 return Err(SQLError::Routine {
141 sqlstate: "42809".into(),
142 message: format!("cannot inherit from partitioned table \"{parent_name}\""),
143 });
144 }
145 if parent_hierarchy.is_partition() {
146 return Err(SQLError::Routine {
147 sqlstate: "42809".into(),
148 message: format!("cannot inherit from partition \"{parent_name}\""),
149 });
150 }
151 }
152 let constraints = context
153 .catalog
154 .declared_constraints(parent)
155 .map_err(|error| SQLError::Internal(format!("read inherited constraints: {error}")))?;
156 check_parent_persistence(
157 &parent_name,
158 constraints.persistence,
159 table.persistence,
160 is_partition,
161 )?;
162 let mut columns = context
163 .partitions
164 .catalog
165 .try_describe_table(parent)
166 .map_err(|error| SQLError::Internal(format!("read inherited row type: {error}")))?
167 .ok_or_else(|| SQLError::UnknownTable(parent.clone()))?;
168 for column in &mut columns {
169 column.attribute_number = None;
170 column.not_null_identity = None;
171 column.default_catalog_oid = None;
173 if let Some(reference) = &mut column.references {
174 reference.catalog_identity = None;
175 reference.referenced_partitions.clear();
176 }
177 if column.not_null_no_inherit {
178 column.not_null = false;
179 column.not_null_explicit = false;
180 column.not_null_name = None;
181 column.not_null_no_inherit = false;
182 column.not_null_validated = true;
183 }
184 column.not_null_is_local = !column.not_null;
185 column.check = None;
187 column.check_name = None;
188 column.check_object_id = None;
189 column.check_catalog_oid = None;
190 column.check_is_local = true;
191 column.check_enforced = true;
192 column.check_validated = true;
193 column.check_no_inherit = false;
194 }
195 if !is_partition {
196 for column in &mut columns {
198 column.references = None;
199 column.primary_key = false;
200 column.unique = false;
201 if column
202 .auto_increment
203 .as_ref()
204 .is_some_and(crate::ast::AutoIncrement::is_identity)
205 {
206 column.auto_increment = None;
207 }
208 }
209 }
210 for column in &columns {
211 if column.not_null {
212 InheritedNotNull::record(
213 &mut inherited_not_nulls,
214 &column.name,
215 column.not_null_name.as_deref(),
216 );
217 }
218 }
219 for column in columns {
220 inherited.merge_parent_column(column, notices)?;
221 }
222 let mut parent_checks = context
224 .catalog
225 .check_definitions(parent)
226 .map_err(|error| SQLError::Internal(format!("read inherited CHECKs: {error}")))?
227 .into_iter()
228 .filter(|check| !check.no_inherit)
229 .collect::<Vec<_>>();
230 parent_checks.sort_by(|left, right| left.name.cmp(&right.name));
231 for mut check in parent_checks {
232 super::check_inheritance::bind_parent_check_columns(parent, &mut check.expr)?;
233 check.is_local = false;
234 check.object_id = None;
235 check.catalog_oid = None;
236 check.validated = check.enforced;
237 super::check_inheritance::merge_inherited_check(
238 &mut inherited_checks,
239 check,
240 &inherited.columns,
241 )?;
242 }
243 if is_partition {
244 inherited_foreign_keys.extend(constraints.foreign_keys.into_iter().map(|mut key| {
245 key.catalog_identity = None;
246 key.referenced_partitions.clear();
247 key
248 }));
249 inherited_keys.extend(constraints.key_constraints.into_iter().map(|mut key| {
250 key.name = None;
251 key.catalog_identity = None;
252 key
253 }));
254 }
255 }
256 for (position, column) in std::mem::take(&mut table.columns).into_iter().enumerate() {
257 if is_partition {
258 inherited.merge_partition_option(column)?;
259 } else {
260 inherited.merge_declared_column(position, column, notices)?;
261 }
262 }
263 table.untyped_columns.clear();
264 column_merge::check_column_count(inherited.columns.len())?;
265 inherited.reject_conflicting_defaults()?;
266 let expressions = inherited.inherited_expressions();
267 table.columns = inherited.columns;
268 inherited_checks.append(&mut table.checks);
269 table.checks = inherited_checks;
270 if is_partition {
271 inherited_foreign_keys.append(&mut table.foreign_keys);
272 inherited_keys.append(&mut table.key_constraints);
273 table.foreign_keys = inherited_foreign_keys;
274 table.key_constraints = inherited_keys;
275 }
276 table.hierarchy.parents = canonical_parents;
277 Ok(MergedParents {
278 expressions,
279 not_nulls: inherited_not_nulls,
280 })
281}
282
283#[derive(Debug, Default)]
285pub struct MergedParents {
286 pub expressions: Vec<String>,
287 pub not_nulls: Vec<InheritedNotNull>,
288}
289
290fn check_parent_persistence(
292 parent: &str,
293 parent_persistence: crate::ast::RelationPersistence,
294 persistence: crate::ast::RelationPersistence,
295 is_partition: bool,
296) -> Result<(), SQLError> {
297 use crate::ast::RelationPersistence::Temporary;
298 let message = if is_partition && parent_persistence != Temporary && persistence == Temporary {
299 format!(
300 "cannot create a temporary relation as partition of permanent relation \"{parent}\""
301 )
302 } else if persistence != Temporary && parent_persistence == Temporary {
303 if is_partition {
304 format!("cannot create a permanent relation as partition of temporary relation \"{parent}\"")
305 } else {
306 format!("cannot inherit from temporary relation \"{parent}\"")
307 }
308 } else {
309 return Ok(());
310 };
311 Err(SQLError::Routine {
312 sqlstate: "42809".into(),
313 message,
314 })
315}
316
317pub fn bind_create_table_partitioning(
319 context: &InheritanceContext<'_>,
320 table: &mut CreateTable,
321) -> Result<(), SQLError> {
322 if let (Some(parent), Some(bound)) = (
323 table.hierarchy.parents.first(),
324 table.hierarchy.partition_bound.as_ref(),
325 ) {
326 let partitioned = context
328 .partitions
329 .catalog
330 .try_table_hierarchy(parent)
331 .map_err(|error| SQLError::Internal(format!("read parent hierarchy: {error}")))?
332 .partition_spec
333 .is_some();
334 if !partitioned {
335 return Err(SQLError::Routine {
336 sqlstate: "42P17".into(),
337 message: format!("\"{}\" is not partitioned", local_relation_name(parent)),
338 });
339 }
340 let bound = transform_partition_bound(&context.partitions, parent, bound)?;
342 validate_new_partition_bound(&context.partitions, parent, &table.name, &bound)?;
343 table.hierarchy.partition_bound = Some(bound);
344 }
345 validate_partition_keys(context, table)
346}
347
348pub fn merge_same_column(
349 inherited: &mut crate::ast::ColumnDef,
350 declared: crate::ast::ColumnDef,
351) -> Result<(), SQLError> {
352 if inherited.ty != declared.ty {
353 return Err(SQLError::Routine {
354 sqlstate: "42804".into(),
355 message: format!(
356 "inherited column \"{}\" has a type conflict",
357 inherited.name
358 ),
359 });
360 }
361 if inherited.generated.is_some() != declared.generated.is_some() {
362 return Err(SQLError::Routine {
363 sqlstate: "42P17".into(),
364 message: format!(
365 "inherited column \"{}\" has a generation conflict",
366 inherited.name
367 ),
368 });
369 }
370 inherited.object_id = declared.object_id;
372 inherited.attribute_number = declared.attribute_number;
373 let not_null_is_local = (inherited.not_null && inherited.not_null_is_local)
374 || (declared.not_null && declared.not_null_is_local);
375 if declared.not_null && (!inherited.not_null || declared.not_null_is_local) {
376 inherited.not_null_name.clone_from(&declared.not_null_name);
377 inherited.not_null_identity = declared.not_null_identity;
378 inherited.not_null_validated = declared.not_null_validated;
379 inherited.not_null_no_inherit = declared.not_null_no_inherit;
380 }
381 inherited.not_null |= declared.not_null;
382 inherited.not_null_is_local = !inherited.not_null || not_null_is_local;
383 inherited.not_null_explicit |= declared.not_null_explicit;
384 inherited.primary_key |= declared.primary_key;
385 inherited.unique |= declared.unique;
386 if declared.auto_increment.is_some() {
387 inherited.auto_increment = declared.auto_increment;
388 }
389 if declared.default.is_some() {
390 inherited.default = declared.default;
391 }
392 if declared.generated.is_some() {
393 inherited.generated = declared.generated;
394 }
395 if declared.check.is_some() {
396 inherited.check = declared.check;
397 inherited.check_name = declared.check_name;
398 inherited.check_enforced = declared.check_enforced;
399 inherited.check_validated = declared.check_validated;
400 inherited.check_no_inherit = declared.check_no_inherit;
401 inherited.check_is_local = declared.check_is_local;
402 inherited.check_object_id = declared.check_object_id;
403 }
404 if declared.references.is_some() {
405 inherited.references = declared.references;
406 }
407 Ok(())
408}
409
410mod column_merge;
411mod partition_keys;
412use partition_keys::validate_partition_keys;
413
414pub mod alter;
415
416pub mod detachment;
417pub mod foreign_keys;
418pub mod origins;
419pub mod restoration;