LCOV - code coverage report
Current view: top level - src/backend/utils/adt - arrayutils.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 68 76 89.5 %
Date: 2023-05-31 02:11:47 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * arrayutils.c
       4             :  *    This file contains some support routines required for array functions.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2023, 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      653242 : ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx)
      33             : {
      34             :     int         i,
      35      653242 :                 scale = 1,
      36      653242 :                 offset = 0;
      37             : 
      38     1306802 :     for (i = n - 1; i >= 0; i--)
      39             :     {
      40      653560 :         offset += (indx[i] - lb[i]) * scale;
      41      653560 :         scale *= dim[i];
      42             :     }
      43      653242 :     return offset;
      44             : }
      45             : 
      46             : /*
      47             :  * Same, but subscripts are assumed 0-based, and use a scale array
      48             :  * instead of raw dimension data (see mda_get_prod to create scale array)
      49             :  */
      50             : int
      51     1770664 : ArrayGetOffset0(int n, const int *tup, const int *scale)
      52             : {
      53             :     int         i,
      54     1770664 :                 lin = 0;
      55             : 
      56     3544018 :     for (i = 0; i < n; i++)
      57     1773354 :         lin += tup[i] * scale[i];
      58     1770664 :     return lin;
      59             : }
      60             : 
      61             : /*
      62             :  * Convert array dimensions into number of elements
      63             :  *
      64             :  * This must do overflow checking, since it is used to validate that a user
      65             :  * dimensionality request doesn't overflow what we can handle.
      66             :  *
      67             :  * We limit array sizes to at most about a quarter billion elements,
      68             :  * so that it's not necessary to check for overflow in quite so many
      69             :  * places --- for instance when palloc'ing Datum arrays.
      70             :  *
      71             :  * The multiplication overflow check only works on machines that have int64
      72             :  * arithmetic, but that is nearly all platforms these days, and doing check
      73             :  * divides for those that don't seems way too expensive.
      74             :  */
      75             : int
      76    83004294 : ArrayGetNItems(int ndim, const int *dims)
      77             : {
      78    83004294 :     return ArrayGetNItemsSafe(ndim, dims, NULL);
      79             : }
      80             : 
      81             : /*
      82             :  * This entry point can return the error into an ErrorSaveContext
      83             :  * instead of throwing an exception.  -1 is returned after an error.
      84             :  */
      85             : int
      86    83368538 : ArrayGetNItemsSafe(int ndim, const int *dims, struct Node *escontext)
      87             : {
      88             :     int32       ret;
      89             :     int         i;
      90             : 
      91             : #define MaxArraySize ((Size) (MaxAllocSize / sizeof(Datum)))
      92             : 
      93    83368538 :     if (ndim <= 0)
      94     2427656 :         return 0;
      95    80940882 :     ret = 1;
      96   161886038 :     for (i = 0; i < ndim; i++)
      97             :     {
      98             :         int64       prod;
      99             : 
     100             :         /* A negative dimension implies that UB-LB overflowed ... */
     101    80945156 :         if (dims[i] < 0)
     102           0 :             ereturn(escontext, -1,
     103             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     104             :                      errmsg("array size exceeds the maximum allowed (%d)",
     105             :                             (int) MaxArraySize)));
     106             : 
     107    80945156 :         prod = (int64) ret * (int64) dims[i];
     108             : 
     109    80945156 :         ret = (int32) prod;
     110    80945156 :         if ((int64) ret != prod)
     111           0 :             ereturn(escontext, -1,
     112             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     113             :                      errmsg("array size exceeds the maximum allowed (%d)",
     114             :                             (int) MaxArraySize)));
     115             :     }
     116             :     Assert(ret >= 0);
     117    80940882 :     if ((Size) ret > MaxArraySize)
     118           0 :         ereturn(escontext, -1,
     119             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     120             :                  errmsg("array size exceeds the maximum allowed (%d)",
     121             :                         (int) MaxArraySize)));
     122    80940882 :     return (int) ret;
     123             : }
     124             : 
     125             : /*
     126             :  * Verify sanity of proposed lower-bound values for an array
     127             :  *
     128             :  * The lower-bound values must not be so large as to cause overflow when
     129             :  * calculating subscripts, e.g. lower bound 2147483640 with length 10
     130             :  * must be disallowed.  We actually insist that dims[i] + lb[i] be
     131             :  * computable without overflow, meaning that an array with last subscript
     132             :  * equal to INT_MAX will be disallowed.
     133             :  *
     134             :  * It is assumed that the caller already called ArrayGetNItems, so that
     135             :  * overflowed (negative) dims[] values have been eliminated.
     136             :  */
     137             : void
     138     1970844 : ArrayCheckBounds(int ndim, const int *dims, const int *lb)
     139             : {
     140     1970844 :     (void) ArrayCheckBoundsSafe(ndim, dims, lb, NULL);
     141     1970844 : }
     142             : 
     143             : /*
     144             :  * This entry point can return the error into an ErrorSaveContext
     145             :  * instead of throwing an exception.
     146             :  */
     147             : bool
     148     2335088 : ArrayCheckBoundsSafe(int ndim, const int *dims, const int *lb,
     149             :                      struct Node *escontext)
     150             : {
     151             :     int         i;
     152             : 
     153     4628340 :     for (i = 0; i < ndim; i++)
     154             :     {
     155             :         /* PG_USED_FOR_ASSERTS_ONLY prevents variable-isn't-read warnings */
     156             :         int32       sum PG_USED_FOR_ASSERTS_ONLY;
     157             : 
     158     2293252 :         if (pg_add_s32_overflow(dims[i], lb[i], &sum))
     159           0 :             ereturn(escontext, false,
     160             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     161             :                      errmsg("array lower bound is too large: %d",
     162             :                             lb[i])));
     163             :     }
     164             : 
     165     2335088 :     return true;
     166             : }
     167             : 
     168             : /*
     169             :  * Compute ranges (sub-array dimensions) for an array slice
     170             :  *
     171             :  * We assume caller has validated slice endpoints, so overflow is impossible
     172             :  */
     173             : void
     174        1016 : mda_get_range(int n, int *span, const int *st, const int *endp)
     175             : {
     176             :     int         i;
     177             : 
     178        2554 :     for (i = 0; i < n; i++)
     179        1538 :         span[i] = endp[i] - st[i] + 1;
     180        1016 : }
     181             : 
     182             : /*
     183             :  * Compute products of array dimensions, ie, scale factors for subscripts
     184             :  *
     185             :  * We assume caller has validated dimensions, so overflow is impossible
     186             :  */
     187             : void
     188      357628 : mda_get_prod(int n, const int *range, int *prod)
     189             : {
     190             :     int         i;
     191             : 
     192      357628 :     prod[n - 1] = 1;
     193      358260 :     for (i = n - 2; i >= 0; i--)
     194         632 :         prod[i] = prod[i + 1] * range[i + 1];
     195      357628 : }
     196             : 
     197             : /*
     198             :  * From products of whole-array dimensions and spans of a sub-array,
     199             :  * compute offset distances needed to step through subarray within array
     200             :  *
     201             :  * We assume caller has validated dimensions, so overflow is impossible
     202             :  */
     203             : void
     204         360 : mda_get_offset_values(int n, int *dist, const int *prod, const int *span)
     205             : {
     206             :     int         i,
     207             :                 j;
     208             : 
     209         360 :     dist[n - 1] = 0;
     210         576 :     for (j = n - 2; j >= 0; j--)
     211             :     {
     212         216 :         dist[j] = prod[j] - 1;
     213         462 :         for (i = j + 1; i < n; i++)
     214         246 :             dist[j] -= (span[i] - 1) * prod[i];
     215             :     }
     216         360 : }
     217             : 
     218             : /*
     219             :  * Generates the tuple that is lexicographically one greater than the current
     220             :  * n-tuple in "curr", with the restriction that the i-th element of "curr" is
     221             :  * less than the i-th element of "span".
     222             :  *
     223             :  * Returns -1 if no next tuple exists, else the subscript position (0..n-1)
     224             :  * corresponding to the dimension to advance along.
     225             :  *
     226             :  * We assume caller has validated dimensions, so overflow is impossible
     227             :  */
     228             : int
     229        1290 : mda_next_tuple(int n, int *curr, const int *span)
     230             : {
     231             :     int         i;
     232             : 
     233        1290 :     if (n <= 0)
     234           0 :         return -1;
     235             : 
     236        1290 :     curr[n - 1] = (curr[n - 1] + 1) % span[n - 1];
     237        1608 :     for (i = n - 1; i && curr[i] == 0; i--)
     238         318 :         curr[i - 1] = (curr[i - 1] + 1) % span[i - 1];
     239             : 
     240        1290 :     if (i)
     241         330 :         return i;
     242         960 :     if (curr[0])
     243         600 :         return 0;
     244             : 
     245         360 :     return -1;
     246             : }
     247             : 
     248             : /*
     249             :  * ArrayGetIntegerTypmods: verify that argument is a 1-D cstring array,
     250             :  * and get the contents converted to integers.  Returns a palloc'd array
     251             :  * and places the length at *n.
     252             :  */
     253             : int32 *
     254        8842 : ArrayGetIntegerTypmods(ArrayType *arr, int *n)
     255             : {
     256             :     int32      *result;
     257             :     Datum      *elem_values;
     258             :     int         i;
     259             : 
     260        8842 :     if (ARR_ELEMTYPE(arr) != CSTRINGOID)
     261           0 :         ereport(ERROR,
     262             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
     263             :                  errmsg("typmod array must be type cstring[]")));
     264             : 
     265        8842 :     if (ARR_NDIM(arr) != 1)
     266           0 :         ereport(ERROR,
     267             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
     268             :                  errmsg("typmod array must be one-dimensional")));
     269             : 
     270        8842 :     if (array_contains_nulls(arr))
     271           0 :         ereport(ERROR,
     272             :                 (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
     273             :                  errmsg("typmod array must not contain nulls")));
     274             : 
     275        8842 :     deconstruct_array_builtin(arr, CSTRINGOID, &elem_values, NULL, n);
     276             : 
     277        8842 :     result = (int32 *) palloc(*n * sizeof(int32));
     278             : 
     279       19614 :     for (i = 0; i < *n; i++)
     280       10772 :         result[i] = pg_strtoint32(DatumGetCString(elem_values[i]));
     281             : 
     282        8842 :     pfree(elem_values);
     283             : 
     284        8842 :     return result;
     285             : }

Generated by: LCOV version 1.14