Skip to main content

uqa_sql/schema/constraint_changes/
names.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Names and independent catalog identities from a borrowed relation declaration.
8
9use super::{ConstraintLocation, ForeignKeyLocation};
10use crate::ast::{
11    ColumnDef, ForeignKey, ReferencedPartitionConstraint, TableCheck, TableConstraintSet,
12    TableKeyConstraint,
13};
14
15#[derive(Clone, Copy)]
16pub struct NamedConstraint<'a> {
17    pub name: &'a str,
18    pub object_id: Option<[u8; 16]>,
19    pub location: ConstraintLocation,
20}
21
22#[cfg(test)]
23mod tests;
24
25#[derive(Clone, Copy)]
26pub struct ConstraintNames<'a> {
27    pub columns: &'a [ColumnDef],
28    pub checks: &'a [TableCheck],
29    pub foreign_keys: &'a [ForeignKey],
30    pub keys: &'a [TableKeyConstraint],
31}
32
33impl<'a> ConstraintNames<'a> {
34    pub fn from_definition(columns: &'a [ColumnDef], constraints: &'a TableConstraintSet) -> Self {
35        Self {
36            columns,
37            checks: &constraints.checks,
38            foreign_keys: &constraints.foreign_keys,
39            keys: &constraints.key_constraints,
40        }
41    }
42
43    pub fn entries(self) -> impl Iterator<Item = NamedConstraint<'a>> {
44        let not_null = self
45            .columns
46            .iter()
47            .enumerate()
48            .filter_map(|(position, column)| {
49                column.not_null.then_some(())?;
50                Some(NamedConstraint {
51                    name: column.not_null_name.as_deref()?,
52                    object_id: column.not_null_identity.map(|id| id.object_id),
53                    location: ConstraintLocation::NotNull(position),
54                })
55            });
56        let column_checks = self
57            .columns
58            .iter()
59            .enumerate()
60            .filter_map(|(position, column)| {
61                column.check.as_ref()?;
62                Some(NamedConstraint {
63                    name: column.check_name.as_deref()?,
64                    object_id: column.check_object_id,
65                    location: ConstraintLocation::ColumnCheck(position),
66                })
67            });
68        let column_foreign_keys =
69            self.columns
70                .iter()
71                .enumerate()
72                .filter_map(|(position, column)| {
73                    let key = column.references.as_ref()?;
74                    Some(NamedConstraint {
75                        name: key.name.as_deref()?,
76                        object_id: key.catalog_identity.map(|id| id.object_id),
77                        location: ConstraintLocation::ColumnForeignKey(position),
78                    })
79                });
80        let checks = self
81            .checks
82            .iter()
83            .enumerate()
84            .filter_map(|(position, check)| {
85                Some(NamedConstraint {
86                    name: check.name.as_deref()?,
87                    object_id: check.object_id,
88                    location: ConstraintLocation::TableCheck(position),
89                })
90            });
91        let foreign_keys = self
92            .foreign_keys
93            .iter()
94            .enumerate()
95            .filter_map(|(position, key)| {
96                Some(NamedConstraint {
97                    name: key.name.as_deref()?,
98                    object_id: key.catalog_identity.map(|id| id.object_id),
99                    location: ConstraintLocation::TableForeignKey(position),
100                })
101            });
102        let keys = self.keys.iter().enumerate().filter_map(|(position, key)| {
103            Some(NamedConstraint {
104                name: key.name.as_deref()?,
105                object_id: key.catalog_identity.map(|id| id.object_id),
106                location: ConstraintLocation::Key(position),
107            })
108        });
109        let column_derived = self
110            .columns
111            .iter()
112            .enumerate()
113            .filter_map(|(position, column)| Some((position, column.references.as_ref()?)))
114            .flat_map(|(position, reference)| {
115                derived_entries(
116                    ForeignKeyLocation::Column(position),
117                    &reference.referenced_partitions,
118                )
119            });
120        let table_derived =
121            self.foreign_keys
122                .iter()
123                .enumerate()
124                .flat_map(|(position, foreign_key)| {
125                    derived_entries(
126                        ForeignKeyLocation::Table(position),
127                        &foreign_key.referenced_partitions,
128                    )
129                });
130        not_null
131            .chain(column_checks)
132            .chain(column_foreign_keys)
133            .chain(checks)
134            .chain(foreign_keys)
135            .chain(keys)
136            .chain(column_derived)
137            .chain(table_derived)
138    }
139}
140
141/// The derived constraints of one foreign key, which share the relation's constraint names.
142fn derived_entries(
143    foreign_key: ForeignKeyLocation,
144    constraints: &[ReferencedPartitionConstraint],
145) -> impl Iterator<Item = NamedConstraint<'_>> {
146    constraints
147        .iter()
148        .enumerate()
149        .map(move |(index, constraint)| NamedConstraint {
150            name: &constraint.name,
151            object_id: Some(constraint.catalog_identity.object_id),
152            location: ConstraintLocation::ReferencedPartition(foreign_key, index),
153        })
154}