uqa_sql/assignment/
targets.rs1use 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 fn assignment_value_type<E>(
120 target: &AssignmentTarget<E>,
121 declared: &ColumnType,
122) -> Result<ColumnType, SQLError> {
123 if target.is_whole_column() {
124 return Ok(declared.clone());
125 }
126 let array = array_assignment_type(target, declared)?;
127 if target
128 .indirection
129 .iter()
130 .any(|step| matches!(step, AssignmentStep::Slice { .. }))
131 {
132 Ok(array)
133 } else {
134 let mut element = &array;
135 while let ColumnType::Array(inner) = element {
136 element = inner;
137 }
138 Ok(element.clone())
139 }
140}
141
142pub fn validate_assignment_source<E>(
144 target: &AssignmentTarget<E>,
145 required: &ColumnType,
146 source: Option<&ColumnType>,
147) -> Result<(), SQLError> {
148 if let Some(source) = source {
149 if !crate::assignment_type_compatible(source, required) {
150 let message = if target.is_whole_column() {
151 format!(
152 "column \"{}\" is of type {} but expression is of type {}",
153 target.column,
154 required.sql_name(),
155 source.sql_name()
156 )
157 } else {
158 format!("subscripted assignment to \"{}\" requires type {} but expression is of type {}", target.column, required.sql_name(), source.sql_name())
159 };
160 return Err(SQLError::Diagnostic {
161 sqlstate: "42804".into(),
162 message,
163 detail: None,
164 hint: Some("You will need to rewrite or cast the expression.".into()),
165 });
166 }
167 }
168 Ok(())
169}
170
171pub fn validate_assignment_result<E>(
173 target: &AssignmentTarget<E>,
174 declared: &ColumnType,
175) -> Result<(), SQLError> {
176 if target.is_whole_column() {
177 return Ok(());
178 }
179 let container = array_assignment_type(target, declared)?;
180 if !crate::type_resolution::explicit_type_compatible(&container, declared) {
181 return Err(error(
182 "42846",
183 format!(
184 "cannot cast type {} to {}",
185 container.sql_name(),
186 declared.sql_name()
187 ),
188 ));
189 }
190 Ok(())
191}
192
193pub fn validate_assignment_default<E>(target: &AssignmentTarget<E>) -> Result<(), SQLError> {
194 match target.indirection.first() {
195 None => Ok(()),
196 Some(AssignmentStep::Field(_)) => {
197 Err(error("0A000", "cannot set a subfield to DEFAULT".into()))
198 }
199 Some(_) => Err(error(
200 "0A000",
201 "cannot set an array element to DEFAULT".into(),
202 )),
203 }
204}