uqa_sql/schema/table_creation/
checks.rs1use super::declaration::{validate_check_expression, CreateTableAnalysisContext};
10use crate::ast::{CreateTable, DeclaredCheck, TableCheck};
11use crate::schema::check_inheritance::{duplicate_check, validate_check_merge};
12use crate::schema::constraint_changes::{restore_column_check, take_column_check};
13use crate::schema::constraint_metadata::{
14 assign_check_name, materialize_check_identity, CatalogIdentityAllocator,
15 ConstraintMetadataError,
16};
17use crate::{SQLError, SQLNotice};
18use std::collections::BTreeSet;
19
20pub fn define_create_table_checks(
22 context: &CreateTableAnalysisContext<'_>,
23 table: &mut CreateTable,
24 held: &BTreeSet<String>,
25 notices: &mut Vec<SQLNotice>,
26 allocate: &mut CatalogIdentityAllocator<'_>,
27) -> Result<(), SQLError> {
28 let relation =
29 uqa_core::RelationIdentity::from_legacy_name(&table.name).map_err(SQLError::Internal)?;
30 let columns = table.columns.clone();
31 let partition = table.hierarchy.is_partition();
32 let (mut inherited, local): (Vec<TableCheck>, Vec<TableCheck>) =
33 std::mem::take(&mut table.checks)
34 .into_iter()
35 .partition(|check| !check.is_local);
36 let mut local = local.into_iter().map(Some).collect::<Vec<_>>();
37 for check in &mut inherited {
39 bind_check(context, table, &columns, check)?;
40 }
41 let mut used = context
43 .index_names
44 .automatic_constraint_names(&table.name)?;
45 used.extend(held.iter().cloned());
46 used.extend(inherited.iter().filter_map(|check| check.name.clone()));
47 let mut chosen = BTreeSet::new();
48 let mut stored = Vec::new();
49 for declared in std::mem::take(&mut table.check_order) {
50 let (mut check, column) = match declared {
51 DeclaredCheck::Column(name) => {
52 let index = table
53 .columns
54 .iter()
55 .position(|column| column.name == name)
56 .ok_or_else(|| lost_check(&name))?;
57 let check = take_column_check(&mut table.columns[index])
58 .ok_or_else(|| lost_check(&name))?;
59 (check, Some(index))
60 }
61 DeclaredCheck::Table(position) => (
62 local
63 .get_mut(position)
64 .and_then(Option::take)
65 .ok_or_else(|| lost_check(&position.to_string()))?,
66 None,
67 ),
68 };
69 bind_check(context, table, &columns, &mut check)?;
70 let merged = if let Some(name) = check.name.clone() {
71 if !chosen.insert(name.clone()) {
72 return Err(SQLError::Routine {
73 sqlstate: "42710".into(),
74 message: format!("check constraint \"{name}\" already exists"),
75 });
76 }
77 used.insert(name);
78 merge_with_existing(&relation.name, &inherited, held, &check, &columns)?
79 } else {
80 assign_check_name(&relation.name, &check.expr, &mut check.name, &mut used)
81 .map_err(|error| SQLError::Internal(error.to_string()))?;
82 chosen.extend(check.name.clone());
83 None
84 };
85 if let Some(name) = merged.and(check.name.as_deref()) {
86 notices.push(SQLNotice::notice(format!(
87 "merging constraint \"{name}\" with inherited definition"
88 )));
89 } else if check.no_inherit && table.hierarchy.partition_spec.is_some() {
90 return Err(SQLError::Routine {
91 sqlstate: "42P16".into(),
92 message: format!(
93 "cannot add NO INHERIT constraint to partitioned table \"{}\"",
94 relation.name
95 ),
96 });
97 }
98 if merged.is_some() {
100 check.is_local = !partition;
101 } else {
102 materialize_check_identity(&mut check.object_id, &mut check.catalog_oid, allocate)
103 .map_err(ConstraintMetadataError::into_sql_error)?;
104 }
105 match (merged, column) {
106 (Some(position), Some(index)) => {
107 let existing = inherited.remove(position);
108 check.object_id = existing.object_id;
109 check.catalog_oid = existing.catalog_oid;
110 restore_column_check(&mut table.columns[index], check);
111 }
112 (Some(position), None) => {
113 check.object_id = inherited[position].object_id;
114 check.catalog_oid = inherited[position].catalog_oid;
115 inherited[position] = check;
116 }
117 (None, Some(index)) => restore_column_check(&mut table.columns[index], check),
118 (None, None) => stored.push(check),
119 }
120 }
121 if local.iter().any(Option::is_some) {
122 return Err(lost_check("a table CHECK"));
123 }
124 inherited.append(&mut stored);
125 table.checks = inherited;
126 Ok(())
127}
128
129fn bind_check(
131 context: &CreateTableAnalysisContext<'_>,
132 table: &CreateTable,
133 columns: &[crate::ast::ColumnDef],
134 check: &mut TableCheck,
135) -> Result<(), SQLError> {
136 validate_check_expression(
137 context,
138 &table.name,
139 &table.qualifier,
140 columns,
141 &mut check.expr,
142 )?;
143 crate::catalog::regrole_dependencies::reject_stored_regrole_constants(
144 context.schema,
145 &check.expr,
146 None,
147 )
148}
149
150fn merge_with_existing(
152 relation: &str,
153 inherited: &[TableCheck],
154 held: &BTreeSet<String>,
155 check: &TableCheck,
156 columns: &[crate::ast::ColumnDef],
157) -> Result<Option<usize>, SQLError> {
158 let name = check.name.as_deref().unwrap_or_default();
159 if let Some(position) = inherited
160 .iter()
161 .position(|existing| existing.name.as_deref() == Some(name))
162 {
163 validate_check_merge(relation, &inherited[position], check, columns)?;
164 return Ok(Some(position));
165 }
166 if held.contains(name) {
167 return Err(duplicate_check(relation, name));
168 }
169 Ok(None)
170}
171
172fn lost_check(name: &str) -> SQLError {
173 SQLError::Internal(format!(
174 "the written order of CREATE TABLE CHECKs lost {name}"
175 ))
176}