Skip to main content

rupg_types/
arrayset.rs

1//! The assignment to an element or a slice of an array, as `a[2] := 5` and `a[1:2] := ARRAY[7, 8]` make it.
2//!
3//! A port of `array_set_element` and `array_set_slice` in `src/backend/utils/adt/arrayfuncs.c`. The caller gives the empty array for a null array, as `array_subscript_assign` of `arraysubs.c` does. An array of fixed length, such as `name`, is not supported here.
4
5use rupg_common::SqlState;
6
7use crate::array::{Array, ArrayDim, MAX_ARRAY_SIZE, MAXDIM, item_count};
8use crate::error::TypeError;
9
10fn subscript_error(message: &str) -> TypeError {
11    TypeError::new(SqlState::ARRAY_SUBSCRIPT_ERROR, message.to_string())
12}
13
14fn too_large() -> TypeError {
15    TypeError::new(
16        SqlState::PROGRAM_LIMIT_EXCEEDED,
17        format!("array size exceeds the maximum allowed ({MAX_ARRAY_SIZE})"),
18    )
19}
20
21/// `ArrayCheckBounds`: the upper bound of each dimension must be in the range of `integer`.
22fn check_bounds(dims: &[ArrayDim]) -> Result<(), TypeError> {
23    for dim in dims {
24        if dim.len.checked_add(dim.lower).is_none() {
25            return Err(TypeError::new(
26                SqlState::PROGRAM_LIMIT_EXCEEDED,
27                format!("array lower bound is too large: {}", dim.lower),
28            ));
29        }
30    }
31    Ok(())
32}
33
34/// The dimension from `lower` to `end`, where `end` is after the last element. A dimension with more elements than an array can have is an error.
35fn dim_between(lower: i64, end: i64) -> Result<ArrayDim, TypeError> {
36    let len = end - lower;
37    if len > MAX_ARRAY_SIZE as i64 {
38        return Err(too_large());
39    }
40    let (Ok(len), Ok(lower)) = (i32::try_from(len), i32::try_from(lower)) else {
41        return Err(too_large());
42    };
43    Ok(ArrayDim { len, lower })
44}
45
46/// The offset in the storage of the element at the positions `at`, which count from 0 in each dimension.
47fn offset(dims: &[ArrayDim], at: &[usize]) -> usize {
48    dims.iter().zip(at).fold(0, |offset, (dim, &i)| offset * dim.len as usize + i)
49}
50
51impl<T> Array<T> {
52    /// `array_set_element`: the array with `value` at the subscripts. The empty array becomes an array with one element at the subscripts. A one-dimensional array grows with null elements to reach a subscript outside it, and another array gives an error for a subscript outside it.
53    ///
54    /// # Errors
55    ///
56    /// `2202E` for a number of subscripts that is not the number of dimensions and for a subscript outside an array of more than one dimension, and `54000` for an array that becomes too large.
57    pub fn set_element(self, subscripts: &[i32], value: Option<T>) -> Result<Array<T>, TypeError> {
58        let n = subscripts.len();
59        if n == 0 || n > MAXDIM {
60            return Err(subscript_error("wrong number of array subscripts"));
61        }
62        let Array { mut dims, mut values } = self;
63        if dims.is_empty() {
64            // `construct_md_array`: one element, with the subscripts as the lower bounds.
65            let dims: Vec<_> = subscripts.iter().map(|&lower| ArrayDim { len: 1, lower }).collect();
66            check_bounds(&dims)?;
67            return Ok(Array { dims, values: vec![value] });
68        }
69        if dims.len() != n {
70            return Err(subscript_error("wrong number of array subscripts"));
71        }
72        if n == 1 {
73            let dim = dims[0];
74            let (lower, end) = (i64::from(dim.lower), i64::from(dim.lower) + i64::from(dim.len));
75            let at = i64::from(subscripts[0]);
76            let grown = dim_between(lower.min(at), end.max(at + 1))?;
77            check_bounds(&[grown])?;
78            let before = (lower - i64::from(grown.lower)) as usize;
79            let after = grown.len as usize - before - dim.len as usize;
80            values.splice(0..0, std::iter::repeat_with(|| None).take(before));
81            values.extend(std::iter::repeat_with(|| None).take(after));
82            dims[0] = grown;
83            values[(at - i64::from(grown.lower)) as usize] = value;
84            return Ok(Array { dims, values });
85        }
86        let mut at = Vec::with_capacity(n);
87        for (dim, &i) in dims.iter().zip(subscripts) {
88            let step = i64::from(i) - i64::from(dim.lower);
89            if step < 0 || step >= i64::from(dim.len) {
90                return Err(subscript_error("array subscript out of range"));
91            }
92            at.push(step as usize);
93        }
94        let place = offset(&dims, &at);
95        values[place] = value;
96        Ok(Array { dims, values })
97    }
98
99    /// `array_set_slice`: the array with the elements of `source` in the slice between the bounds, in the order of storage. A bound that the query does not give is the bound of the array, and a dimension with no subscripts is the full dimension. The empty array becomes an array with the dimensions of the slice, whose bounds must all be given. A one-dimensional array grows with null elements to reach the slice. The elements of `source` after the elements that the slice needs do not count.
100    ///
101    /// # Errors
102    ///
103    /// `2202E` for more subscripts than dimensions, for a missing bound of a slice of the empty array, for an upper bound under its lower bound, for a bound outside an array of more than one dimension and for a source with too few elements, and `54000` for an array that becomes too large.
104    pub fn set_slice(
105        self,
106        lower: &[Option<i32>],
107        upper: &[Option<i32>],
108        source: Array<T>,
109    ) -> Result<Array<T>, TypeError> {
110        let n = upper.len();
111        let Array { mut dims, mut values } = self;
112        let source = source.values;
113        if dims.is_empty() {
114            let mut dims = Vec::with_capacity(n);
115            for (lo, up) in lower.iter().zip(upper) {
116                let (Some(lo), Some(up)) = (lo, up) else {
117                    let mut error =
118                        subscript_error("array slice subscript must provide both boundaries");
119                    error.detail = Some(
120                        "When assigning to a slice of an empty array value, slice boundaries must be fully specified."
121                            .to_string(),
122                    );
123                    return Err(error);
124                };
125                let len = i64::from(*up) - i64::from(*lo) + 1;
126                let len = i32::try_from(len).map_err(|_| too_large())?;
127                dims.push(ArrayDim { len, lower: *lo });
128            }
129            let count = item_count(&dims)?;
130            if source.len() < count {
131                return Err(subscript_error("source array too small"));
132            }
133            check_bounds(&dims)?;
134            if count == 0 {
135                return Ok(Array::empty());
136            }
137            return Ok(Array { dims, values: source.into_iter().take(count).collect() });
138        }
139        if dims.len() < n {
140            return Err(subscript_error("wrong number of array subscripts"));
141        }
142        let mut ranges = Vec::with_capacity(dims.len());
143        for (i, dim) in dims.iter().enumerate() {
144            let (first, end) = (i64::from(dim.lower), i64::from(dim.lower) + i64::from(dim.len));
145            let lo = lower.get(i).copied().flatten().map_or(first, i64::from);
146            let up = upper.get(i).copied().flatten().map_or(end - 1, i64::from);
147            if lo > up {
148                return Err(subscript_error("upper bound cannot be less than lower bound"));
149            }
150            if dims.len() > 1 && (lo < first || up >= end) {
151                return Err(subscript_error("array subscript out of range"));
152            }
153            ranges.push((lo, up));
154        }
155        if dims.len() == 1 {
156            let dim = dims[0];
157            let (lo, up) = ranges[0];
158            let (lower, end) = (i64::from(dim.lower), i64::from(dim.lower) + i64::from(dim.len));
159            let grown = dim_between(lower.min(lo), end.max(up + 1))?;
160            check_bounds(&[grown])?;
161            let span = (up - lo + 1) as usize;
162            if source.len() < span {
163                return Err(subscript_error("source array too small"));
164            }
165            let before = (lower - i64::from(grown.lower)) as usize;
166            let after = grown.len as usize - before - dim.len as usize;
167            values.splice(0..0, std::iter::repeat_with(|| None).take(before));
168            values.extend(std::iter::repeat_with(|| None).take(after));
169            dims[0] = grown;
170            let start = (lo - i64::from(grown.lower)) as usize;
171            values.splice(start..start + span, source.into_iter().take(span));
172            return Ok(Array { dims, values });
173        }
174        let span: usize = ranges.iter().map(|&(lo, up)| (up - lo + 1) as usize).product();
175        if source.len() < span {
176            return Err(subscript_error("source array too small"));
177        }
178        // The positions of the slice from 0 in each dimension, in the order of storage.
179        let mut at: Vec<usize> = ranges
180            .iter()
181            .zip(&dims)
182            .map(|(&(lo, _), dim)| (lo - i64::from(dim.lower)) as usize)
183            .collect();
184        for item in source.into_iter().take(span) {
185            let place = offset(&dims, &at);
186            values[place] = item;
187            for level in (0..at.len()).rev() {
188                let (lo, up) = ranges[level];
189                let first = (lo - i64::from(dims[level].lower)) as usize;
190                if at[level] < (up - i64::from(dims[level].lower)) as usize {
191                    at[level] += 1;
192                    break;
193                }
194                at[level] = first;
195            }
196        }
197        Ok(Array { dims, values })
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    fn one(values: &[i32]) -> Array<i32> {
206        Array::one(values.iter().copied().map(Some).collect())
207    }
208
209    fn grid(rows: &[&[i32]]) -> Array<i32> {
210        let len = rows[0].len() as i32;
211        let dims = vec![ArrayDim { len: rows.len() as i32, lower: 1 }, ArrayDim { len, lower: 1 }];
212        Array { dims, values: rows.iter().flat_map(|r| r.iter().copied().map(Some)).collect() }
213    }
214
215    fn values(array: &Array<i32>) -> Vec<Option<i32>> {
216        array.values.clone()
217    }
218
219    #[test]
220    fn element() {
221        let a = one(&[1, 2, 3]).set_element(&[5], Some(5)).unwrap();
222        assert_eq!(a.dims, vec![ArrayDim { len: 5, lower: 1 }]);
223        assert_eq!(values(&a), vec![Some(1), Some(2), Some(3), None, Some(5)]);
224        let a = one(&[1]).set_element(&[-1], Some(9)).unwrap();
225        assert_eq!(a.dims, vec![ArrayDim { len: 3, lower: -1 }]);
226        assert_eq!(values(&a), vec![Some(9), None, Some(1)]);
227        let a = Array::empty().set_element(&[2, 3], Some(7)).unwrap();
228        let dims = vec![ArrayDim { len: 1, lower: 2 }, ArrayDim { len: 1, lower: 3 }];
229        assert_eq!(a.dims, dims);
230        let a = grid(&[&[1, 2], &[3, 4]]).set_element(&[2, 1], Some(30)).unwrap();
231        assert_eq!(values(&a), vec![Some(1), Some(2), Some(30), Some(4)]);
232        let out = grid(&[&[1, 2], &[3, 4]]).set_element(&[3, 1], Some(5)).unwrap_err();
233        assert_eq!(out.message, "array subscript out of range");
234        let out = one(&[1, 2]).set_element(&[1, 1], Some(5)).unwrap_err();
235        assert_eq!(out.message, "wrong number of array subscripts");
236        let out = one(&[1, 2, 3]).set_element(&[i32::MAX], Some(1)).unwrap_err();
237        assert_eq!(out.sqlstate, SqlState::PROGRAM_LIMIT_EXCEEDED);
238        let out = Array::empty().set_element(&[i32::MAX], Some(1)).unwrap_err();
239        assert_eq!(out.message, "array lower bound is too large: 2147483647");
240    }
241
242    #[test]
243    fn slice() {
244        let a = one(&[1, 2, 3]).set_slice(&[Some(5)], &[Some(6)], one(&[50, 60])).unwrap();
245        assert_eq!(values(&a), vec![Some(1), Some(2), Some(3), None, Some(50), Some(60)]);
246        let a = one(&[1, 2, 3]).set_slice(&[None], &[Some(2)], one(&[7, 8, 9])).unwrap();
247        assert_eq!(values(&a), vec![Some(7), Some(8), Some(3)]);
248        let a = Array::empty().set_slice(&[Some(2)], &[Some(3)], one(&[1, 2])).unwrap();
249        assert_eq!(a.dims, vec![ArrayDim { len: 2, lower: 2 }]);
250        let a = Array::empty().set_slice(&[Some(3)], &[Some(2)], one(&[1, 2])).unwrap();
251        assert!(a.dims.is_empty());
252        let out = Array::empty().set_slice(&[None], &[Some(3)], one(&[1])).unwrap_err();
253        assert_eq!(out.message, "array slice subscript must provide both boundaries");
254        let out = one(&[1, 2, 3]).set_slice(&[Some(1)], &[Some(3)], one(&[1, 2])).unwrap_err();
255        assert_eq!(out.message, "source array too small");
256        let out = one(&[1, 2, 3]).set_slice(&[Some(3)], &[Some(1)], one(&[1, 2])).unwrap_err();
257        assert_eq!(out.message, "upper bound cannot be less than lower bound");
258        let a = grid(&[&[1, 2, 3], &[4, 5, 6]]);
259        let a = a.set_slice(&[Some(1), Some(2)], &[Some(2), Some(3)], one(&[20, 30, 50, 60]));
260        let a = a.unwrap();
261        let expect = [1, 20, 30, 4, 50, 60].map(Some).to_vec();
262        assert_eq!(values(&a), expect);
263        let a = a.set_slice(&[Some(2)], &[Some(2)], one(&[-1, -2, -3])).unwrap();
264        assert_eq!(values(&a), [1, 20, 30, -1, -2, -3].map(Some).to_vec());
265        let out = a.set_slice(&[Some(1), Some(1)], &[Some(3), Some(1)], one(&[1, 2, 3]));
266        assert_eq!(out.unwrap_err().message, "array subscript out of range");
267    }
268}