1use crate::{
9 ast::{ColumnDef, ColumnType, ForeignKey, TableCheck, TableKeyConstraint},
10 SQLError,
11};
12pub fn validate_added_key_columns(
13 table: &str,
14 constraint: &TableKeyConstraint,
15 columns: &[ColumnDef],
16) -> Result<(), SQLError> {
17 let column_names: std::collections::BTreeSet<&str> =
18 columns.iter().map(|column| column.name.as_str()).collect();
19 for column in &constraint.columns {
20 if !column_names.contains(column.as_str()) {
21 return Err(SQLError::TypeMismatch(format!(
22 "ALTER TABLE ADD CONSTRAINT references unknown column `{column}`"
23 )));
24 }
25 }
26 validate_included_key_columns(constraint, &column_names)?;
27 if constraint.without_overlaps {
28 let period_column = constraint.columns.last().ok_or_else(|| {
29 SQLError::TypeMismatch(
30 "constraint using WITHOUT OVERLAPS needs at least two columns".into(),
31 )
32 })?;
33 let period_type = columns
34 .iter()
35 .find(|column| column.name == *period_column)
36 .map(|column| &column.ty)
37 .ok_or_else(|| SQLError::UnknownColumn(format!("{table}.{period_column}")))?;
38 if !matches!(
39 period_type,
40 ColumnType::Range(_) | ColumnType::Multirange(_)
41 ) {
42 return Err(SQLError::Routine {
43 sqlstate: "42804".into(),
44 message: format!(
45 "column \"{period_column}\" in WITHOUT OVERLAPS is not a range or multirange type"
46 ),
47 });
48 }
49 if constraint.columns.len() < 2 {
50 return Err(SQLError::TypeMismatch(
51 "constraint using WITHOUT OVERLAPS needs at least two columns".into(),
52 ));
53 }
54 }
55
56 Ok(())
57}
58pub fn validate_included_key_columns(
60 constraint: &TableKeyConstraint,
61 column_names: &std::collections::BTreeSet<&str>,
62) -> Result<(), SQLError> {
63 match constraint
64 .included_columns
65 .iter()
66 .find(|column| !column_names.contains(column.as_str()))
67 {
68 Some(column) => Err(SQLError::Routine {
69 sqlstate: "42703".into(),
70 message: format!("column \"{column}\" named in key does not exist"),
71 }),
72 None => Ok(()),
73 }
74}
75
76pub fn validate_added_key_identity(
77 table: &str,
78 constraint: &TableKeyConstraint,
79 existing_keys: &[TableKeyConstraint],
80 checks: &[TableCheck],
81 foreign_keys: &[ForeignKey],
82) -> Result<(), SQLError> {
83 if let Some(name) = constraint.name.as_deref() {
84 let check_name_exists = checks
85 .iter()
86 .any(|existing| existing.name.as_deref() == Some(name));
87 let foreign_name_exists = foreign_keys
88 .iter()
89 .any(|existing| existing.name.as_deref() == Some(name));
90 let key_name_exists = existing_keys
91 .iter()
92 .any(|existing| existing.name.as_deref() == Some(name));
93 if check_name_exists || foreign_name_exists || key_name_exists {
94 return Err(SQLError::TypeMismatch(format!(
95 "constraint `{name}` already exists on table `{table}`"
96 )));
97 }
98 }
99 if constraint.kind == crate::ast::TableKeyConstraintKind::PrimaryKey
100 && existing_keys
101 .iter()
102 .any(|existing| existing.kind == crate::ast::TableKeyConstraintKind::PrimaryKey)
103 {
104 return Err(SQLError::TypeMismatch(format!(
105 "multiple PRIMARY KEY constraints are not allowed on table `{table}`"
106 )));
107 }
108
109 Ok(())
110}
111
112pub fn apply_primary_key_columns(
114 table: &str,
115 constraint: &TableKeyConstraint,
116 columns: &mut [ColumnDef],
117) -> Result<(), String> {
118 if constraint.kind == crate::ast::TableKeyConstraintKind::PrimaryKey {
119 for key_column in &constraint.columns {
120 let column = columns
121 .iter_mut()
122 .find(|column| column.name == *key_column)
123 .ok_or_else(|| {
124 format!("column `{key_column}` does not exist on table `{table}`")
125 })?;
126 column.not_null = true;
127 }
128 }
129 Ok(())
130}