uqa_sql/schema/
constraint_views.rs1use crate::ast::{ColumnDef, ForeignKey, TableCheck, TableKeyConstraint, TableKeyConstraintKind};
9use std::collections::BTreeSet;
10use uqa_core::RelationIdentity;
11
12pub fn column_checks(columns: &[ColumnDef]) -> Vec<TableCheck> {
13 let mut out = Vec::new();
14 for column in columns {
15 if let Some(expr) = column.check.clone() {
16 out.push(TableCheck {
17 catalog_oid: column.check_catalog_oid,
18 name: column
19 .check_name
20 .clone()
21 .or_else(|| Some(format!("{}_check", column.name))),
22 expr,
23 enforced: column.check_enforced,
24 validated: column.check_validated,
25 no_inherit: column.check_no_inherit,
26 object_id: column.check_object_id,
27 is_local: column.check_is_local,
28 partition_constraint: None,
29 });
30 }
31 }
32 out
33}
34pub fn append_column_foreign_keys(columns: &[ColumnDef], foreign_keys: &mut Vec<ForeignKey>) {
35 for column in columns {
36 if let Some(reference) = &column.references {
37 let mut foreign_key = super::foreign_keys::column_foreign_key(column, reference);
38 foreign_key.name = foreign_key
39 .name
40 .or_else(|| Some(format!("{}_fkey", column.name)));
41 foreign_keys.push(foreign_key);
42 }
43 }
44}
45pub fn append_column_keys(columns: &[ColumnDef], constraints: &mut Vec<TableKeyConstraint>) {
46 for column in columns {
47 let kind = if column.primary_key {
48 Some(TableKeyConstraintKind::PrimaryKey)
49 } else if column.unique {
50 Some(TableKeyConstraintKind::Unique)
51 } else {
52 None
53 };
54 let Some(kind) = kind else {
55 continue;
56 };
57 if constraints.iter().any(|constraint| {
58 constraint.kind == kind
59 && constraint.columns.as_slice() == std::slice::from_ref(&column.name)
60 }) {
61 continue;
62 }
63 constraints.push(TableKeyConstraint {
64 catalog_identity: None,
65 index_identity: None,
66 name: None,
67 kind,
68 columns: vec![column.name.clone()],
69 included_columns: Vec::new(),
70 nulls_not_distinct: false,
71 without_overlaps: false,
72 });
73 }
74}
75pub fn auto_increment_columns(columns: &[ColumnDef]) -> BTreeSet<String> {
76 columns
77 .iter()
78 .filter(|column| column.auto_increment.is_some())
79 .map(|column| column.name.clone())
80 .collect()
81}
82pub fn unique_scalar_columns(
83 constraints: Vec<TableKeyConstraint>,
84 auto_increment: &BTreeSet<String>,
85) -> Vec<String> {
86 constraints
87 .into_iter()
88 .filter(|constraint| constraint.columns.len() == 1)
89 .map(|constraint| constraint.columns[0].clone())
90 .filter(|column| !auto_increment.contains(column))
91 .collect()
92}
93pub trait StoredTableNames {
95 fn stored_table_exists(&self, relation: &RelationIdentity) -> bool;
96 fn stored_table_names(&self) -> Vec<RelationIdentity>;
97}
98pub fn canonical_stored_foreign_key_target(
99 catalog: &dyn StoredTableNames,
100 reference: &str,
101) -> Result<String, String> {
102 let (schema, local_name) = RelationIdentity::parse_reference(reference)
103 .map_err(|error| format!("invalid persisted foreign-key target `{reference}`: {error}"))?;
104 if let Some(schema) = schema {
105 let target = RelationIdentity::new(schema, local_name);
106 if catalog.stored_table_exists(&target) {
107 return Ok(target.qualified_name());
108 }
109 return Err(format!(
110 "dangling persisted foreign-key target `{reference}`"
111 ));
112 }
113 let candidates = catalog
114 .stored_table_names()
115 .into_iter()
116 .filter(|candidate| candidate.name == local_name)
117 .map(|candidate| candidate.qualified_name())
118 .collect::<Vec<_>>();
119 match candidates.as_slice() {
120 [target] => Ok(target.clone()),
121 [] => Err(format!(
122 "dangling persisted foreign-key target `{reference}`"
123 )),
124 _ => Err(format!(
125 "ambiguous persisted foreign-key target `{reference}` matches {}",
126 candidates.join(", ")
127 )),
128 }
129}
130pub fn bind_stored_foreign_key_targets(
131 catalog: &dyn StoredTableNames,
132 foreign_keys: &mut [ForeignKey],
133) -> Result<(), String> {
134 for foreign_key in foreign_keys {
135 foreign_key.ref_table =
136 canonical_stored_foreign_key_target(catalog, &foreign_key.ref_table)?;
137 }
138 Ok(())
139}
140
141pub fn bind_stored_foreign_key_declarations(
143 catalog: &dyn StoredTableNames,
144 columns: &mut [ColumnDef],
145 constraints: &mut crate::ast::TableConstraintSet,
146) -> Result<bool, String> {
147 let mut changed = false;
148 let mut bind = |target: &mut String| -> Result<(), String> {
149 let canonical = canonical_stored_foreign_key_target(catalog, target)?;
150 changed |= *target != canonical;
151 *target = canonical;
152 Ok(())
153 };
154 for column in columns {
155 if let Some(reference) = &mut column.references {
156 bind(&mut reference.table)?;
157 }
158 }
159 for key in constraints
160 .foreign_keys
161 .iter_mut()
162 .chain(&mut constraints.hierarchy.partition_inherited_foreign_keys)
163 {
164 bind(&mut key.ref_table)?;
165 }
166 Ok(changed)
167}
168
169#[cfg(test)]
170mod tests;