Skip to main content

uqa_sql/schema/constraint_changes/
renaming.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Inherited constraint rename diagnostics and metadata edits preserve constraint identity.
8
9use super::{
10    constraint_error, ensure_constraint_name_available, find_constraint, ConstraintLocation,
11};
12use crate::{
13    ast::{ColumnDef, TableConstraintSet},
14    SQLError,
15};
16
17pub fn ensure_recursive_rename(
18    name: &str,
19    recurse: bool,
20    has_children: bool,
21) -> Result<(), SQLError> {
22    if !recurse && has_children {
23        return Err(constraint_error(
24            "42P16",
25            format!("inherited constraint \"{name}\" must be renamed in child tables too"),
26        ));
27    }
28    Ok(())
29}
30
31pub fn ensure_rename_parents(name: &str, parents: usize, expected: usize) -> Result<(), SQLError> {
32    if parents > expected {
33        return Err(constraint_error(
34            "42P16",
35            format!("cannot rename inherited constraint \"{name}\""),
36        ));
37    }
38    Ok(())
39}
40
41pub fn rename_inherited_constraint(
42    table: &str,
43    columns: &mut [ColumnDef],
44    constraints: &mut TableConstraintSet,
45    from: &str,
46    to: &str,
47) -> Result<(), SQLError> {
48    let location = find_constraint(columns, constraints, from)
49        .filter(|location| {
50            matches!(
51                location,
52                ConstraintLocation::NotNull(_)
53                    | ConstraintLocation::ColumnCheck(_)
54                    | ConstraintLocation::TableCheck(_)
55            )
56        })
57        .ok_or_else(|| {
58            constraint_error(
59                "42704",
60                format!("constraint \"{from}\" for table \"{table}\" does not exist"),
61            )
62        })?;
63    ensure_constraint_name_available(columns, constraints, Some(to), table)?;
64    match location {
65        ConstraintLocation::NotNull(index) => columns[index].not_null_name = Some(to.to_string()),
66        ConstraintLocation::ColumnCheck(index) => columns[index].check_name = Some(to.to_string()),
67        ConstraintLocation::TableCheck(index) => {
68            constraints.checks[index].name = Some(to.to_string());
69        }
70        _ => unreachable!("only inherited constraint locations are selected"),
71    }
72    Ok(())
73}
74
75/// Foreign-key rename is local even for a partition parent or a partition clone. Enforcement and deferred-event identities remain unchanged.
76pub fn rename_foreign_key(
77    table: &str,
78    columns: &mut [ColumnDef],
79    constraints: &mut TableConstraintSet,
80    from: &str,
81    to: &str,
82) -> Result<bool, SQLError> {
83    let Some(location) = find_constraint(columns, constraints, from).filter(|location| {
84        matches!(
85            location,
86            ConstraintLocation::ColumnForeignKey(_) | ConstraintLocation::TableForeignKey(_)
87        )
88    }) else {
89        return Ok(false);
90    };
91    ensure_constraint_name_available(columns, constraints, Some(to), table)?;
92    let (name, identity) = match location {
93        ConstraintLocation::ColumnForeignKey(index) => {
94            let reference = columns[index]
95                .references
96                .as_mut()
97                .expect("selected foreign key");
98            (&mut reference.name, reference.catalog_identity)
99        }
100        ConstraintLocation::TableForeignKey(index) => {
101            let reference = &mut constraints.foreign_keys[index];
102            (&mut reference.name, reference.catalog_identity)
103        }
104        _ => unreachable!("only foreign-key locations are selected"),
105    };
106    let identity = identity
107        .ok_or_else(|| SQLError::Internal("FOREIGN KEY has no durable catalog identity".into()))?;
108    *name = Some(to.to_string());
109    for inherited in &mut constraints.hierarchy.partition_inherited_foreign_keys {
110        if inherited.catalog_identity == Some(identity) {
111            inherited.name = Some(to.to_string());
112        }
113    }
114    Ok(true)
115}
116
117#[cfg(test)]
118mod tests;