uqa_sql/schema/constraint_metadata/
identity.rs1use super::{CatalogIdentityAllocator, ConstraintMetadataError, ConstraintMetadataResult};
10use crate::ast::{ColumnDef, ConstraintCatalogIdentity};
11
12pub mod claims;
13pub mod foreign_keys;
14pub mod keys;
15pub mod legacy;
16
17pub(super) fn materialize_key_identity(
18 key: &mut crate::ast::TableKeyConstraint,
19 allocate: &mut CatalogIdentityAllocator<'_>,
20) -> ConstraintMetadataResult<bool> {
21 if let Some(identity) = key.catalog_identity {
22 if identity.is_valid() {
23 return Ok(false);
24 }
25 return Err(ConstraintMetadataError::Invalid(
26 "invalid key constraint catalog identity".into(),
27 ));
28 }
29 let object_id = allocate.allocate_object_id("key constraint")?;
30 key.catalog_identity = Some(ConstraintCatalogIdentity {
31 object_id,
32 oid: allocate.allocate_catalog_oid(super::CatalogOidClass::Constraint, &object_id)?,
33 });
34 if !key
35 .catalog_identity
36 .is_some_and(ConstraintCatalogIdentity::is_valid)
37 {
38 return Err(ConstraintMetadataError::Invalid(
39 "invalid key constraint catalog identity".into(),
40 ));
41 }
42 Ok(true)
43}
44
45pub(super) fn materialize_check_oid(
46 object_id: Option<[u8; 16]>,
47 oid: &mut Option<i64>,
48 allocate: &mut CatalogIdentityAllocator<'_>,
49) -> ConstraintMetadataResult<bool> {
50 let object_id = object_id.ok_or_else(|| {
51 ConstraintMetadataError::Invalid("CHECK has no catalog incarnation".into())
52 })?;
53 let changed = oid.is_none();
54 let value = match *oid {
55 Some(value) => value,
56 None => allocate.allocate_catalog_oid(super::CatalogOidClass::Constraint, &object_id)?,
57 };
58 if !(ConstraintCatalogIdentity {
59 object_id,
60 oid: value,
61 })
62 .is_valid()
63 {
64 return Err(ConstraintMetadataError::Invalid(
65 "invalid CHECK catalog identity".into(),
66 ));
67 }
68 *oid = Some(value);
69 Ok(changed)
70}
71
72pub(super) fn materialize_not_null_identity(
73 column: &mut ColumnDef,
74 allocate: &mut CatalogIdentityAllocator<'_>,
75) -> ConstraintMetadataResult<bool> {
76 if let Some(identity) = column.not_null_identity {
77 validate(identity)?;
78 return Ok(false);
79 }
80 let object_id = allocate.allocate_object_id("NOT NULL constraint")?;
81 let identity = ConstraintCatalogIdentity {
82 object_id,
83 oid: allocate.allocate_catalog_oid(super::CatalogOidClass::Constraint, &object_id)?,
84 };
85 validate(identity)?;
86 column.not_null_identity = Some(identity);
87 Ok(true)
88}
89
90fn validate(identity: ConstraintCatalogIdentity) -> ConstraintMetadataResult<()> {
91 if identity.is_valid() {
92 Ok(())
93 } else {
94 Err(ConstraintMetadataError::Invalid(
95 "invalid NOT NULL constraint catalog identity".into(),
96 ))
97 }
98}
99
100pub fn validate_not_null_identities(columns: &[ColumnDef]) -> ConstraintMetadataResult<()> {
101 let mut identities = std::collections::BTreeSet::new();
102 let mut oids = std::collections::BTreeSet::new();
103 for column in columns {
104 match (column.not_null, column.not_null_identity) {
105 (true, Some(identity)) => {
106 validate(identity)?;
107 if !identities.insert(identity.object_id) || !oids.insert(identity.oid) {
108 return Err(ConstraintMetadataError::Invalid(
109 "duplicate NOT NULL constraint catalog identity".into(),
110 ));
111 }
112 }
113 (true, None) => {
114 return Err(ConstraintMetadataError::Invalid(
115 "NOT NULL constraint requires an initial catalog identity migration".into(),
116 ))
117 }
118 (false, Some(_)) => {
119 return Err(ConstraintMetadataError::Invalid(
120 "nullable column retains a NOT NULL constraint identity".into(),
121 ))
122 }
123 (false, None) => {}
124 }
125 }
126 Ok(())
127}
128
129pub fn migrate_constraint_metadata(
131 relation: &uqa_core::RelationIdentity,
132 columns: &mut [ColumnDef],
133 constraints: &mut crate::ast::TableConstraintSet,
134 allocate: &mut CatalogIdentityAllocator<'_>,
135) -> ConstraintMetadataResult<bool> {
136 let missing: Vec<_> = columns
137 .iter()
138 .map(|column| column.not_null && column.not_null_identity.is_none())
139 .collect();
140 let changed = super::materialize_constraint_metadata(relation, columns, constraints, allocate)?;
141 for (column, missing) in columns.iter_mut().zip(missing) {
142 if missing {
143 let name = column.not_null_name.as_deref().ok_or_else(|| {
144 ConstraintMetadataError::Invalid("migrated NOT NULL constraint has no name".into())
145 })?;
146 let identity = column.not_null_identity.as_mut().ok_or_else(|| {
147 ConstraintMetadataError::Invalid(
148 "migrated NOT NULL constraint has no identity".into(),
149 )
150 })?;
151 identity.oid = crate::catalog::oids::stable_oid(
152 "constraint",
153 &format!("{}.{}.{name}", relation.schema, relation.name),
154 );
155 }
156 }
157 validate_not_null_identities(columns)?;
158 Ok(changed)
159}
160
161#[cfg(test)]
162mod tests;