Skip to main content

uqa_sql/schema/inheritance/
detachment.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Preserve local constraint rows while separating detached foreign-key enforcement families.
8
9use crate::ast::{ColumnDef, TableConstraintSet};
10use crate::catalog::constraints::{
11    constraint_identities_match, foreign_key_identity, ConstraintIdentity,
12};
13use crate::SQLError;
14use std::collections::BTreeMap;
15
16pub type ConstraintIdentityChange = (ConstraintIdentity, ConstraintIdentity);
17
18pub fn split_foreign_key_families(
19    table: &str,
20    columns: &mut [ColumnDef],
21    constraints: &mut TableConstraintSet,
22    replacements: &BTreeMap<[u8; 16], [u8; 16]>,
23) -> Result<Vec<ConstraintIdentityChange>, SQLError> {
24    let mut changes = Vec::new();
25    for column in columns {
26        let Some(reference) = column.references.as_ref() else {
27            continue;
28        };
29        let Some(replacement) = reference.object_id.and_then(|id| replacements.get(&id)) else {
30            continue;
31        };
32        let before = foreign_key_identity(
33            table,
34            &crate::schema::foreign_keys::column_foreign_key(column, reference),
35        )?;
36        column
37            .references
38            .as_mut()
39            .expect("selected reference")
40            .object_id = Some(*replacement);
41        let mut after = before.clone();
42        after.object_id = Some(*replacement);
43        changes.push((before, after));
44    }
45    for key in &mut constraints.foreign_keys {
46        let Some(replacement) = key.object_id.and_then(|id| replacements.get(&id)) else {
47            continue;
48        };
49        let before = foreign_key_identity(table, key)?;
50        key.object_id = Some(*replacement);
51        changes.push((before, foreign_key_identity(table, key)?));
52    }
53    for key in &mut constraints.hierarchy.partition_inherited_foreign_keys {
54        if let Some(replacement) = key.object_id.and_then(|id| replacements.get(&id)) {
55            key.object_id = Some(*replacement);
56        }
57    }
58    Ok(changes)
59}
60
61/// Materialize each split family's prior explicit mode on both resulting groups. An ALL mode remains session-owned and needs no per-row replacement.
62pub fn preserve_split_constraint_modes(
63    named: &mut BTreeMap<ConstraintIdentity, bool>,
64    retained: &[ConstraintIdentity],
65    detached: &[ConstraintIdentityChange],
66) {
67    let changes: Vec<_> = retained
68        .iter()
69        .map(|identity| (identity, identity))
70        .chain(detached.iter().map(|(before, after)| (before, after)))
71        .filter_map(|(before, after)| {
72            named.iter().find_map(|(identity, deferred)| {
73                constraint_identities_match(before, identity).then_some((after.clone(), *deferred))
74            })
75        })
76        .collect();
77    named.extend(changes);
78}
79
80#[cfg(test)]
81mod tests;