1use crate::ast::{ColumnType, CompositeAttributeDefinition};
10use crate::catalog::composite_type::StoredCompositeAttribute;
11use crate::type_resolution::{resolve_declared_column_type, FunctionTypeResolver};
12use crate::SQLError;
13
14pub mod type_changes;
15
16pub fn validate_renamed_attribute(
18 definition: &crate::catalog::composite_type::StoredComposite,
19 from: &str,
20 to: &str,
21) -> Result<(), SQLError> {
22 super::columns::renamed_column_position(
23 &definition.identity.name,
24 definition
25 .live_attributes()
26 .map(|attribute| attribute.name.as_str()),
27 from,
28 to,
29 false,
30 )
31 .map(|_| ())
32}
33
34pub fn validate_removed_attribute(
36 definition: &crate::catalog::composite_type::StoredComposite,
37 removal: &crate::ast::CompositeAttributeRemoval,
38) -> Result<bool, SQLError> {
39 if definition
40 .live_attributes()
41 .any(|attribute| attribute.name == removal.name)
42 {
43 return Ok(true);
44 }
45 if removal.if_exists {
46 Ok(false)
47 } else {
48 Err(super::columns::undefined_relation_column(
49 &definition.identity.name,
50 &removal.name,
51 ))
52 }
53}
54
55pub fn prepare_added_attribute(
57 types: &dyn FunctionTypeResolver,
58 composites: &dyn crate::expr::composites::CompositeTypeCatalog,
59 definition: &crate::catalog::composite_type::StoredComposite,
60 addition: &crate::ast::CompositeAttributeAddition,
61) -> Result<StoredCompositeAttribute, SQLError> {
62 let attribute = &addition.attribute;
63 if definition
64 .live_attributes()
65 .any(|current| current.name == attribute.name)
66 {
67 return Err(SQLError::Routine {
68 sqlstate: "42701".into(),
69 message: format!(
70 "column \"{}\" of relation \"{}\" already exists",
71 attribute.name, definition.identity.name
72 ),
73 });
74 }
75 super::table_creation::column_declarations::check_serial_array(&addition.declaration)?;
76 let number = definition.next_attribute_number();
77 if usize::try_from(number).map_or(true, |number| number > MAX_ATTRIBUTES) {
78 return Err(SQLError::Routine {
79 sqlstate: "54011".into(),
80 message: format!("tables can have at most {MAX_ATTRIBUTES} columns"),
81 });
82 }
83 let declared = match &attribute.ty {
84 ColumnType::Named(name) if addition.declaration.serial => {
85 crate::compiler::compile_retained_type_declaration(name)?
86 }
87 ColumnType::Named(name) => crate::compiler::compile_retained_type_reference(name)?,
88 other => other.clone(),
89 };
90 prepare_attribute(
91 types, composites, definition, attribute, &declared, number, true,
92 )
93}
94
95fn prepare_attribute(
96 types: &dyn FunctionTypeResolver,
97 composites: &dyn crate::expr::composites::CompositeTypeCatalog,
98 definition: &crate::catalog::composite_type::StoredComposite,
99 attribute: &CompositeAttributeDefinition,
100 declared: &ColumnType,
101 number: i16,
102 check_setof: bool,
103) -> Result<StoredCompositeAttribute, SQLError> {
104 let ty = resolve_declared_column_type(types, declared)?;
105 types.require_type_usage(&ty)?;
106 let collation = attribute_collation(&ty, attribute.collation.as_deref())?;
107 if check_setof && attribute.setof {
108 return Err(SQLError::Routine {
109 sqlstate: "42P16".into(),
110 message: format!("column \"{}\" cannot be declared SETOF", attribute.name),
111 });
112 }
113 super::columns::validate_postgres_relation_column_type(&attribute.name, &ty)?;
114 if crate::expr::composites::type_contains_composite(&ty, definition.oid, composites)? {
115 return Err(SQLError::Routine {
116 sqlstate: "42P16".into(),
117 message: format!(
118 "composite type {} cannot be made a member of itself",
119 definition.identity.name
120 ),
121 });
122 }
123 Ok(StoredCompositeAttribute {
124 name: attribute.name.clone(),
125 ty,
126 collation,
127 number,
128 dropped: false,
129 })
130}
131
132pub const MAX_ATTRIBUTES: usize = 1600;
134
135#[must_use]
137pub fn type_is_collatable(ty: &ColumnType) -> bool {
138 match ty {
139 ColumnType::Text
140 | ColumnType::Varchar(_)
141 | ColumnType::Bpchar
142 | ColumnType::Character(_)
143 | ColumnType::Name
144 | ColumnType::PgNodeTree => true,
145 ColumnType::Domain { base, .. } | ColumnType::Array(base) => type_is_collatable(base),
146 _ => false,
147 }
148}
149
150pub use crate::catalog::collations::builtin_collation_oid;
151
152fn attribute_collation(
154 ty: &ColumnType,
155 collation: Option<&str>,
156) -> Result<Option<String>, SQLError> {
157 let Some(collation) = collation else {
158 return Ok(None);
159 };
160 let names = crate::compiler::parse_regobject_name(collation)
161 .ok_or_else(|| SQLError::Internal(format!("malformed collation name {collation}")))?;
162 let (schema, name) = match names.as_slice() {
163 [name] => (None, name.as_str()),
164 [schema, name] => (Some(schema.as_str()), name.as_str()),
165 _ => {
166 return Err(SQLError::Internal(format!(
167 "malformed collation name {collation}"
168 )))
169 }
170 };
171 if schema.is_some_and(|schema| schema != "pg_catalog") || builtin_collation_oid(name).is_none()
172 {
173 return Err(SQLError::Routine {
174 sqlstate: "42704".into(),
175 message: format!(
176 "collation \"{}\" for encoding \"UTF8\" does not exist",
177 names.join(".")
178 ),
179 });
180 }
181 if !type_is_collatable(ty) {
182 return Err(SQLError::Routine {
183 sqlstate: "42804".into(),
184 message: format!("collations are not supported by type {}", ty.display_name()),
185 });
186 }
187 Ok(Some(name.to_owned()))
188}
189
190pub fn prepare_composite_attributes(
192 types: &dyn FunctionTypeResolver,
193 attributes: &[CompositeAttributeDefinition],
194) -> Result<Vec<StoredCompositeAttribute>, SQLError> {
195 if attributes.len() > MAX_ATTRIBUTES {
196 return Err(SQLError::Routine {
197 sqlstate: "54011".into(),
198 message: format!("tables can have at most {MAX_ATTRIBUTES} columns"),
199 });
200 }
201 for (position, attribute) in attributes.iter().enumerate() {
202 if attributes[position + 1..]
203 .iter()
204 .any(|later| later.name == attribute.name)
205 {
206 return Err(SQLError::Routine {
207 sqlstate: "42701".into(),
208 message: format!("column \"{}\" specified more than once", attribute.name),
209 });
210 }
211 }
212 let mut prepared = Vec::with_capacity(attributes.len());
213 for (position, attribute) in attributes.iter().enumerate() {
214 let ty = resolve_declared_column_type(types, &attribute.ty)?;
215 types.require_type_usage(&ty)?;
216 let collation = attribute_collation(&ty, attribute.collation.as_deref())?;
217 if attribute.setof {
218 return Err(SQLError::Routine {
219 sqlstate: "42P16".into(),
220 message: format!("column \"{}\" cannot be declared SETOF", attribute.name),
221 });
222 }
223 prepared.push(StoredCompositeAttribute {
224 name: attribute.name.clone(),
225 ty,
226 collation,
227 number: i16::try_from(position + 1)
228 .map_err(|_| SQLError::Internal("composite attribute number overflow".into()))?,
229 dropped: false,
230 });
231 }
232 for attribute in &prepared {
233 super::columns::validate_postgres_relation_column_type(&attribute.name, &attribute.ty)?;
234 }
235 Ok(prepared)
236}
237
238#[cfg(test)]
239mod tests;