Skip to main content

uqa_sql/catalog/
constraints.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use uqa_core::RelationIdentity;
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
10pub struct ConstraintIdentity {
11    pub relation: RelationIdentity,
12    pub name: String,
13    pub object_id: Option<[u8; 16]>,
14}
15
16pub fn constraint_identities_match(left: &ConstraintIdentity, right: &ConstraintIdentity) -> bool {
17    match (left.object_id, right.object_id) {
18        (Some(left), Some(right)) => left == right,
19        _ => left == right,
20    }
21}
22
23/// Reconcile a retained constraint with a renamed row before considering a renamed relation. A removed row on a surviving relation must not bind to another partition copy.
24pub fn find_live_constraint_identity<'a>(
25    live: &'a [ConstraintIdentity],
26    live_relations: &std::collections::BTreeSet<RelationIdentity>,
27    identity: &ConstraintIdentity,
28) -> Option<&'a ConstraintIdentity> {
29    live.iter()
30        .find(|current| *current == identity)
31        .or_else(|| {
32            live.iter().find(|current| {
33                current.relation == identity.relation
34                    && constraint_identities_match(identity, current)
35            })
36        })
37        .or_else(|| {
38            if live_relations.contains(&identity.relation) {
39                return None;
40            }
41            live.iter()
42                .find(|current| constraint_identities_match(identity, current))
43        })
44}
45
46pub fn foreign_key_identity(
47    table: &str,
48    foreign_key: &crate::ast::ForeignKey,
49) -> Result<ConstraintIdentity, crate::SQLError> {
50    let relation = RelationIdentity::from_legacy_name(table).map_err(|error| {
51        crate::SQLError::Internal(format!(
52            "decode foreign-key relation identity '{table}': {error}"
53        ))
54    })?;
55    let name = foreign_key.name.clone().ok_or_else(|| {
56        crate::SQLError::Internal(format!(
57            "foreign key on '{table}' has no materialized constraint name"
58        ))
59    })?;
60    let object_id = foreign_key.object_id.ok_or_else(|| {
61        crate::SQLError::Internal(format!(
62            "foreign key '{name}' on '{table}' has no materialized object identity"
63        ))
64    })?;
65    Ok(ConstraintIdentity {
66        relation,
67        name,
68        object_id: Some(object_id),
69    })
70}
71
72#[cfg(test)]
73mod tests;