uqa_sql/schema/domains/
constraints.rs1use crate::ast::{ConstraintCatalogIdentity, CreateDomain};
10use crate::schema::constraint_metadata::{
11 assign_constraint_name, CatalogIdentityAllocator, CatalogOidClass, ConstraintMetadataError,
12 ConstraintMetadataResult,
13};
14use crate::SQLError;
15use std::collections::BTreeSet;
16use uqa_core::RelationIdentity;
17
18pub fn assign_names(
19 definition: &mut CreateDomain,
20 schema_names: &BTreeSet<String>,
21) -> Result<(), SQLError> {
22 let identity =
23 RelationIdentity::from_legacy_name(&definition.name).map_err(SQLError::Internal)?;
24 let mut names = BTreeSet::new();
25 let mut automatic = schema_names.clone();
26 if let Some(not_null) = &mut definition.not_null {
27 assign_constraint_name(
28 &mut not_null.name,
29 (&identity.name, "", "not_null"),
30 &mut automatic,
31 )
32 .map_err(|error| crate::catalog::errors::storage_error("domain constraint name", &error))?;
33 let name = not_null.name.as_ref().expect("assigned NOT NULL name");
34 names.insert(name.clone());
35 automatic.insert(name.clone());
36 }
37 for check in &mut definition.checks {
38 if let Some(name) = &check.name {
39 if !names.insert(name.clone()) {
40 return Err(crate::assignment::domain::domain_error(
41 "42710",
42 format!(
43 "constraint \"{name}\" for domain \"{}\" already exists",
44 identity.name
45 ),
46 ));
47 }
48 } else {
49 assign_constraint_name(
50 &mut check.name,
51 (&identity.name, "", "check"),
52 &mut automatic,
53 )
54 .map_err(|error| {
55 crate::catalog::errors::storage_error("domain constraint name", &error)
56 })?;
57 names.insert(check.name.clone().expect("assigned CHECK name"));
58 }
59 automatic.extend(check.name.iter().cloned());
60 }
61 Ok(())
62}
63
64pub fn identities(
65 definition: &CreateDomain,
66) -> impl Iterator<Item = ConstraintCatalogIdentity> + '_ {
67 definition
68 .not_null
69 .iter()
70 .filter_map(|constraint| constraint.catalog_identity)
71 .chain(
72 definition
73 .checks
74 .iter()
75 .filter_map(|constraint| constraint.catalog_identity),
76 )
77}
78
79pub fn validate(definition: &CreateDomain, allow_missing: bool) -> ConstraintMetadataResult<()> {
81 let mut names = BTreeSet::new();
82 let mut objects = BTreeSet::new();
83 let mut oids = BTreeSet::new();
84 let rows = definition
85 .not_null
86 .iter()
87 .map(|constraint| (&constraint.name, constraint.catalog_identity))
88 .chain(
89 definition
90 .checks
91 .iter()
92 .map(|constraint| (&constraint.name, constraint.catalog_identity)),
93 );
94 for (name, identity) in rows {
95 if let Some(name) = name {
96 if name.is_empty() || !names.insert(name) {
97 return Err(invalid("invalid or duplicate domain constraint name"));
98 }
99 } else if !allow_missing {
100 return Err(invalid("domain constraint has no durable name"));
101 }
102 if let Some(identity) = identity {
103 if !identity.is_valid()
104 || !objects.insert(identity.object_id)
105 || !oids.insert(identity.oid)
106 {
107 return Err(invalid(
108 "invalid or duplicate domain constraint catalog identity",
109 ));
110 }
111 } else if !allow_missing {
112 return Err(invalid("domain constraint has no catalog identity"));
113 }
114 }
115 Ok(())
116}
117
118pub fn materialize(
119 definition: &mut CreateDomain,
120 allocate: &mut CatalogIdentityAllocator<'_>,
121) -> ConstraintMetadataResult<bool> {
122 validate(definition, true)?;
123 let relation = RelationIdentity::from_legacy_name(&definition.name).map_err(invalid)?;
124 for identity in identities(definition) {
125 allocate.include_catalog_identity(&relation, CatalogOidClass::Constraint, identity)?;
126 }
127 let mut changed = false;
128 let rows = definition
129 .not_null
130 .iter_mut()
131 .map(|constraint| &mut constraint.catalog_identity)
132 .chain(
133 definition
134 .checks
135 .iter_mut()
136 .map(|constraint| &mut constraint.catalog_identity),
137 );
138 for target in rows {
139 if target.is_some() {
140 continue;
141 }
142 let object_id = allocate.allocate_object_id("domain constraint")?;
143 *target = Some(ConstraintCatalogIdentity {
144 object_id,
145 oid: allocate.allocate_catalog_oid(CatalogOidClass::Constraint, &object_id)?,
146 });
147 changed = true;
148 }
149 validate(definition, false)?;
150 Ok(changed)
151}
152
153fn invalid(message: impl Into<String>) -> ConstraintMetadataError {
154 ConstraintMetadataError::Invalid(message.into())
155}
156
157#[cfg(test)]
158mod tests;