1use crate::{
9 assignment::columns::{AssignmentColumnCatalog, ColumnCatalogError},
10 ast::{ColumnDef, ColumnType, Expr, GeneratedColumn, GeneratedColumnKind},
11 schema::{
12 columns::addition::AddedColumnKeys, constraint_changes::ConstraintTypeContext,
13 dependencies::registration::SchemaDependencyBindingContext,
14 },
15 SQLError,
16};
17pub trait ColumnChangeCatalog {
19 fn has_column(&self, table: &str, column: &str) -> Result<bool, ColumnCatalogError>;
20 fn column_type(
21 &self,
22 table: &str,
23 column: &str,
24 ) -> Result<Option<ColumnType>, ColumnCatalogError>;
25 fn stored_columns(&self, table: &str) -> Result<Vec<ColumnDef>, ColumnCatalogError>;
26}
27pub struct ColumnAlterAnalysisContext<'a> {
28 pub columns: &'a dyn AssignmentColumnCatalog,
29 pub keys: &'a dyn AddedColumnKeys,
30 pub state: &'a dyn ColumnChangeCatalog,
31 pub bindings: SchemaDependencyBindingContext<'a>,
32 pub constraint_types: ConstraintTypeContext<'a>,
33}
34fn ddl_storage_error(action: &str, error: ColumnCatalogError) -> SQLError {
35 crate::catalog::errors::storage_error(action, error.as_ref())
36}
37pub fn generated_columns_referencing_column(columns: &[ColumnDef], column: &str) -> Vec<String> {
38 columns
39 .iter()
40 .filter(|candidate| candidate.name != column)
41 .filter(|candidate| {
42 candidate.generated.as_ref().is_some_and(|generated| {
43 crate::schema::dependencies::schema_expr_references_column(
44 &generated.expression,
45 column,
46 )
47 })
48 })
49 .map(|candidate| candidate.name.clone())
50 .collect()
51}
52pub fn validate_column_default(
53 context: &ColumnAlterAnalysisContext<'_>,
54 table: &str,
55 column: &str,
56 default: &mut Expr,
57) -> Result<(), SQLError> {
58 super::reject_default_change_on_generated_column(context.columns, table, column)?;
59 let target = context
60 .state
61 .column_type(table, column)
62 .map_err(|error| ddl_storage_error("ALTER COLUMN SET DEFAULT", error))?
63 .ok_or_else(|| SQLError::UnknownColumn(format!("{table}.{column}")))?;
64 let binding = context.bindings.bindings.binding_scope()?;
65 crate::schema::defaults::validate_default_expression(
66 &crate::schema::SchemaBindingContext {
67 catalog: context.bindings.schema,
68 binding: &binding.context(),
69 },
70 default,
71 &target,
72 )
73}
74pub fn analyze_generated_expression(
75 context: &ColumnAlterAnalysisContext<'_>,
76 table: &str,
77 qualifier: &str,
78 name: &str,
79 expression: Expr,
80) -> Result<(GeneratedColumn, GeneratedColumnKind), SQLError> {
81 let mut columns = context
82 .columns
83 .try_describe_table(table)
84 .map_err(|error| ddl_storage_error("ALTER COLUMN SET EXPRESSION", error))?
85 .ok_or_else(|| SQLError::UnknownTable(table.to_string()))?;
86 let column = columns
87 .iter_mut()
88 .find(|column| column.name == name)
89 .ok_or_else(|| SQLError::UnknownColumn(format!("{table}.{name}")))?;
90 let Some(current) = column.generated.as_ref() else {
91 return Err(SQLError::TypeMismatch(format!(
92 "column `{name}` of relation `{table}` is not a generated column"
93 )));
94 };
95 let kind = current.kind;
96 column.generated = Some(GeneratedColumn {
97 kind,
98 expression: Box::new(expression),
99 function_dependencies: Vec::new(),
100 });
101 let key_constraints = context
102 .keys
103 .try_key_constraints(table)
104 .map_err(|error| ddl_storage_error("ALTER COLUMN SET EXPRESSION", error))?;
105 let foreign_keys = context
106 .keys
107 .try_foreign_keys(table)
108 .map_err(|error| ddl_storage_error("ALTER COLUMN SET EXPRESSION", error))?;
109 crate::schema::generated::prepare_generated_columns(
110 context.bindings.schema,
111 qualifier,
112 &mut columns,
113 &key_constraints,
114 &foreign_keys,
115 )?;
116 let generated = columns
117 .iter()
118 .find(|column| column.name == name)
119 .and_then(|column| column.generated.clone())
120 .ok_or_else(|| {
121 SQLError::Internal(format!(
122 "generated column `{name}` disappeared during validation"
123 ))
124 })?;
125 Ok((generated, kind))
126}
127pub fn validate_drop_expression(
128 context: &ColumnAlterAnalysisContext<'_>,
129 table: &str,
130 name: &str,
131) -> Result<(), SQLError> {
132 let columns = context
133 .columns
134 .try_describe_table(table)
135 .map_err(|error| ddl_storage_error("ALTER COLUMN DROP EXPRESSION", error))?
136 .ok_or_else(|| SQLError::UnknownTable(table.to_string()))?;
137 let column = columns
138 .iter()
139 .find(|column| column.name == name)
140 .ok_or_else(|| SQLError::UnknownColumn(format!("{table}.{name}")))?;
141 let Some(generated) = column.generated.as_ref() else {
142 return Err(SQLError::TypeMismatch(format!(
143 "column `{name}` of relation `{table}` is not a generated column"
144 )));
145 };
146 if generated.kind == GeneratedColumnKind::Virtual {
147 return Err(SQLError::Unsupported(format!(
148 "ALTER TABLE / DROP EXPRESSION is not supported for virtual generated column `{name}`"
149 )));
150 }
151 Ok(())
152}
153pub fn analyze_column_type(
154 context: &ColumnAlterAnalysisContext<'_>,
155 table: &str,
156 qualifier: &str,
157 name: &str,
158 ty: &ColumnType,
159) -> Result<Option<GeneratedColumnKind>, SQLError> {
160 if !context
161 .state
162 .has_column(table, name)
163 .map_err(|error| ddl_storage_error("ALTER COLUMN", error))?
164 {
165 return Err(SQLError::Unsupported(format!(
166 "ALTER TABLE ALTER COLUMN: column `{name}` does not exist"
167 )));
168 }
169 super::validate_postgres_relation_column_type(name, ty)?;
170 let mut candidate_columns = context
171 .columns
172 .try_describe_table(table)
173 .map_err(|error| ddl_storage_error("ALTER COLUMN TYPE", error))?
174 .ok_or_else(|| SQLError::UnknownTable(table.to_string()))?;
175 let candidate = candidate_columns
176 .iter_mut()
177 .find(|column| column.name == name)
178 .ok_or_else(|| SQLError::UnknownColumn(format!("{table}.{name}")))?;
179 candidate.ty.clone_from(ty);
180 let target_generated_kind = candidate.generated.as_ref().map(|generated| generated.kind);
181 if target_generated_kind.is_none() {
182 let columns = context
183 .state
184 .stored_columns(table)
185 .map_err(|error| ddl_storage_error("ALTER COLUMN TYPE", error))?;
186 let dependents = generated_columns_referencing_column(&columns, name);
187 if !dependents.is_empty() {
188 return Err(SQLError::TypeMismatch(format!("cannot alter type of column `{name}` because generated column(s) `{}` depend on it", dependents.join("`, `"))));
189 }
190 }
191 let key_constraints = context
192 .keys
193 .try_key_constraints(table)
194 .map_err(|error| ddl_storage_error("ALTER COLUMN TYPE", error))?;
195 let foreign_keys = context
196 .keys
197 .try_foreign_keys(table)
198 .map_err(|error| ddl_storage_error("ALTER COLUMN TYPE", error))?;
199 crate::schema::constraint_changes::validate_altered_constraint_column_types(
200 &context.constraint_types,
201 table,
202 &candidate_columns,
203 &key_constraints,
204 &foreign_keys,
205 )?;
206 crate::schema::generated::prepare_generated_columns(
207 context.bindings.schema,
208 qualifier,
209 &mut candidate_columns,
210 &key_constraints,
211 &foreign_keys,
212 )?;
213 Ok(target_generated_kind)
214}