diff options
Diffstat (limited to '')
-rw-r--r-- | src/backend/utils/adt/arrayutils.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c new file mode 100644 index 0000000..6988edd --- /dev/null +++ b/src/backend/utils/adt/arrayutils.c @@ -0,0 +1,265 @@ +/*------------------------------------------------------------------------- + * + * arrayutils.c + * This file contains some support routines required for array functions. + * + * Portions Copyright (c) 1996-2021, 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. + * + * We limit array sizes to at most about a quarter billion elements, + * so that it's not necessary to check for overflow in quite so many + * places --- for instance when palloc'ing Datum arrays. + * + * 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) +{ + int32 ret; + int i; + +#define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum))) + + 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) + ereport(ERROR, + (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) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array size exceeds the maximum allowed (%d)", + (int) MaxArraySize))); + } + Assert(ret >= 0); + if ((Size) ret > MaxArraySize) + ereport(ERROR, + (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) +{ + 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)) + ereport(ERROR, + (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), + errmsg("array lower bound is too large: %d", + lb[i]))); + } +} + +/* + * 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"))); + + /* hardwired knowledge about cstring's representation details here */ + deconstruct_array(arr, CSTRINGOID, + -2, false, TYPALIGN_CHAR, + &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; +} |