uqa_sql/schema/constraint_changes/
inheritance.rs1use super::{constraint_error, find_constraint, ConstraintLocation};
10use crate::{
11 ast::{ColumnDef, TableConstraintSet},
12 SQLError,
13};
14
15#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum InheritedConstraintKey<'a> {
17 Check(&'a str),
18 NotNull(&'a str),
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub struct InheritedConstraint<'a> {
23 pub name: &'a str,
24 pub key: InheritedConstraintKey<'a>,
25 pub no_inherit: bool,
26 pub is_local: bool,
27}
28
29impl<'a> InheritedConstraint<'a> {
30 pub fn find(
31 columns: &'a [ColumnDef],
32 constraints: &'a TableConstraintSet,
33 name: &'a str,
34 ) -> Option<Self> {
35 let (key, no_inherit, is_local) = match find_constraint(columns, constraints, name)? {
36 ConstraintLocation::NotNull(index) => {
37 let column = &columns[index];
38 (
39 InheritedConstraintKey::NotNull(column.name.as_str()),
40 column.not_null_no_inherit,
41 column.not_null_is_local,
42 )
43 }
44 ConstraintLocation::ColumnCheck(index) => {
45 let column = &columns[index];
46 (
47 InheritedConstraintKey::Check(name),
48 column.check_no_inherit,
49 column.check_is_local,
50 )
51 }
52 ConstraintLocation::TableCheck(index) => {
53 let check = &constraints.checks[index];
54 (
55 InheritedConstraintKey::Check(name),
56 check.no_inherit,
57 check.is_local,
58 )
59 }
60 _ => return None,
61 };
62 Some(Self {
63 name,
64 key,
65 no_inherit,
66 is_local,
67 })
68 }
69}
70
71pub fn not_null_constraint<'a>(columns: &'a [ColumnDef], name: &str) -> Option<&'a ColumnDef> {
72 columns
73 .iter()
74 .find(|column| column.name == name && column.not_null)
75}
76
77impl InheritedConstraintKey<'_> {
78 pub fn find<'a>(
79 self,
80 columns: &'a [ColumnDef],
81 constraints: &'a TableConstraintSet,
82 ) -> Option<InheritedConstraint<'a>> {
83 let name = match self {
84 Self::NotNull(column) => not_null_constraint(columns, column)?
85 .not_null_name
86 .as_deref()?,
87 Self::Check(name) => match find_constraint(columns, constraints, name)? {
88 ConstraintLocation::ColumnCheck(index) => columns[index].check_name.as_deref()?,
89 ConstraintLocation::TableCheck(index) => {
90 constraints.checks[index].name.as_deref()?
91 }
92 _ => return None,
93 },
94 };
95 InheritedConstraint::find(columns, constraints, name)
96 }
97
98 pub fn require<'a>(
99 self,
100 table: &str,
101 columns: &'a [ColumnDef],
102 constraints: &'a TableConstraintSet,
103 ) -> Result<InheritedConstraint<'a>, SQLError> {
104 self.find(columns, constraints).ok_or_else(|| match self {
105 Self::Check(name) => constraint_error("42704", format!("constraint \"{name}\" of relation \"{table}\" does not exist")),
106 Self::NotNull(column) => constraint_error("XX000", format!("cache lookup failed for not-null constraint on column \"{column}\" of relation \"{table}\"")),
107 })
108 }
109}
110
111#[derive(Clone, Copy, Debug, PartialEq, Eq)]
112pub enum InheritedConstraintRemoval {
113 Drop,
114 Keep,
115 MakeLocal,
116}
117
118pub fn inherited_constraint_removal(
119 recurse: bool,
120 is_local: bool,
121 remaining_parents: usize,
122) -> InheritedConstraintRemoval {
123 if recurse && !is_local && remaining_parents == 0 {
124 InheritedConstraintRemoval::Drop
125 } else if !recurse && remaining_parents == 0 && !is_local {
126 InheritedConstraintRemoval::MakeLocal
127 } else {
128 InheritedConstraintRemoval::Keep
129 }
130}
131
132pub fn ensure_inherited_constraint_removable(
133 table: &str,
134 name: &str,
135 parents: usize,
136) -> Result<(), SQLError> {
137 if parents == 0 {
138 return Ok(());
139 }
140 let relation =
141 uqa_core::RelationIdentity::from_legacy_name(table).map_err(SQLError::Internal)?;
142 Err(constraint_error(
143 "42P16",
144 format!(
145 "cannot drop inherited constraint \"{name}\" of relation \"{}\"",
146 relation.name
147 ),
148 ))
149}
150
151pub fn make_constraint_local(
152 columns: &mut [ColumnDef],
153 constraints: &mut TableConstraintSet,
154 name: &str,
155) -> Result<(), SQLError> {
156 match find_constraint(columns, constraints, name) {
157 Some(ConstraintLocation::NotNull(index)) => columns[index].not_null_is_local = true,
158 Some(ConstraintLocation::ColumnCheck(index)) => columns[index].check_is_local = true,
159 Some(ConstraintLocation::TableCheck(index)) => constraints.checks[index].is_local = true,
160 _ => {
161 return Err(SQLError::Internal(format!(
162 "inherited constraint \"{name}\" disappeared"
163 )))
164 }
165 }
166 Ok(())
167}
168
169#[cfg(test)]
170mod tests;