/src/postgres/src/backend/utils/adt/arrayutils.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * arrayutils.c |
4 | | * This file contains some support routines required for array functions. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/utils/adt/arrayutils.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | |
16 | | #include "postgres.h" |
17 | | |
18 | | #include "catalog/pg_type.h" |
19 | | #include "common/int.h" |
20 | | #include "utils/array.h" |
21 | | #include "utils/builtins.h" |
22 | | #include "utils/memutils.h" |
23 | | |
24 | | |
25 | | /* |
26 | | * Convert subscript list into linear element number (from 0) |
27 | | * |
28 | | * We assume caller has already range-checked the dimensions and subscripts, |
29 | | * so no overflow is possible. |
30 | | */ |
31 | | int |
32 | | ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx) |
33 | 0 | { |
34 | 0 | int i, |
35 | 0 | scale = 1, |
36 | 0 | offset = 0; |
37 | |
|
38 | 0 | for (i = n - 1; i >= 0; i--) |
39 | 0 | { |
40 | 0 | offset += (indx[i] - lb[i]) * scale; |
41 | 0 | scale *= dim[i]; |
42 | 0 | } |
43 | 0 | return offset; |
44 | 0 | } |
45 | | |
46 | | /* |
47 | | * Convert array dimensions into number of elements |
48 | | * |
49 | | * This must do overflow checking, since it is used to validate that a user |
50 | | * dimensionality request doesn't overflow what we can handle. |
51 | | * |
52 | | * The multiplication overflow check only works on machines that have int64 |
53 | | * arithmetic, but that is nearly all platforms these days, and doing check |
54 | | * divides for those that don't seems way too expensive. |
55 | | */ |
56 | | int |
57 | | ArrayGetNItems(int ndim, const int *dims) |
58 | 0 | { |
59 | 0 | return ArrayGetNItemsSafe(ndim, dims, NULL); |
60 | 0 | } |
61 | | |
62 | | /* |
63 | | * This entry point can return the error into an ErrorSaveContext |
64 | | * instead of throwing an exception. -1 is returned after an error. |
65 | | */ |
66 | | int |
67 | | ArrayGetNItemsSafe(int ndim, const int *dims, struct Node *escontext) |
68 | 0 | { |
69 | 0 | int32 ret; |
70 | 0 | int i; |
71 | |
|
72 | 0 | if (ndim <= 0) |
73 | 0 | return 0; |
74 | 0 | ret = 1; |
75 | 0 | for (i = 0; i < ndim; i++) |
76 | 0 | { |
77 | 0 | int64 prod; |
78 | | |
79 | | /* A negative dimension implies that UB-LB overflowed ... */ |
80 | 0 | if (dims[i] < 0) |
81 | 0 | ereturn(escontext, -1, |
82 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
83 | 0 | errmsg("array size exceeds the maximum allowed (%d)", |
84 | 0 | (int) MaxArraySize))); |
85 | | |
86 | 0 | prod = (int64) ret * (int64) dims[i]; |
87 | |
|
88 | 0 | ret = (int32) prod; |
89 | 0 | if ((int64) ret != prod) |
90 | 0 | ereturn(escontext, -1, |
91 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
92 | 0 | errmsg("array size exceeds the maximum allowed (%d)", |
93 | 0 | (int) MaxArraySize))); |
94 | 0 | } |
95 | 0 | Assert(ret >= 0); |
96 | 0 | if ((Size) ret > MaxArraySize) |
97 | 0 | ereturn(escontext, -1, |
98 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
99 | 0 | errmsg("array size exceeds the maximum allowed (%d)", |
100 | 0 | (int) MaxArraySize))); |
101 | 0 | return (int) ret; |
102 | 0 | } |
103 | | |
104 | | /* |
105 | | * Verify sanity of proposed lower-bound values for an array |
106 | | * |
107 | | * The lower-bound values must not be so large as to cause overflow when |
108 | | * calculating subscripts, e.g. lower bound 2147483640 with length 10 |
109 | | * must be disallowed. We actually insist that dims[i] + lb[i] be |
110 | | * computable without overflow, meaning that an array with last subscript |
111 | | * equal to INT_MAX will be disallowed. |
112 | | * |
113 | | * It is assumed that the caller already called ArrayGetNItems, so that |
114 | | * overflowed (negative) dims[] values have been eliminated. |
115 | | */ |
116 | | void |
117 | | ArrayCheckBounds(int ndim, const int *dims, const int *lb) |
118 | 0 | { |
119 | 0 | (void) ArrayCheckBoundsSafe(ndim, dims, lb, NULL); |
120 | 0 | } |
121 | | |
122 | | /* |
123 | | * This entry point can return the error into an ErrorSaveContext |
124 | | * instead of throwing an exception. |
125 | | */ |
126 | | bool |
127 | | ArrayCheckBoundsSafe(int ndim, const int *dims, const int *lb, |
128 | | struct Node *escontext) |
129 | 0 | { |
130 | 0 | int i; |
131 | |
|
132 | 0 | for (i = 0; i < ndim; i++) |
133 | 0 | { |
134 | | /* PG_USED_FOR_ASSERTS_ONLY prevents variable-isn't-read warnings */ |
135 | 0 | int32 sum PG_USED_FOR_ASSERTS_ONLY; |
136 | |
|
137 | 0 | if (pg_add_s32_overflow(dims[i], lb[i], &sum)) |
138 | 0 | ereturn(escontext, false, |
139 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
140 | 0 | errmsg("array lower bound is too large: %d", |
141 | 0 | lb[i]))); |
142 | 0 | } |
143 | | |
144 | 0 | return true; |
145 | 0 | } |
146 | | |
147 | | /* |
148 | | * Compute ranges (sub-array dimensions) for an array slice |
149 | | * |
150 | | * We assume caller has validated slice endpoints, so overflow is impossible |
151 | | */ |
152 | | void |
153 | | mda_get_range(int n, int *span, const int *st, const int *endp) |
154 | 0 | { |
155 | 0 | int i; |
156 | |
|
157 | 0 | for (i = 0; i < n; i++) |
158 | 0 | span[i] = endp[i] - st[i] + 1; |
159 | 0 | } |
160 | | |
161 | | /* |
162 | | * Compute products of array dimensions, ie, scale factors for subscripts |
163 | | * |
164 | | * We assume caller has validated dimensions, so overflow is impossible |
165 | | */ |
166 | | void |
167 | | mda_get_prod(int n, const int *range, int *prod) |
168 | 0 | { |
169 | 0 | int i; |
170 | |
|
171 | 0 | prod[n - 1] = 1; |
172 | 0 | for (i = n - 2; i >= 0; i--) |
173 | 0 | prod[i] = prod[i + 1] * range[i + 1]; |
174 | 0 | } |
175 | | |
176 | | /* |
177 | | * From products of whole-array dimensions and spans of a sub-array, |
178 | | * compute offset distances needed to step through subarray within array |
179 | | * |
180 | | * We assume caller has validated dimensions, so overflow is impossible |
181 | | */ |
182 | | void |
183 | | mda_get_offset_values(int n, int *dist, const int *prod, const int *span) |
184 | 0 | { |
185 | 0 | int i, |
186 | 0 | j; |
187 | |
|
188 | 0 | dist[n - 1] = 0; |
189 | 0 | for (j = n - 2; j >= 0; j--) |
190 | 0 | { |
191 | 0 | dist[j] = prod[j] - 1; |
192 | 0 | for (i = j + 1; i < n; i++) |
193 | 0 | dist[j] -= (span[i] - 1) * prod[i]; |
194 | 0 | } |
195 | 0 | } |
196 | | |
197 | | /* |
198 | | * Generates the tuple that is lexicographically one greater than the current |
199 | | * n-tuple in "curr", with the restriction that the i-th element of "curr" is |
200 | | * less than the i-th element of "span". |
201 | | * |
202 | | * Returns -1 if no next tuple exists, else the subscript position (0..n-1) |
203 | | * corresponding to the dimension to advance along. |
204 | | * |
205 | | * We assume caller has validated dimensions, so overflow is impossible |
206 | | */ |
207 | | int |
208 | | mda_next_tuple(int n, int *curr, const int *span) |
209 | 0 | { |
210 | 0 | int i; |
211 | |
|
212 | 0 | if (n <= 0) |
213 | 0 | return -1; |
214 | | |
215 | 0 | curr[n - 1] = (curr[n - 1] + 1) % span[n - 1]; |
216 | 0 | for (i = n - 1; i && curr[i] == 0; i--) |
217 | 0 | curr[i - 1] = (curr[i - 1] + 1) % span[i - 1]; |
218 | |
|
219 | 0 | if (i) |
220 | 0 | return i; |
221 | 0 | if (curr[0]) |
222 | 0 | return 0; |
223 | | |
224 | 0 | return -1; |
225 | 0 | } |
226 | | |
227 | | /* |
228 | | * ArrayGetIntegerTypmods: verify that argument is a 1-D cstring array, |
229 | | * and get the contents converted to integers. Returns a palloc'd array |
230 | | * and places the length at *n. |
231 | | */ |
232 | | int32 * |
233 | | ArrayGetIntegerTypmods(ArrayType *arr, int *n) |
234 | 0 | { |
235 | 0 | int32 *result; |
236 | 0 | Datum *elem_values; |
237 | 0 | int i; |
238 | |
|
239 | 0 | if (ARR_ELEMTYPE(arr) != CSTRINGOID) |
240 | 0 | ereport(ERROR, |
241 | 0 | (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), |
242 | 0 | errmsg("typmod array must be type cstring[]"))); |
243 | | |
244 | 0 | if (ARR_NDIM(arr) != 1) |
245 | 0 | ereport(ERROR, |
246 | 0 | (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), |
247 | 0 | errmsg("typmod array must be one-dimensional"))); |
248 | | |
249 | 0 | if (array_contains_nulls(arr)) |
250 | 0 | ereport(ERROR, |
251 | 0 | (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), |
252 | 0 | errmsg("typmod array must not contain nulls"))); |
253 | | |
254 | 0 | deconstruct_array_builtin(arr, CSTRINGOID, &elem_values, NULL, n); |
255 | |
|
256 | 0 | result = (int32 *) palloc(*n * sizeof(int32)); |
257 | |
|
258 | 0 | for (i = 0; i < *n; i++) |
259 | 0 | result[i] = pg_strtoint32(DatumGetCString(elem_values[i])); |
260 | |
|
261 | 0 | pfree(elem_values); |
262 | |
|
263 | 0 | return result; |
264 | 0 | } |