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_oid = crate::catalog::collations::declarations::select(
107 types,
108 &ty,
109 attribute.collation.as_deref(),
110 )?;
111 let collation = attribute
112 .collation
113 .as_ref()
114 .and_then(|_| crate::catalog::collations::builtin_collation_name(i64::from(collation_oid)))
115 .map(str::to_owned);
116 if check_setof && attribute.setof {
117 return Err(SQLError::Routine {
118 sqlstate: "42P16".into(),
119 message: format!("column \"{}\" cannot be declared SETOF", attribute.name),
120 });
121 }
122 super::columns::validate_postgres_relation_column_type(&attribute.name, &ty)?;
123 if crate::expr::composites::type_contains_composite(&ty, definition.oid, composites)? {
124 return Err(SQLError::Routine {
125 sqlstate: "42P16".into(),
126 message: format!(
127 "composite type {} cannot be made a member of itself",
128 definition.identity.name
129 ),
130 });
131 }
132 Ok(StoredCompositeAttribute {
133 name: attribute.name.clone(),
134 ty,
135 collation,
136 collation_oid: Some(collation_oid),
137 number,
138 dropped: false,
139 })
140}
141
142pub const MAX_ATTRIBUTES: usize = 1600;
144
145#[must_use]
147pub fn type_is_collatable(ty: &ColumnType) -> bool {
148 match ty {
149 ColumnType::Text
150 | ColumnType::Varchar(_)
151 | ColumnType::Bpchar
152 | ColumnType::Character(_)
153 | ColumnType::Name
154 | ColumnType::PgNodeTree => true,
155 ColumnType::Domain { base, .. } | ColumnType::Array(base) => type_is_collatable(base),
156 _ => false,
157 }
158}
159
160pub use crate::catalog::collations::builtin_collation_oid;
161
162pub fn prepare_composite_attributes(
164 types: &dyn FunctionTypeResolver,
165 attributes: &[CompositeAttributeDefinition],
166) -> Result<Vec<StoredCompositeAttribute>, SQLError> {
167 if attributes.len() > MAX_ATTRIBUTES {
168 return Err(SQLError::Routine {
169 sqlstate: "54011".into(),
170 message: format!("tables can have at most {MAX_ATTRIBUTES} columns"),
171 });
172 }
173 for (position, attribute) in attributes.iter().enumerate() {
174 if attributes[position + 1..]
175 .iter()
176 .any(|later| later.name == attribute.name)
177 {
178 return Err(SQLError::Routine {
179 sqlstate: "42701".into(),
180 message: format!("column \"{}\" specified more than once", attribute.name),
181 });
182 }
183 }
184 let mut prepared = Vec::with_capacity(attributes.len());
185 for (position, attribute) in attributes.iter().enumerate() {
186 let ty = resolve_declared_column_type(types, &attribute.ty)?;
187 types.require_type_usage(&ty)?;
188 let collation_oid = crate::catalog::collations::declarations::select(
189 types,
190 &ty,
191 attribute.collation.as_deref(),
192 )?;
193 let collation = attribute
194 .collation
195 .as_ref()
196 .and_then(|_| {
197 crate::catalog::collations::builtin_collation_name(i64::from(collation_oid))
198 })
199 .map(str::to_owned);
200 if attribute.setof {
201 return Err(SQLError::Routine {
202 sqlstate: "42P16".into(),
203 message: format!("column \"{}\" cannot be declared SETOF", attribute.name),
204 });
205 }
206 prepared.push(StoredCompositeAttribute {
207 name: attribute.name.clone(),
208 ty,
209 collation,
210 collation_oid: Some(collation_oid),
211 number: i16::try_from(position + 1)
212 .map_err(|_| SQLError::Internal("composite attribute number overflow".into()))?,
213 dropped: false,
214 });
215 }
216 for attribute in &prepared {
217 super::columns::validate_postgres_relation_column_type(&attribute.name, &attribute.ty)?;
218 }
219 Ok(prepared)
220}
221
222#[cfg(test)]
223mod tests;