1use uqa_core::{
10 memory::{Produced, ProductionControl, ProductionString, ProductionVec},
11 ArrayValue, Value, ValueRetentionError,
12};
13
14use crate::error::{Result, SQLError};
15
16use super::cast_value_from_with_control;
17
18pub(super) fn binary_compatible_elements(
20 source: Option<&str>,
21 target: &str,
22 control: &ProductionControl<'_>,
23) -> Result<bool> {
24 let Some(source) = source else {
25 return Ok(true);
26 };
27 let source = crate::ColumnType::from_sql_name_with_control(source, control)?;
28 let target = crate::ColumnType::from_sql_name_with_control(target, control)?;
29 Ok(*source == *target
30 || crate::type_resolution::cast_catalog_entry_with_control(&source, &target, control)?
31 .is_some_and(|entry| entry.method == crate::type_resolution::CastMethod::Binary))
32}
33
34type ArrayDimensions = Produced<Vec<(i32, usize)>>;
35
36pub fn parse_pg_array_literal(text: &str) -> Result<ArrayValue> {
40 parse_pg_array_literal_with_control(text, &ProductionControl::uncontrolled())?
41 .into_uncontrolled()
42 .map_err(|_| SQLError::Internal("ordinary array parse owner".into()))
43}
44
45pub fn parse_pg_array_literal_with_control(
46 text: &str,
47 control: &ProductionControl<'_>,
48) -> Result<Produced<ArrayValue>> {
49 let mut parser = PgArrayLiteralParser::new(text, *control);
50 let (declared_dimensions, items) = parser.parse()?;
51 match array_shape_with_control(&items, control) {
52 Ok(shape) => drop(shape),
53 Err(ShapeProductionError::Shape(error)) => return Err(parser.error(error.message())),
54 Err(ShapeProductionError::Control(error)) => return Err(error.into()),
55 }
56 let array = match &declared_dimensions {
57 Some(declared) => {
58 let mut bounds = ProductionVec::new(*control);
59 bounds.reserve(declared.len())?;
60 for (lower, _) in &**declared {
61 bounds.push_copy(*lower)?;
62 }
63 ArrayValue::with_lower_bounds_with_control(items, bounds.finish()?, control)?
64 }
65 None => ArrayValue::try_new_with_control(items, control)?,
66 }
67 .ok_or_else(|| parser.error("specified array dimensions do not match array contents"))?;
68 if let Some(declared) = declared_dimensions {
69 if !declared
70 .iter()
71 .map(|(_, length)| *length)
72 .eq(array.dimensions().iter().copied())
73 {
74 return Err(parser.error("specified array dimensions do not match array contents"));
75 }
76 }
77 Ok(array)
78}
79
80pub(super) fn cast_array_elements(
81 items: &[Value],
82 element_type: &str,
83 source_element_type: Option<&str>,
84 control: &ProductionControl<'_>,
85) -> Result<Produced<Vec<Value>>> {
86 let mut output = ProductionVec::new(*control);
87 output.reserve(items.len())?;
88 for item in items {
89 let value = match item {
90 Value::List(nested) => {
91 let (nested, memory) =
92 cast_array_elements(nested, element_type, source_element_type, control)?
93 .into_parts();
94 control.finish(Value::List(nested), memory)?
95 }
96 other => {
97 cast_value_from_with_control(other, element_type, source_element_type, control)?
98 }
99 };
100 output.push_produced(value)?;
101 }
102 Ok(output.finish()?)
103}
104
105pub(super) struct PgArrayLiteralParser<'a, 'control> {
106 control: ProductionControl<'control>,
107 source: &'a str,
108 chars: std::iter::Peekable<std::str::Chars<'a>>,
109}
110
111type ParsedArrayLiteral = (Option<ArrayDimensions>, Produced<Vec<Value>>);
112
113impl<'a, 'control> PgArrayLiteralParser<'a, 'control> {
114 fn new(source: &'a str, control: ProductionControl<'control>) -> Self {
115 Self {
116 control,
117 source,
118 chars: source.chars().peekable(),
119 }
120 }
121
122 fn parse(&mut self) -> Result<ParsedArrayLiteral> {
123 self.skip_whitespace()?;
124 let dimensions = self.parse_dimension_declaration()?;
125 let items = self.parse_array()?;
126 self.skip_whitespace()?;
127 if self.chars.peek().is_some() {
128 return Err(self.error("unexpected content after closing brace"));
129 }
130 Ok((dimensions, items))
131 }
132
133 fn parse_dimension_declaration(&mut self) -> Result<Option<ArrayDimensions>> {
134 if self.chars.peek() != Some(&'[') {
135 return Ok(None);
136 }
137 let mut dimensions = ProductionVec::new(self.control);
138 while self.chars.next_if_eq(&'[').is_some() {
139 self.skip_whitespace()?;
140 let lower = self.parse_dimension_bound()?;
141 self.skip_whitespace()?;
142 if self.chars.next() != Some(':') {
143 return Err(self.error("array dimension must contain `:`"));
144 }
145 self.skip_whitespace()?;
146 let upper = self.parse_dimension_bound()?;
147 self.skip_whitespace()?;
148 if self.chars.next() != Some(']') {
149 return Err(self.error("array dimension is missing a closing `]`"));
150 }
151 if upper == i32::MAX {
152 return Err(SQLError::Routine {
153 sqlstate: "54000".into(),
154 message: format!("array upper bound is too large: {upper}"),
155 });
156 }
157 if upper < lower {
158 return Err(SQLError::Routine {
159 sqlstate: "2202E".into(),
160 message: "upper bound cannot be less than lower bound".into(),
161 });
162 }
163 let length = i64::from(upper)
164 .checked_sub(i64::from(lower))
165 .and_then(|difference| difference.checked_add(1))
166 .and_then(|length| usize::try_from(length).ok())
167 .ok_or_else(|| self.error("array dimension is out of range"))?;
168 dimensions.push_copy((lower, length))?;
169 self.skip_whitespace()?;
170 }
171 if self.chars.next() != Some('=') {
172 return Err(self.error("array dimensions must be followed by `=`"));
173 }
174 self.skip_whitespace()?;
175 Ok(Some(dimensions.finish()?))
176 }
177
178 fn parse_dimension_bound(&mut self) -> Result<i32> {
179 let mut text = ProductionString::new(self.control);
180 if self
181 .chars
182 .peek()
183 .is_some_and(|character| matches!(character, '+' | '-'))
184 {
185 text.push(self.chars.next().expect("peeked array bound sign"))?;
186 }
187 while self.chars.peek().is_some_and(char::is_ascii_digit) {
188 text.push(self.chars.next().expect("peeked array bound digit"))?;
189 }
190 if text.is_empty() || matches!(&*text, "+" | "-") {
191 return Err(self.error("array dimension bound must be an integer"));
192 }
193 text.parse()
194 .map_err(|_| self.error("array dimension bound is out of range"))
195 }
196
197 fn parse_array(&mut self) -> Result<Produced<Vec<Value>>> {
198 if self.chars.next() != Some('{') {
199 return Err(SQLError::Diagnostic {
200 sqlstate: "22P02".into(),
201 message: format!("malformed array literal: \"{}\"", self.source),
202 detail: Some("Array value must start with \"{\" or dimension information.".into()),
203 hint: None,
204 });
205 }
206 self.skip_whitespace()?;
207 if self.chars.next_if_eq(&'}').is_some() {
208 return Ok(ProductionVec::new(self.control).finish()?);
209 }
210
211 let mut items = ProductionVec::new(self.control);
212 loop {
213 self.skip_whitespace()?;
214 items.push_produced(self.parse_element()?)?;
215 self.skip_whitespace()?;
216 match self.chars.next() {
217 Some(',') => {
218 self.skip_whitespace()?;
219 if matches!(self.chars.peek(), None | Some('}')) {
220 return Err(self.error("array contains a missing element"));
221 }
222 }
223 Some('}') => break,
224 Some(_) => {
225 return Err(self.error("array elements must be separated by commas"));
226 }
227 None => return Err(self.error("array is missing a closing `}`")),
228 }
229 }
230 Ok(items.finish()?)
231 }
232
233 fn parse_element(&mut self) -> Result<Produced<Value>> {
234 match self.chars.peek() {
235 Some('{') => {
236 let (value, memory) = self.parse_array()?.into_parts();
237 Ok(self.control.finish(Value::List(value), memory)?)
238 }
239 Some('"') => {
240 let (value, memory) = self.parse_quoted_element()?.into_parts();
241 Ok(self.control.finish(Value::Str(value), memory)?)
242 }
243 Some(',') | Some('}') | None => Err(self.error("array contains a missing element")),
244 Some(_) => self.parse_unquoted_element(),
245 }
246 }
247
248 fn parse_quoted_element(&mut self) -> Result<Produced<String>> {
249 let _opening_quote = self.chars.next();
250 let mut value = ProductionString::new(self.control);
251 loop {
252 match self.chars.next() {
253 Some('"') => return Ok(value.finish()?),
254 Some('\\') => value.push(
255 self.chars
256 .next()
257 .ok_or_else(|| self.error("quoted element ends with an escape"))?,
258 )?,
259 Some(character) => value.push(character)?,
260 None => return Err(self.error("array contains an unterminated quoted element")),
261 }
262 }
263 }
264
265 fn parse_unquoted_element(&mut self) -> Result<Produced<Value>> {
266 let mut value = ProductionString::new(self.control);
267 let mut significant_len = 0;
268 let mut was_escaped = false;
269 while let Some(character) = self.chars.peek().copied() {
270 match character {
271 ',' | '}' => break,
272 '{' | '"' => {
273 return Err(self.error("array contains an unescaped special character"));
274 }
275 '\\' => {
276 let _escape = self.chars.next();
277 let escaped = self
278 .chars
279 .next()
280 .ok_or_else(|| self.error("array element ends with an escape"))?;
281 value.push(escaped)?;
282 significant_len = value.len();
283 was_escaped = true;
284 }
285 _ => {
286 let _character = self.chars.next();
287 value.push(character)?;
288 if !character.is_whitespace() {
289 significant_len = value.len();
290 }
291 }
292 }
293 }
294 let value = value.finish()?;
295 let significant = &value[..significant_len];
296 if significant.is_empty() {
297 return Err(self.error("array contains a missing element"));
298 }
299 if !was_escaped && significant.eq_ignore_ascii_case("null") {
300 Ok(self
301 .control
302 .finish(Value::Null, self.control.empty_reservation())?)
303 } else {
304 let (mut value, memory) = value.into_parts();
305 value.truncate(significant_len);
306 Ok(self.control.finish(Value::Str(value), memory)?)
307 }
308 }
309
310 fn skip_whitespace(&mut self) -> Result<()> {
311 self.control.check()?;
312 while self
313 .chars
314 .next_if(|character| character.is_whitespace())
315 .is_some()
316 {
317 self.control.check()?;
318 }
319 Ok(())
320 }
321
322 fn error(&self, detail: &str) -> SQLError {
323 SQLError::Routine {
324 sqlstate: "22P02".into(),
325 message: format!("malformed array literal: \"{}\" ({detail})", self.source),
326 }
327 }
328}
329
330#[derive(Clone, Copy, Debug, PartialEq, Eq)]
331pub(super) enum ArrayShapeError {
332 MixedNesting,
333 MismatchedDimensions,
334}
335
336impl ArrayShapeError {
337 fn message(self) -> &'static str {
338 match self {
339 Self::MixedNesting => "cannot mix nested arrays and scalar elements",
340 Self::MismatchedDimensions => "multidimensional arrays must have matching dimensions",
341 }
342 }
343}
344
345pub(super) fn array_shape(items: &[Value]) -> std::result::Result<Vec<usize>, ArrayShapeError> {
346 match array_shape_with_control(items, &ProductionControl::uncontrolled()) {
347 Ok(shape) => Ok(shape.into_uncontrolled().expect("ordinary array shape")),
348 Err(ShapeProductionError::Shape(error)) => Err(error),
349 Err(ShapeProductionError::Control(_)) => unreachable!("ordinary shape production"),
350 }
351}
352
353enum ShapeProductionError {
354 Shape(ArrayShapeError),
355 Control(ValueRetentionError),
356}
357impl From<ValueRetentionError> for ShapeProductionError {
358 fn from(error: ValueRetentionError) -> Self {
359 Self::Control(error)
360 }
361}
362
363fn array_shape_with_control(
364 items: &[Value],
365 control: &ProductionControl<'_>,
366) -> std::result::Result<Produced<Vec<usize>>, ShapeProductionError> {
367 let mut dimensions = ProductionVec::new(*control);
368 dimensions.push_copy(items.len())?;
369 let mut nested_shape: Option<Produced<Vec<usize>>> = None;
370 let mut has_scalar = false;
371 for item in items {
372 control.check()?;
373 if let Value::List(nested) = item {
374 let shape = array_shape_with_control(nested, control)?;
375 if has_scalar {
376 return Err(ShapeProductionError::Shape(ArrayShapeError::MixedNesting));
377 }
378 if nested_shape
379 .as_ref()
380 .is_some_and(|expected| **expected != *shape)
381 {
382 return Err(ShapeProductionError::Shape(
383 ArrayShapeError::MismatchedDimensions,
384 ));
385 }
386 nested_shape = Some(shape);
387 } else {
388 if nested_shape.is_some() {
389 return Err(ShapeProductionError::Shape(ArrayShapeError::MixedNesting));
390 }
391 has_scalar = true;
392 }
393 }
394 if let Some(shape) = nested_shape {
395 for length in &*shape {
396 dimensions.push_copy(*length)?;
397 }
398 }
399 Ok(dimensions.finish()?)
400}
401
402pub fn array_dimensions(items: &[Value]) -> Result<Vec<usize>> {
407 array_shape(items).map_err(|error| SQLError::TypeMismatch(error.message().to_string()))
408}