1use crate::{
10 ast::{AssignmentStep, AssignmentTarget},
11 ColumnType, SQLError,
12};
13use std::collections::BTreeMap;
14
15#[cfg(test)]
16mod tests;
17
18fn error(code: &str, message: String) -> SQLError {
19 SQLError::Routine {
20 sqlstate: code.into(),
21 message,
22 }
23}
24
25pub fn validate_assignment_type<E>(
27 target: &AssignmentTarget<E>,
28 declared: Option<&ColumnType>,
29) -> Result<(), SQLError> {
30 if declared.is_none() && !target.is_whole_column() {
31 array_assignment_type(target, &ColumnType::Named("unknown".into()))?;
32 }
33 Ok(())
34}
35
36pub fn validate_repeated_targets<'a, E: 'a>(
38 targets: impl IntoIterator<Item = &'a AssignmentTarget<E>>,
39 insert: bool,
40) -> Result<(), SQLError> {
41 let mut seen = BTreeMap::new();
42 for target in targets {
43 let whole = target.is_whole_column();
44 if let Some(previous) = seen.insert(&target.column, whole) {
45 if whole || previous {
46 return Err(if insert {
47 error(
48 "42701",
49 format!("column \"{}\" specified more than once", target.column),
50 )
51 } else {
52 error(
53 "42601",
54 format!("multiple assignments to same column \"{}\"", target.column),
55 )
56 });
57 }
58 }
59 }
60 Ok(())
61}
62
63pub fn array_assignment_type<E>(
65 target: &AssignmentTarget<E>,
66 declared: &ColumnType,
67) -> Result<ColumnType, SQLError> {
68 let mut base = declared;
69 while let ColumnType::Domain { base: inner, .. } = base {
70 base = inner;
71 }
72 if let Some(AssignmentStep::Field(field)) = target.indirection.first() {
73 return Err(error("42804", format!(
74 "cannot assign to field \"{field}\" of column \"{}\" because its type {} is not a composite type",
75 target.column, declared.sql_name()
76 )));
77 }
78 let array = match base {
79 ColumnType::Array(_) => base.clone(),
80 ColumnType::Int2Vector => ColumnType::Array(Box::new(ColumnType::SmallInteger)),
81 ColumnType::OidVector => ColumnType::Array(Box::new(ColumnType::Oid)),
82 _ => {
83 return Err(error(
84 "42804",
85 format!(
86 "cannot subscript type {} because it does not support subscripting",
87 declared.sql_name()
88 ),
89 ))
90 }
91 };
92 if let Some(AssignmentStep::Field(field)) = target
93 .indirection
94 .iter()
95 .find(|step| matches!(step, AssignmentStep::Field(_)))
96 {
97 let mut element = &array;
98 while let ColumnType::Array(inner) = element {
99 element = inner;
100 }
101 return Err(error("42804", format!(
102 "cannot assign to field \"{field}\" of column \"{}\" because its type {} is not a composite type",
103 target.column, element.sql_name()
104 )));
105 }
106 if target.indirection.len() > 6 {
107 return Err(error(
108 "54000",
109 format!(
110 "number of array dimensions ({}) exceeds the maximum allowed (6)",
111 target.indirection.len()
112 ),
113 ));
114 }
115 Ok(array)
116}
117
118pub enum AssignmentLevel<'a, E> {
120 Field(&'a str),
121 Subscripts(&'a [AssignmentStep<E>]),
122}
123
124impl<E> AssignmentLevel<'_, E> {
125 pub fn is_slice(&self) -> bool {
127 match self {
128 Self::Field(_) => false,
129 Self::Subscripts(steps) => steps
130 .iter()
131 .any(|step| matches!(step, AssignmentStep::Slice { .. })),
132 }
133 }
134}
135
136pub fn assignment_levels<E>(steps: &[AssignmentStep<E>]) -> Vec<AssignmentLevel<'_, E>> {
138 let mut levels = Vec::new();
139 let mut position = 0;
140 while position < steps.len() {
141 if let AssignmentStep::Field(field) = &steps[position] {
142 levels.push(AssignmentLevel::Field(field));
143 position += 1;
144 continue;
145 }
146 let start = position;
147 while position < steps.len() && !matches!(steps[position], AssignmentStep::Field(_)) {
148 position += 1;
149 }
150 levels.push(AssignmentLevel::Subscripts(&steps[start..position]));
151 }
152 levels
153}
154
155pub fn has_field_step<E>(target: &AssignmentTarget<E>) -> bool {
157 target
158 .indirection
159 .iter()
160 .any(|step| matches!(step, AssignmentStep::Field(_)))
161}
162
163pub fn field_assignment_types<E>(
165 target: &AssignmentTarget<E>,
166 declared: &ColumnType,
167 composites: Option<&dyn crate::expr::composites::CompositeTypeCatalog>,
168) -> Result<Vec<ColumnType>, SQLError> {
169 let mut types = vec![declared.clone()];
170 for level in assignment_levels(&target.indirection) {
171 let current = types.last().expect("the declared type starts the levels");
172 let mut base = current;
173 while let ColumnType::Domain { base: inner, .. } = base {
174 base = inner;
175 }
176 let next = match level {
177 AssignmentLevel::Field(field) => {
178 let ColumnType::Composite(reference) = base else {
179 return Err(error("42804", format!(
180 "cannot assign to field \"{field}\" of column \"{}\" because its type {} is not a composite type",
181 target.column, current.display_name()
182 )));
183 };
184 let descriptor = crate::expr::composites::descriptor(composites, reference.oid)?;
185 let Some((_, attribute)) = descriptor.attribute(field) else {
186 return Err(error("42703", format!(
187 "cannot assign to field \"{field}\" of column \"{}\" because there is no such column in data type {}",
188 target.column, current.display_name()
189 )));
190 };
191 attribute.ty.clone()
192 }
193 AssignmentLevel::Subscripts(steps) => {
194 let array = match base {
195 ColumnType::Array(_) => base.clone(),
196 ColumnType::Int2Vector => ColumnType::Array(Box::new(ColumnType::SmallInteger)),
197 ColumnType::OidVector => ColumnType::Array(Box::new(ColumnType::Oid)),
198 _ => {
199 return Err(error(
200 "42804",
201 format!(
202 "cannot subscript type {} because it does not support subscripting",
203 current.display_name()
204 ),
205 ))
206 }
207 };
208 if steps.len() > 6 {
209 return Err(error(
210 "54000",
211 format!(
212 "number of array dimensions ({}) exceeds the maximum allowed (6)",
213 steps.len()
214 ),
215 ));
216 }
217 if steps
218 .iter()
219 .any(|step| matches!(step, AssignmentStep::Slice { .. }))
220 {
221 array
222 } else {
223 let mut element = &array;
224 while let ColumnType::Array(inner) = element {
225 element = inner;
226 }
227 element.clone()
228 }
229 }
230 };
231 types.push(next);
232 }
233 Ok(types)
234}
235
236pub fn assignment_value_type<E>(
238 target: &AssignmentTarget<E>,
239 declared: &ColumnType,
240) -> Result<ColumnType, SQLError> {
241 if target.is_whole_column() {
242 return Ok(declared.clone());
243 }
244 let array = array_assignment_type(target, declared)?;
245 if target
246 .indirection
247 .iter()
248 .any(|step| matches!(step, AssignmentStep::Slice { .. }))
249 {
250 Ok(array)
251 } else {
252 let mut element = &array;
253 while let ColumnType::Array(inner) = element {
254 element = inner;
255 }
256 Ok(element.clone())
257 }
258}
259
260pub fn validate_assignment_source<E>(
262 target: &AssignmentTarget<E>,
263 required: &ColumnType,
264 source: Option<&ColumnType>,
265) -> Result<(), SQLError> {
266 if let Some(source) = source {
267 if !crate::assignment_type_compatible(source, required) {
268 let levels = assignment_levels(&target.indirection);
270 let name = levels
271 .iter()
272 .rev()
273 .find_map(|level| match level {
274 AssignmentLevel::Field(field) => Some(*field),
275 AssignmentLevel::Subscripts(_) => None,
276 })
277 .unwrap_or(&target.column);
278 let message = match levels.last() {
279 None => format!(
280 "column \"{}\" is of type {} but expression is of type {}",
281 target.column,
282 required.display_name(),
283 source.display_name()
284 ),
285 Some(AssignmentLevel::Field(field)) => format!(
286 "subfield \"{field}\" is of type {} but expression is of type {}",
287 required.display_name(),
288 source.display_name()
289 ),
290 Some(AssignmentLevel::Subscripts(_)) => format!(
291 "subscripted assignment to \"{name}\" requires type {} but expression is of type {}",
292 required.display_name(),
293 source.display_name()
294 ),
295 };
296 return Err(SQLError::Diagnostic {
297 sqlstate: "42804".into(),
298 message,
299 detail: None,
300 hint: Some("You will need to rewrite or cast the expression.".into()),
301 });
302 }
303 }
304 Ok(())
305}
306
307pub fn validate_assignment_result<E>(
309 target: &AssignmentTarget<E>,
310 declared: &ColumnType,
311) -> Result<(), SQLError> {
312 if target.is_whole_column() || has_field_step(target) {
314 return Ok(());
315 }
316 let container = array_assignment_type(target, declared)?;
317 if !crate::type_resolution::explicit_type_compatible(&container, declared) {
318 return Err(error(
319 "42846",
320 format!(
321 "cannot cast type {} to {}",
322 container.sql_name(),
323 declared.sql_name()
324 ),
325 ));
326 }
327 Ok(())
328}
329
330pub fn validate_assignment_default<E>(target: &AssignmentTarget<E>) -> Result<(), SQLError> {
331 match target.indirection.first() {
332 None => Ok(()),
333 Some(AssignmentStep::Field(_)) => {
334 Err(error("0A000", "cannot set a subfield to DEFAULT".into()))
335 }
336 Some(_) => Err(error(
337 "0A000",
338 "cannot set an array element to DEFAULT".into(),
339 )),
340 }
341}