Skip to main content

rupg_types/
array.rs

1//! Arrays in the text and the binary format, and `int2vector` and `oidvector`.
2//!
3//! A port of `array_in`, `array_out`, `array_recv` and `array_send` in `src/backend/utils/adt/arrayfuncs.c`. The functions are generic over the element: the caller gives the input, the output, the receive or the send function of the element type, with the typmod in the closure. The text input calls the element input in the order of the elements, so the first error is the error of PostgreSQL also when a later part of the string is malformed.
4//!
5//! Lifted from `crates/rudb-pgtypes/src/array.rs` of tamnd/rudb at f5f7065a (spec/04 section 4.9).
6
7use rupg_common::SqlState;
8
9use crate::binary::Recv;
10use crate::error::TypeError;
11use crate::number::{int_out, is_space, uint32_in_subr};
12use crate::oid;
13use crate::types::{Oid, format_type};
14
15/// The maximum number of dimensions of an array.
16pub const MAXDIM: usize = 6;
17
18/// The maximum number of elements of an array, `MaxArraySize`.
19pub const MAX_ARRAY_SIZE: usize = 134_217_727;
20
21/// The first OID that `initdb` gives to an object that is not in the catalog headers.
22const FIRST_GENBKI_OBJECT_ID: Oid = 10_000;
23
24/// One dimension of an array: the number of elements and the lower bound.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct ArrayDim {
27    pub len: i32,
28    pub lower: i32,
29}
30
31/// An array value. The elements are in row-major order, and `None` is a null element. An empty array has no dimensions.
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Array<T> {
34    pub dims: Vec<ArrayDim>,
35    pub values: Vec<Option<T>>,
36}
37
38impl<T> Array<T> {
39    pub fn empty() -> Array<T> {
40        Array { dims: Vec::new(), values: Vec::new() }
41    }
42
43    /// A one-dimensional array with the lower bound 1, or the empty array when there are no values.
44    pub fn one(values: Vec<Option<T>>) -> Array<T> {
45        if values.is_empty() {
46            return Array::empty();
47        }
48        let len = i32::try_from(values.len()).unwrap_or(i32::MAX);
49        Array { dims: vec![ArrayDim { len, lower: 1 }], values }
50    }
51
52    /// The array of an `int2vector` or an `oidvector`: one dimension with the lower bound 0, or the empty array when there are no values.
53    pub fn vector(values: Vec<Option<T>>) -> Array<T> {
54        if values.is_empty() {
55            return Array::empty();
56        }
57        let len = i32::try_from(values.len()).unwrap_or(i32::MAX);
58        Array { dims: vec![ArrayDim { len, lower: 0 }], values }
59    }
60}
61
62fn malformed(input: &str, detail: &str) -> TypeError {
63    let mut error = TypeError::new(
64        SqlState::INVALID_TEXT_REPRESENTATION,
65        format!("malformed array literal: \"{input}\""),
66    );
67    error.detail = Some(detail.to_string());
68    error
69}
70
71fn limit(message: String) -> TypeError {
72    TypeError::new(SqlState::PROGRAM_LIMIT_EXCEEDED, message)
73}
74
75fn too_large() -> TypeError {
76    limit(format!("array size exceeds the maximum allowed ({MAX_ARRAY_SIZE})"))
77}
78
79fn too_many_dimensions() -> TypeError {
80    limit(format!("number of array dimensions exceeds the maximum allowed ({MAXDIM})"))
81}
82
83fn binary(message: String) -> TypeError {
84    TypeError::new(SqlState::INVALID_BINARY_REPRESENTATION, message)
85}
86
87/// The number of elements of an array with these dimensions, `ArrayGetNItems`.
88pub(crate) fn item_count(dims: &[ArrayDim]) -> Result<usize, TypeError> {
89    let mut count = 1i32;
90    for dim in dims {
91        // A negative length is an upper bound that overflowed.
92        if dim.len < 0 {
93            return Err(too_large());
94        }
95        count = count.checked_mul(dim.len).ok_or_else(too_large)?;
96    }
97    let count = if dims.is_empty() { 0 } else { count as usize };
98    if count > MAX_ARRAY_SIZE {
99        return Err(too_large());
100    }
101    Ok(count)
102}
103
104/// `ReadDimensionInt`: an integer with an optional sign and no white space before it. The end is the start when there are no digits.
105fn dimension_int(b: &[u8], start: usize) -> Result<(i32, usize), TypeError> {
106    let at = |i: usize| b.get(i).copied().unwrap_or(0);
107    let mut i = start;
108    let neg = at(i) == b'-';
109    if neg || at(i) == b'+' {
110        i += 1;
111    }
112    let first = i;
113    let mut value = 0i64;
114    while at(i).is_ascii_digit() {
115        value = value.saturating_mul(10).saturating_add(i64::from(at(i) - b'0'));
116        i += 1;
117    }
118    if i == first {
119        return Ok((0, start));
120    }
121    let value = if neg { -value } else { value };
122    let value = i32::try_from(value)
123        .map_err(|_| limit("array bound is out of integer range".to_string()))?;
124    Ok((value, i))
125}
126
127/// A token of the part in braces, `ArrayToken`.
128enum Token {
129    Start,
130    End,
131    Delim,
132    Element,
133    Null,
134}
135
136/// The reader of the part in braces. The text of the last element is in `buf`.
137struct Reader<'a> {
138    input: &'a str,
139    b: &'a [u8],
140    at: usize,
141    delim: u8,
142    array_nulls: bool,
143    buf: Vec<u8>,
144}
145
146impl Reader<'_> {
147    fn byte(&self, i: usize) -> u8 {
148        self.b.get(i).copied().unwrap_or(0)
149    }
150
151    fn malformed(&self, detail: &str) -> TypeError {
152        malformed(self.input, detail)
153    }
154
155    fn unexpected(&self, c: u8) -> TypeError {
156        self.malformed(&format!("Unexpected \"{}\" character.", char::from(c)))
157    }
158
159    fn end(&self) -> TypeError {
160        self.malformed("Unexpected end of input.")
161    }
162
163    /// `ReadArrayToken`.
164    fn token(&mut self) -> Result<Token, TypeError> {
165        self.buf.clear();
166        let mut p = self.at;
167        loop {
168            match self.byte(p) {
169                0 => return Err(self.end()),
170                b'{' => {
171                    self.at = p + 1;
172                    return Ok(Token::Start);
173                }
174                b'}' => {
175                    self.at = p + 1;
176                    return Ok(Token::End);
177                }
178                b'"' => return self.quoted(p + 1),
179                c if c == self.delim => {
180                    self.at = p + 1;
181                    return Ok(Token::Delim);
182                }
183                c if is_space(c) => p += 1,
184                _ => return self.unquoted(p),
185            }
186        }
187    }
188
189    fn quoted(&mut self, mut p: usize) -> Result<Token, TypeError> {
190        loop {
191            match self.byte(p) {
192                0 => return Err(self.end()),
193                b'\\' => {
194                    p += 1;
195                    match self.byte(p) {
196                        0 => return Err(self.end()),
197                        c => self.buf.push(c),
198                    }
199                    p += 1;
200                }
201                b'"' => loop {
202                    // Only white space can come between the quote and the next token.
203                    p += 1;
204                    match self.byte(p) {
205                        0 => return Err(self.end()),
206                        c if c == self.delim || c == b'}' || c == b'{' => {
207                            self.at = p;
208                            return Ok(Token::Element);
209                        }
210                        c if !is_space(c) => {
211                            return Err(self.malformed("Incorrectly quoted array element."));
212                        }
213                        _ => {}
214                    }
215                },
216                c => {
217                    self.buf.push(c);
218                    p += 1;
219                }
220            }
221        }
222    }
223
224    fn unquoted(&mut self, mut p: usize) -> Result<Token, TypeError> {
225        // The white space at the end is not part of the element, and `keep` is the length without it.
226        let mut keep = 0;
227        let mut escapes = false;
228        loop {
229            match self.byte(p) {
230                0 => return Err(self.end()),
231                b'{' => return Err(self.unexpected(b'{')),
232                b'"' => return Err(self.malformed("Incorrectly quoted array element.")),
233                b'\\' => {
234                    p += 1;
235                    match self.byte(p) {
236                        0 => return Err(self.end()),
237                        c => self.buf.push(c),
238                    }
239                    p += 1;
240                    keep = self.buf.len();
241                    escapes = true;
242                }
243                c if c == self.delim || c == b'}' => {
244                    self.buf.truncate(keep);
245                    self.at = p;
246                    if self.array_nulls && !escapes && self.buf.eq_ignore_ascii_case(b"NULL") {
247                        return Ok(Token::Null);
248                    }
249                    return Ok(Token::Element);
250                }
251                c => {
252                    self.buf.push(c);
253                    if !is_space(c) {
254                        keep = self.buf.len();
255                    }
256                    p += 1;
257                }
258            }
259        }
260    }
261
262    /// The text of the last element. The reader removes only ASCII bytes from a UTF-8 string, and it cuts only after a byte that is not white space, so the text is UTF-8.
263    fn element(&self) -> &str {
264        std::str::from_utf8(&self.buf).unwrap_or_default()
265    }
266}
267
268/// The text input of an array: `{1,2,3}`, `{{1,2},{3,4}}` or `[0:1]={a,b}`. `delim` is the `typdelim` of the element type, and `array_nulls` is the setting of the same name: when it is off, an unquoted `NULL` is the string and not a null. `element` is the input function of the element type. It does not get the null elements.
269pub fn array_in<T>(
270    s: &str,
271    delim: u8,
272    array_nulls: bool,
273    mut element: impl FnMut(&str) -> Result<T, TypeError>,
274) -> Result<Array<T>, TypeError> {
275    let b = s.as_bytes();
276    let at = |i: usize| b.get(i).copied().unwrap_or(0);
277    let mut p = 0;
278
279    // ReadArrayDimensions: zero or more `[n]` or `[m:n]`, with white space between them.
280    let mut dims: Vec<ArrayDim> = Vec::new();
281    loop {
282        while is_space(at(p)) {
283            p += 1;
284        }
285        if at(p) != b'[' {
286            break;
287        }
288        p += 1;
289        if dims.len() >= MAXDIM {
290            return Err(too_many_dimensions());
291        }
292        let (first, end) = dimension_int(b, p)?;
293        if end == p {
294            return Err(malformed(
295                s,
296                "\"[\" must introduce explicitly-specified array dimensions.",
297            ));
298        }
299        p = end;
300        let (lower, upper) = if at(p) == b':' {
301            p += 1;
302            let (upper, end) = dimension_int(b, p)?;
303            if end == p {
304                return Err(malformed(s, "Missing array dimension value."));
305            }
306            p = end;
307            (first, upper)
308        } else {
309            (1, first)
310        };
311        if at(p) != b']' {
312            return Err(malformed(s, "Missing \"]\" after array dimensions."));
313        }
314        p += 1;
315        if upper < lower {
316            return Err(TypeError::new(
317                SqlState::ARRAY_SUBSCRIPT_ERROR,
318                "upper bound cannot be less than lower bound".to_string(),
319            ));
320        }
321        if upper == i32::MAX {
322            return Err(limit(format!("array upper bound is too large: {upper}")));
323        }
324        let len = upper.checked_sub(lower).and_then(|n| n.checked_add(1)).ok_or_else(too_large)?;
325        dims.push(ArrayDim { len, lower });
326    }
327    if dims.is_empty() {
328        if at(p) != b'{' {
329            return Err(malformed(
330                s,
331                "Array value must start with \"{\" or dimension information.",
332            ));
333        }
334    } else {
335        if at(p) != b'=' {
336            return Err(malformed(s, "Missing \"=\" after array dimensions."));
337        }
338        p += 1;
339        while is_space(at(p)) {
340            p += 1;
341        }
342        if at(p) != b'{' {
343            return Err(malformed(s, "Array contents must start with \"{\"."));
344        }
345    }
346
347    // ReadArrayStr: the elements, and the dimensions when the input does not give them.
348    let specified = !dims.is_empty();
349    let mut ndim = dims.len();
350    let mut lens = [-1i32; MAXDIM];
351    for (len, dim) in lens.iter_mut().zip(&dims) {
352        *len = dim.len;
353    }
354    let mut counts = [0i32; MAXDIM];
355    let mut values = Vec::new();
356    let mut level = 0;
357    let mut frozen = specified;
358    let mut expect_delim = false;
359    let mut reader = Reader { input: s, b, at: p, delim, array_nulls, buf: Vec::new() };
360    let dimension_error = || match specified {
361        true => malformed(s, "Specified array dimensions do not match array contents."),
362        false => {
363            malformed(s, "Multidimensional arrays must have sub-arrays with matching dimensions.")
364        }
365    };
366    loop {
367        match reader.token()? {
368            Token::Start => {
369                if expect_delim {
370                    return Err(reader.unexpected(b'{'));
371                }
372                if level >= MAXDIM {
373                    return Err(too_many_dimensions());
374                }
375                counts[level] = 0;
376                level += 1;
377                if level > ndim {
378                    if frozen {
379                        return Err(dimension_error());
380                    }
381                    ndim = level;
382                }
383            }
384            Token::End => {
385                // A right brace can end an empty sub-array. Else it must come where a delimiter can come.
386                if counts[level - 1] > 0 && !expect_delim {
387                    return Err(reader.unexpected(b'}'));
388                }
389                level -= 1;
390                if level > 0 {
391                    counts[level - 1] += 1;
392                }
393                if lens[level] < 0 {
394                    lens[level] = counts[level];
395                } else if counts[level] != lens[level] {
396                    return Err(dimension_error());
397                }
398                expect_delim = true;
399            }
400            Token::Delim => {
401                if !expect_delim {
402                    return Err(reader.unexpected(delim));
403                }
404                expect_delim = false;
405            }
406            token @ (Token::Element | Token::Null) => {
407                if expect_delim {
408                    return Err(reader.malformed("Unexpected array element."));
409                }
410                if values.len() >= MAX_ARRAY_SIZE {
411                    return Err(too_large());
412                }
413                values.push(match token {
414                    Token::Null => None,
415                    _ => Some(element(reader.element())?),
416                });
417                // After the first element the number of dimensions is fixed, and each element must be at the last level.
418                frozen = true;
419                if level != ndim {
420                    return Err(dimension_error());
421                }
422                counts[level - 1] += 1;
423                expect_delim = true;
424            }
425        }
426        if level == 0 {
427            break;
428        }
429    }
430    if b[reader.at..].iter().any(|&c| !is_space(c)) {
431        return Err(malformed(s, "Junk after closing right brace."));
432    }
433    if values.is_empty() {
434        return Ok(Array::empty());
435    }
436    let lowers = dims.iter().map(|dim| dim.lower).chain(std::iter::repeat(1));
437    let dims = lens[..ndim].iter().zip(lowers).map(|(&len, lower)| ArrayDim { len, lower });
438    Ok(Array { dims: dims.collect(), values })
439}
440
441/// The text output of an array. `element` is the output function of the element type. The function quotes an element that is empty, that is `NULL` in any case, or that has a quote, a backslash, a brace, the delimiter or white space.
442pub fn array_out<T>(
443    array: &Array<T>,
444    delim: u8,
445    out: &mut Vec<u8>,
446    mut element: impl FnMut(&T, &mut Vec<u8>),
447) {
448    let ndim = array.dims.len();
449    if array.values.is_empty() || ndim == 0 {
450        out.extend_from_slice(b"{}");
451        return;
452    }
453    if array.dims.iter().any(|dim| dim.lower != 1) {
454        for dim in &array.dims {
455            out.push(b'[');
456            int_out(dim.lower.into(), out);
457            out.push(b':');
458            int_out(i64::from(dim.lower) + i64::from(dim.len) - 1, out);
459            out.push(b']');
460        }
461        out.push(b'=');
462    }
463    out.push(b'{');
464    let mut index = [0i32; MAXDIM];
465    let mut open = 0;
466    for value in &array.values {
467        out.extend(std::iter::repeat_n(b'{', ndim - 1 - open));
468        match value {
469            None => out.extend_from_slice(b"NULL"),
470            Some(value) => {
471                // The element goes to the end of the output, and moves into quotes only when it needs them.
472                let start = out.len();
473                element(value, out);
474                let text = &out[start..];
475                let quote = text.is_empty()
476                    || text.eq_ignore_ascii_case(b"NULL")
477                    || text.iter().any(|&c| {
478                        matches!(c, b'"' | b'\\' | b'{' | b'}') || c == delim || is_space(c)
479                    });
480                if quote {
481                    let text = out.split_off(start);
482                    out.push(b'"');
483                    for c in text {
484                        if c == b'"' || c == b'\\' {
485                            out.push(b'\\');
486                        }
487                        out.push(c);
488                    }
489                    out.push(b'"');
490                }
491            }
492        }
493        // Close each dimension that is now full, and write the delimiter in the first that is not.
494        open = ndim;
495        for i in (0..ndim).rev() {
496            index[i] += 1;
497            if index[i] < array.dims[i].len {
498                out.push(delim);
499                open = i;
500                break;
501            }
502            index[i] = 0;
503            out.push(b'}');
504        }
505        if open == ndim {
506            break;
507        }
508    }
509}
510
511/// The binary input of an array. `elem` is the element type that the column or the parameter has, and `element` is the receive function of that type, which gets the bytes of one element.
512pub fn array_recv<'a, T>(
513    recv: &mut Recv<'a>,
514    elem: Oid,
515    mut element: impl FnMut(&mut Recv<'a>) -> Result<T, TypeError>,
516) -> Result<Array<T>, TypeError> {
517    let ndim = recv.i32()?;
518    if ndim < 0 {
519        return Err(binary(format!("invalid number of dimensions: {ndim}")));
520    }
521    if ndim as usize > MAXDIM {
522        return Err(limit(format!(
523            "number of array dimensions ({ndim}) exceeds the maximum allowed ({MAXDIM})"
524        )));
525    }
526    let flags = recv.i32()?;
527    if flags != 0 && flags != 1 {
528        return Err(binary("invalid array flags".to_string()));
529    }
530    // Only the OIDs of the built-in types are the same on each server, so a different OID is an error only when both are built-in. Else the data is read as the expected type.
531    let got = recv.u32()?;
532    if got != elem && got < FIRST_GENBKI_OBJECT_ID && elem < FIRST_GENBKI_OBJECT_ID {
533        return Err(TypeError::new(
534            SqlState::DATATYPE_MISMATCH,
535            format!(
536                "binary data has array element type {got} ({}) instead of expected {elem} ({})",
537                format_type(got),
538                format_type(elem)
539            ),
540        ));
541    }
542    let mut dims = Vec::with_capacity(ndim as usize);
543    for _ in 0..ndim {
544        let len = recv.i32()?;
545        let lower = recv.i32()?;
546        dims.push(ArrayDim { len, lower });
547    }
548    let count = item_count(&dims)?;
549    for dim in &dims {
550        if dim.len.checked_add(dim.lower).is_none() {
551            return Err(limit(format!("array lower bound is too large: {}", dim.lower)));
552        }
553    }
554    if count == 0 {
555        return Ok(Array::empty());
556    }
557    let mut values = Vec::with_capacity(count.min(recv.remaining() / 4));
558    for number in 1..=count {
559        let len = recv.i32()?;
560        if len < -1 || len > i32::try_from(recv.remaining()).unwrap_or(i32::MAX) {
561            return Err(binary("insufficient data left in message".to_string()));
562        }
563        if len == -1 {
564            values.push(None);
565            continue;
566        }
567        let mut item = Recv::new(recv.bytes(len as usize)?);
568        values.push(Some(element(&mut item)?));
569        if item.remaining() != 0 {
570            return Err(binary(format!("improper binary format in array element {number}")));
571        }
572    }
573    Ok(Array { dims, values })
574}
575
576/// The binary output of an array. `elem` is the OID of the element type, and `element` is the send function of that type.
577pub fn array_send<T>(
578    array: &Array<T>,
579    elem: Oid,
580    out: &mut Vec<u8>,
581    mut element: impl FnMut(&T, &mut Vec<u8>),
582) {
583    let ndim = i32::try_from(array.dims.len()).unwrap_or(i32::MAX);
584    out.extend_from_slice(&ndim.to_be_bytes());
585    let nulls = array.values.iter().any(Option::is_none);
586    out.extend_from_slice(&i32::from(nulls).to_be_bytes());
587    out.extend_from_slice(&elem.to_be_bytes());
588    for dim in &array.dims {
589        out.extend_from_slice(&dim.len.to_be_bytes());
590        out.extend_from_slice(&dim.lower.to_be_bytes());
591    }
592    for value in &array.values {
593        let Some(value) = value else {
594            out.extend_from_slice(&(-1i32).to_be_bytes());
595            continue;
596        };
597        let at = out.len();
598        out.extend_from_slice(&[0; 4]);
599        element(value, out);
600        let len = i32::try_from(out.len() - at - 4).unwrap_or(i32::MAX);
601        out[at..at + 4].copy_from_slice(&len.to_be_bytes());
602    }
603}
604
605/// The text input of `int2vector`: integers between white space. The input reads each number with `strtol`, and an error shows the rest of the string from the bad number.
606pub fn int2vector_in(s: &str) -> Result<Vec<i16>, TypeError> {
607    let b = s.as_bytes();
608    let at = |i: usize| b.get(i).copied().unwrap_or(0);
609    let mut values = Vec::new();
610    let mut p = 0;
611    loop {
612        while is_space(at(p)) {
613            p += 1;
614        }
615        if p == b.len() {
616            return Ok(values);
617        }
618        let rest = &s[p..];
619        let mut i = p;
620        let neg = at(i) == b'-';
621        if neg || at(i) == b'+' {
622            i += 1;
623        }
624        let first = i;
625        let mut value = 0i64;
626        let mut overflow = false;
627        while at(i).is_ascii_digit() {
628            let digit = i64::from(at(i) - b'0');
629            match value.checked_mul(10).and_then(|v| v.checked_add(digit)) {
630                Some(v) => value = v,
631                None => overflow = true,
632            }
633            i += 1;
634        }
635        if i == first {
636            return Err(TypeError::syntax("smallint", rest));
637        }
638        let value = match i16::try_from(if neg { -value } else { value }) {
639            Ok(value) if !overflow => value,
640            _ => return Err(TypeError::range(rest, "smallint")),
641        };
642        if i < b.len() && at(i) != b' ' {
643            return Err(TypeError::syntax("smallint", rest));
644        }
645        values.push(value);
646        p = i;
647    }
648}
649
650/// The text output of `int2vector`: the numbers with a space between them.
651pub fn int2vector_out(values: &[i16], out: &mut Vec<u8>) {
652    for (i, &value) in values.iter().enumerate() {
653        if i > 0 {
654            out.push(b' ');
655        }
656        int_out(value.into(), out);
657    }
658}
659
660/// The binary input of `int2vector`: an array of `int2` with one dimension, the lower bound 0 and no null. An empty value is an error, because it has no dimension.
661pub fn int2vector_recv(recv: &mut Recv<'_>) -> Result<Vec<i16>, TypeError> {
662    vector_recv(recv, oid::INT2, Recv::i16, "int2vector")
663}
664
665/// The binary output of `int2vector`.
666pub fn int2vector_send(values: &[i16], out: &mut Vec<u8>) {
667    vector_send(values, oid::INT2, out, |v, out| out.extend_from_slice(&v.to_be_bytes()));
668}
669
670/// The text input of `oidvector`. Each number is read as the input of `oid` reads it, but the next number can follow with no space.
671pub fn oidvector_in(s: &str) -> Result<Vec<u32>, TypeError> {
672    let b = s.as_bytes();
673    let mut values = Vec::new();
674    let mut p = 0;
675    loop {
676        while p < b.len() && is_space(b[p]) {
677            p += 1;
678        }
679        if p == b.len() {
680            return Ok(values);
681        }
682        let (value, len) = uint32_in_subr(&s[p..], "oid")?;
683        values.push(value);
684        p += len;
685    }
686}
687
688/// The text output of `oidvector`.
689pub fn oidvector_out(values: &[u32], out: &mut Vec<u8>) {
690    for (i, &value) in values.iter().enumerate() {
691        if i > 0 {
692            out.push(b' ');
693        }
694        int_out(value.into(), out);
695    }
696}
697
698/// The binary input of `oidvector`, with the rules of [`int2vector_recv`].
699pub fn oidvector_recv(recv: &mut Recv<'_>) -> Result<Vec<u32>, TypeError> {
700    vector_recv(recv, oid::OID, Recv::u32, "oidvector")
701}
702
703/// The binary output of `oidvector`.
704pub fn oidvector_send(values: &[u32], out: &mut Vec<u8>) {
705    vector_send(values, oid::OID, out, |v, out| out.extend_from_slice(&v.to_be_bytes()));
706}
707
708fn vector_recv<'a, T>(
709    recv: &mut Recv<'a>,
710    elem: Oid,
711    element: impl FnMut(&mut Recv<'a>) -> Result<T, TypeError>,
712    type_name: &str,
713) -> Result<Vec<T>, TypeError> {
714    let array = array_recv(recv, elem, element)?;
715    let valid = matches!(array.dims[..], [ArrayDim { lower: 0, .. }]);
716    if !valid || array.values.iter().any(Option::is_none) {
717        return Err(binary(format!("invalid {type_name} data")));
718    }
719    Ok(array.values.into_iter().flatten().collect())
720}
721
722fn vector_send<T: Copy>(
723    values: &[T],
724    elem: Oid,
725    out: &mut Vec<u8>,
726    mut element: impl FnMut(T, &mut Vec<u8>),
727) {
728    // A vector has one dimension also when it is empty.
729    let len = i32::try_from(values.len()).unwrap_or(i32::MAX);
730    for word in [1, 0, elem as i32, len, 0] {
731        out.extend_from_slice(&word.to_be_bytes());
732    }
733    for &value in values {
734        let at = out.len();
735        out.extend_from_slice(&[0; 4]);
736        element(value, out);
737        let len = i32::try_from(out.len() - at - 4).unwrap_or(i32::MAX);
738        out[at..at + 4].copy_from_slice(&len.to_be_bytes());
739    }
740}
741
742#[cfg(test)]
743mod tests {
744    use super::*;
745
746    fn int4s(s: &str) -> Result<Array<i32>, TypeError> {
747        array_in(s, b',', true, crate::int4_in)
748    }
749
750    fn text(array: &Array<String>) -> String {
751        let mut out = Vec::new();
752        array_out(array, b',', &mut out, |v, out| out.extend_from_slice(v.as_bytes()));
753        String::from_utf8(out).unwrap()
754    }
755
756    fn texts(s: &str) -> Array<String> {
757        array_in(s, b',', true, |v| Ok(v.to_string())).unwrap()
758    }
759
760    #[test]
761    fn the_text_form_reads_the_dimensions_from_the_braces() {
762        let array = int4s(" {{1,2},{3,NULL}} ").unwrap();
763        let dims = [ArrayDim { len: 2, lower: 1 }, ArrayDim { len: 2, lower: 1 }];
764        assert_eq!(array.dims, dims);
765        assert_eq!(array.values, [Some(1), Some(2), Some(3), None]);
766        assert_eq!(int4s("{}"), Ok(Array::empty()));
767        assert_eq!(int4s("{{},{}}"), Ok(Array::empty()));
768        let array = int4s("[0:1]={7,8}").unwrap();
769        assert_eq!(array.dims, [ArrayDim { len: 2, lower: 0 }]);
770
771        let detail = |s: &str| int4s(s).unwrap_err().detail.unwrap();
772        assert_eq!(
773            detail("{1,{2}}"),
774            "Multidimensional arrays must have sub-arrays with matching dimensions."
775        );
776        assert_eq!(detail("{1,}"), "Unexpected \"}\" character.");
777        assert_eq!(detail("{1,,2}"), "Unexpected \",\" character.");
778        assert_eq!(detail("{{1}{2}}"), "Unexpected \"{\" character.");
779        assert_eq!(
780            detail("{{1},2}"),
781            "Multidimensional arrays must have sub-arrays with matching dimensions."
782        );
783        assert_eq!(
784            detail("{{1},{2,3}}"),
785            "Multidimensional arrays must have sub-arrays with matching dimensions."
786        );
787        assert_eq!(detail("[1:2]={1}"), "Specified array dimensions do not match array contents.");
788        assert_eq!(detail("{1"), "Unexpected end of input.");
789        assert_eq!(detail("{1} x"), "Junk after closing right brace.");
790        assert_eq!(detail("{\"1\"x}"), "Incorrectly quoted array element.");
791        assert_eq!(detail("{\"1\" \"2\"}"), "Incorrectly quoted array element.");
792        assert_eq!(detail("{1\"2\"}"), "Incorrectly quoted array element.");
793        assert_eq!(detail("[1:2"), "Missing \"]\" after array dimensions.");
794        assert_eq!(detail("[1:]={1}"), "Missing array dimension value.");
795        assert_eq!(
796            detail("[x]={1}"),
797            "\"[\" must introduce explicitly-specified array dimensions."
798        );
799        assert_eq!(detail("[1]{1}"), "Missing \"=\" after array dimensions.");
800        assert_eq!(detail("[1]= 1"), "Array contents must start with \"{\".");
801        assert_eq!(detail("1"), "Array value must start with \"{\" or dimension information.");
802        assert_eq!(
803            int4s("{1 2}").unwrap_err().message,
804            "invalid input syntax for type integer: \"1 2\""
805        );
806        assert_eq!(int4s("[2:1]={}").unwrap_err().sqlstate.as_str(), "2202E");
807        assert_eq!(
808            int4s("[1:2147483647]={}").unwrap_err().message,
809            "array upper bound is too large: 2147483647"
810        );
811        assert_eq!(
812            int4s("[1:99999999999]={}").unwrap_err().message,
813            "array bound is out of integer range"
814        );
815        // The element input runs in order, so its error comes before the syntax error after it.
816        assert_eq!(
817            int4s("{x,}").unwrap_err().message,
818            "invalid input syntax for type integer: \"x\""
819        );
820        let deep = format!("{}1{}", "{".repeat(7), "}".repeat(7));
821        assert_eq!(int4s(&deep).unwrap_err().sqlstate.as_str(), "54000");
822    }
823
824    #[test]
825    fn the_output_quotes_only_what_the_input_needs() {
826        let array = texts(r#"{"", "NULL", a b, "x\"y", plain, NULL}"#);
827        assert_eq!(text(&array), r#"{"","NULL","a b","x\"y",plain,NULL}"#);
828        assert_eq!(texts(&text(&array)), array);
829        assert_eq!(text(&texts("[-1:0][2:2]={{a},{b}}")), "[-1:0][2:2]={{a},{b}}");
830        assert_eq!(text(&texts("{{a,b},{c,d}}")), "{{a,b},{c,d}}");
831        let off = array_in("{NULL}", b',', false, |v| Ok(v.to_string())).unwrap();
832        assert_eq!(off.values, [Some("NULL".to_string())]);
833    }
834
835    #[test]
836    fn the_binary_form_goes_back_to_the_same_array() {
837        let array = int4s("[0:2]={1,NULL,3}").unwrap();
838        let mut bytes = Vec::new();
839        array_send(&array, oid::INT4, &mut bytes, |v, out| out.extend_from_slice(&v.to_be_bytes()));
840        let back = array_recv(&mut Recv::new(&bytes), oid::INT4, Recv::i32).unwrap();
841        assert_eq!(back, array);
842
843        let error = array_recv(&mut Recv::new(&bytes), oid::INT2, Recv::i16).unwrap_err();
844        assert_eq!(
845            error.message,
846            "binary data has array element type 23 (integer) instead of expected 21 (smallint)"
847        );
848        let error = array_recv(&mut Recv::new(&bytes), oid::INT8, Recv::i64).unwrap_err();
849        assert_eq!(error.sqlstate.as_str(), "42804");
850        // Element 1 has four bytes, and a receive function that reads two leaves two.
851        let mut words = bytes.clone();
852        words[8..12].copy_from_slice(&oid::INT2.to_be_bytes());
853        let error = array_recv(&mut Recv::new(&words), oid::INT2, Recv::i16).unwrap_err();
854        assert_eq!(error.message, "improper binary format in array element 1");
855
856        let header =
857            |words: &[i32]| -> Vec<u8> { words.iter().flat_map(|w| w.to_be_bytes()).collect() };
858        let message = |bytes: Vec<u8>| {
859            array_recv(&mut Recv::new(&bytes), oid::INT4, Recv::i32).unwrap_err().message
860        };
861        assert_eq!(message(header(&[-1])), "invalid number of dimensions: -1");
862        assert_eq!(
863            message(header(&[7])),
864            "number of array dimensions (7) exceeds the maximum allowed (6)"
865        );
866        assert_eq!(message(header(&[1, 2])), "invalid array flags");
867        assert_eq!(
868            message(header(&[1, 0, 23, -1, 1])),
869            "array size exceeds the maximum allowed (134217727)"
870        );
871        assert_eq!(
872            message(header(&[1, 0, 23, 1, i32::MAX])),
873            "array lower bound is too large: 2147483647"
874        );
875        assert_eq!(message(header(&[1, 0, 23, 1, 1, 5, 0])), "insufficient data left in message");
876        let empty = header(&[1, 0, 23, 0, 1]);
877        assert_eq!(array_recv(&mut Recv::new(&empty), oid::INT4, Recv::i32), Ok(Array::empty()));
878    }
879
880    #[test]
881    fn a_vector_is_a_zero_based_array_with_spaces_in_the_text() {
882        assert_eq!(int2vector_in(" 1  -2 3 "), Ok(vec![1, -2, 3]));
883        assert_eq!(int2vector_in(""), Ok(vec![]));
884        assert_eq!(
885            int2vector_in("1 x").unwrap_err().message,
886            "invalid input syntax for type smallint: \"x\""
887        );
888        assert_eq!(
889            int2vector_in("1,2").unwrap_err().message,
890            "invalid input syntax for type smallint: \"1,2\""
891        );
892        assert_eq!(
893            int2vector_in("40000 1").unwrap_err().message,
894            "value \"40000 1\" is out of range for type smallint"
895        );
896        assert_eq!(oidvector_in("1-2 0x10"), Ok(vec![1, u32::MAX - 1, 16]));
897        assert_eq!(
898            oidvector_in("1 x").unwrap_err().message,
899            "invalid input syntax for type oid: \"x\""
900        );
901
902        let mut bytes = Vec::new();
903        int2vector_send(&[5, 6], &mut bytes);
904        assert_eq!(int2vector_recv(&mut Recv::new(&bytes)), Ok(vec![5, 6]));
905        let mut out = Vec::new();
906        int2vector_out(&[5, -6], &mut out);
907        assert_eq!(out, b"5 -6");
908
909        let mut empty = Vec::new();
910        oidvector_send(&[], &mut empty);
911        let error = oidvector_recv(&mut Recv::new(&empty)).unwrap_err();
912        assert_eq!(
913            (error.sqlstate.as_str(), error.message.as_str()),
914            ("22P03", "invalid oidvector data")
915        );
916    }
917}