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. A constraint a foreign key derives on a referenced partition renames alone, as `PostgreSQL` renames its row.
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(_)
87                | ConstraintLocation::TableForeignKey(_)
88                | ConstraintLocation::ReferencedPartition(..)
89        )
90    }) else {
91        return Ok(false);
92    };
93    ensure_constraint_name_available(columns, constraints, Some(to), table)?;
94    if let ConstraintLocation::ReferencedPartition(foreign_key, index) = location {
95        let derived = foreign_key
96            .derived_mut(columns, constraints)
97            .and_then(|derived| derived.get_mut(index))
98            .ok_or_else(|| SQLError::Internal("derived constraint disappeared".into()))?;
99        derived.name = to.to_string();
100        return Ok(true);
101    }
102    let (name, identity) = match location {
103        ConstraintLocation::ColumnForeignKey(index) => {
104            let reference = columns[index]
105                .references
106                .as_mut()
107                .expect("selected foreign key");
108            (&mut reference.name, reference.catalog_identity)
109        }
110        ConstraintLocation::TableForeignKey(index) => {
111            let reference = &mut constraints.foreign_keys[index];
112            (&mut reference.name, reference.catalog_identity)
113        }
114        _ => unreachable!("only foreign-key locations are selected"),
115    };
116    let identity = identity
117        .ok_or_else(|| SQLError::Internal("FOREIGN KEY has no durable catalog identity".into()))?;
118    *name = Some(to.to_string());
119    for inherited in &mut constraints.hierarchy.partition_inherited_foreign_keys {
120        if inherited.catalog_identity == Some(identity) {
121            inherited.name = Some(to.to_string());
122        }
123    }
124    Ok(true)
125}
126
127#[cfg(test)]
128mod tests;