uqa_sql/schema/constraint_changes/
not_null_removal.rs1use super::constraint_error;
10use crate::{
11 ast::{ColumnDef, TableConstraintSet, TableKeyConstraintKind},
12 SQLError,
13};
14
15pub fn validate_constraint_removal(
16 table: &str,
17 column: &ColumnDef,
18 constraints: &TableConstraintSet,
19) -> Result<(), SQLError> {
20 if column.primary_key
21 || constraints.key_constraints.iter().any(|key| {
22 key.kind == TableKeyConstraintKind::PrimaryKey && key.columns.contains(&column.name)
23 })
24 {
25 return Err(constraint_error(
26 "42P16",
27 format!("column \"{}\" is in a primary key", column.name),
28 ));
29 }
30 reject_identity(table, column, "55000")
31}
32
33pub fn validate_column_removal(table: &str, column: &ColumnDef) -> Result<(), SQLError> {
34 if !column.not_null {
35 return Ok(());
36 }
37 reject_identity(table, column, "42601")
38}
39
40fn reject_identity(table: &str, column: &ColumnDef, sqlstate: &str) -> Result<(), SQLError> {
41 if !column
42 .auto_increment
43 .as_ref()
44 .is_some_and(|definition| definition.is_identity())
45 {
46 return Ok(());
47 }
48 let relation =
49 uqa_core::RelationIdentity::from_legacy_name(table).map_err(SQLError::Internal)?;
50 Err(constraint_error(
51 sqlstate,
52 format!(
53 "column \"{}\" of relation \"{}\" is an identity column",
54 column.name, relation.name
55 ),
56 ))
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn identity_not_null_removal_distinguishes_column_and_constraint_syntax() {
65 let crate::Statement::CreateTable(table) = crate::compile(
66 "CREATE TABLE t(v integer GENERATED ALWAYS AS IDENTITY CONSTRAINT nn NOT NULL)",
67 )
68 .unwrap()
69 .remove(0) else {
70 panic!("table");
71 };
72 let column = &table.columns[0];
73 let constraints = TableConstraintSet::default();
74 assert_eq!(
75 validate_constraint_removal("public.t", column, &constraints)
76 .unwrap_err()
77 .sqlstate(),
78 Some("55000")
79 );
80 assert_eq!(
81 validate_column_removal("public.t", column)
82 .unwrap_err()
83 .sqlstate(),
84 Some("42601")
85 );
86 let mut keyed = column.clone();
87 keyed.primary_key = true;
88 assert_eq!(
89 validate_constraint_removal("public.t", &keyed, &constraints)
90 .unwrap_err()
91 .sqlstate(),
92 Some("42P16")
93 );
94 }
95
96 #[test]
97 fn serial_and_nullable_columns_do_not_inherit_identity_protection() {
98 let crate::Statement::CreateTable(table) =
99 crate::compile("CREATE TABLE t(v serial CONSTRAINT nn NOT NULL, nullable integer)")
100 .unwrap()
101 .remove(0)
102 else {
103 panic!("table");
104 };
105 for column in &table.columns {
106 validate_constraint_removal("public.t", column, &TableConstraintSet::default())
107 .unwrap();
108 validate_column_removal("public.t", column).unwrap();
109 }
110 }
111}