| /*------------------------------------------------------------------------- |
| * |
| * arrayutils.c |
| * This file contains some support routines required for array functions. |
| * |
| * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/backend/utils/adt/arrayutils.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| |
| #include "postgres.h" |
| |
| #include "catalog/pg_type.h" |
| #include "common/int.h" |
| #include "utils/array.h" |
| #include "utils/builtins.h" |
| #include "utils/memutils.h" |
| |
| |
| /* |
| * Convert subscript list into linear element number (from 0) |
| * |
| * We assume caller has already range-checked the dimensions and subscripts, |
| * so no overflow is possible. |
| */ |
| int |
| ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx) |
| { |
| int i, |
| scale = 1, |
| offset = 0; |
| |
| for (i = n - 1; i >= 0; i--) |
| { |
| offset += (indx[i] - lb[i]) * scale; |
| scale *= dim[i]; |
| } |
| return offset; |
| } |
| |
| /* |
| * Same, but subscripts are assumed 0-based, and use a scale array |
| * instead of raw dimension data (see mda_get_prod to create scale array) |
| */ |
| int |
| ArrayGetOffset0(int n, const int *tup, const int *scale) |
| { |
| int i, |
| lin = 0; |
| |
| for (i = 0; i < n; i++) |
| lin += tup[i] * scale[i]; |
| return lin; |
| } |
| |
| /* |
| * Convert array dimensions into number of elements |
| * |
| * This must do overflow checking, since it is used to validate that a user |
| * dimensionality request doesn't overflow what we can handle. |
| * |
| * The multiplication overflow check only works on machines that have int64 |
| * arithmetic, but that is nearly all platforms these days, and doing check |
| * divides for those that don't seems way too expensive. |
| */ |
| int |
| ArrayGetNItems(int ndim, const int *dims) |
| { |
| return ArrayGetNItemsSafe(ndim, dims, NULL); |
| } |
| |
| /* |
| * This entry point can return the error into an ErrorSaveContext |
| * instead of throwing an exception. -1 is returned after an error. |
| */ |
| int |
| ArrayGetNItemsSafe(int ndim, const int *dims, struct Node *escontext) |
| { |
| int32 ret; |
| int i; |
| |
| if (ndim <= 0) |
| return 0; |
| ret = 1; |
| for (i = 0; i < ndim; i++) |
| { |
| int64 prod; |
| |
| /* A negative dimension implies that UB-LB overflowed ... */ |
| if (dims[i] < 0) |
| ereturn(escontext, -1, |
| (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
| errmsg("array size exceeds the maximum allowed (%d)", |
| (int) MaxArraySize))); |
| |
| prod = (int64) ret * (int64) dims[i]; |
| |
| ret = (int32) prod; |
| if ((int64) ret != prod) |
| ereturn(escontext, -1, |
| (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
| errmsg("array size exceeds the maximum allowed (%d)", |
| (int) MaxArraySize))); |
| } |
| Assert(ret >= 0); |
| if ((Size) ret > MaxArraySize) |
| ereturn(escontext, -1, |
| (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
| errmsg("array size exceeds the maximum allowed (%d)", |
| (int) MaxArraySize))); |
| return (int) ret; |
| } |
| |
| /* |
| * Verify sanity of proposed lower-bound values for an array |
| * |
| * The lower-bound values must not be so large as to cause overflow when |
| * calculating subscripts, e.g. lower bound 2147483640 with length 10 |
| * must be disallowed. We actually insist that dims[i] + lb[i] be |
| * computable without overflow, meaning that an array with last subscript |
| * equal to INT_MAX will be disallowed. |
| * |
| * It is assumed that the caller already called ArrayGetNItems, so that |
| * overflowed (negative) dims[] values have been eliminated. |
| */ |
| void |
| ArrayCheckBounds(int ndim, const int *dims, const int *lb) |
| { |
| (void) ArrayCheckBoundsSafe(ndim, dims, lb, NULL); |
| } |
| |
| /* |
| * This entry point can return the error into an ErrorSaveContext |
| * instead of throwing an exception. |
| */ |
| bool |
| ArrayCheckBoundsSafe(int ndim, const int *dims, const int *lb, |
| struct Node *escontext) |
| { |
| int i; |
| |
| for (i = 0; i < ndim; i++) |
| { |
| /* PG_USED_FOR_ASSERTS_ONLY prevents variable-isn't-read warnings */ |
| int32 sum PG_USED_FOR_ASSERTS_ONLY; |
| |
| if (pg_add_s32_overflow(dims[i], lb[i], &sum)) |
| ereturn(escontext, false, |
| (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
| errmsg("array lower bound is too large: %d", |
| lb[i]))); |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Compute ranges (sub-array dimensions) for an array slice |
| * |
| * We assume caller has validated slice endpoints, so overflow is impossible |
| */ |
| void |
| mda_get_range(int n, int *span, const int *st, const int *endp) |
| { |
| int i; |
| |
| for (i = 0; i < n; i++) |
| span[i] = endp[i] - st[i] + 1; |
| } |
| |
| /* |
| * Compute products of array dimensions, ie, scale factors for subscripts |
| * |
| * We assume caller has validated dimensions, so overflow is impossible |
| */ |
| void |
| mda_get_prod(int n, const int *range, int *prod) |
| { |
| int i; |
| |
| prod[n - 1] = 1; |
| for (i = n - 2; i >= 0; i--) |
| prod[i] = prod[i + 1] * range[i + 1]; |
| } |
| |
| /* |
| * From products of whole-array dimensions and spans of a sub-array, |
| * compute offset distances needed to step through subarray within array |
| * |
| * We assume caller has validated dimensions, so overflow is impossible |
| */ |
| void |
| mda_get_offset_values(int n, int *dist, const int *prod, const int *span) |
| { |
| int i, |
| j; |
| |
| dist[n - 1] = 0; |
| for (j = n - 2; j >= 0; j--) |
| { |
| dist[j] = prod[j] - 1; |
| for (i = j + 1; i < n; i++) |
| dist[j] -= (span[i] - 1) * prod[i]; |
| } |
| } |
| |
| /* |
| * Generates the tuple that is lexicographically one greater than the current |
| * n-tuple in "curr", with the restriction that the i-th element of "curr" is |
| * less than the i-th element of "span". |
| * |
| * Returns -1 if no next tuple exists, else the subscript position (0..n-1) |
| * corresponding to the dimension to advance along. |
| * |
| * We assume caller has validated dimensions, so overflow is impossible |
| */ |
| int |
| mda_next_tuple(int n, int *curr, const int *span) |
| { |
| int i; |
| |
| if (n <= 0) |
| return -1; |
| |
| curr[n - 1] = (curr[n - 1] + 1) % span[n - 1]; |
| for (i = n - 1; i && curr[i] == 0; i--) |
| curr[i - 1] = (curr[i - 1] + 1) % span[i - 1]; |
| |
| if (i) |
| return i; |
| if (curr[0]) |
| return 0; |
| |
| return -1; |
| } |
| |
| /* |
| * ArrayGetIntegerTypmods: verify that argument is a 1-D cstring array, |
| * and get the contents converted to integers. Returns a palloc'd array |
| * and places the length at *n. |
| */ |
| int32 * |
| ArrayGetIntegerTypmods(ArrayType *arr, int *n) |
| { |
| int32 *result; |
| Datum *elem_values; |
| int i; |
| |
| if (ARR_ELEMTYPE(arr) != CSTRINGOID) |
| ereport(ERROR, |
| (errcode(ERRCODE_ARRAY_ELEMENT_ERROR), |
| errmsg("typmod array must be type cstring[]"))); |
| |
| if (ARR_NDIM(arr) != 1) |
| ereport(ERROR, |
| (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), |
| errmsg("typmod array must be one-dimensional"))); |
| |
| if (array_contains_nulls(arr)) |
| ereport(ERROR, |
| (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), |
| errmsg("typmod array must not contain nulls"))); |
| |
| deconstruct_array_builtin(arr, CSTRINGOID, &elem_values, NULL, n); |
| |
| result = (int32 *) palloc(*n * sizeof(int32)); |
| |
| for (i = 0; i < *n; i++) |
| result[i] = pg_strtoint32(DatumGetCString(elem_values[i])); |
| |
| pfree(elem_values); |
| |
| return result; |
| } |