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