LCOV - code coverage report
Current view: top level - src/backend/utils/adt - array_expanded.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 139 145 95.9 %
Date: 2024-04-26 02:11:37 Functions: 8 8 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * array_expanded.c
       4             :  *    Basic functions for manipulating expanded arrays.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/utils/adt/array_expanded.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/tupmacs.h"
      18             : #include "utils/array.h"
      19             : #include "utils/lsyscache.h"
      20             : #include "utils/memutils.h"
      21             : 
      22             : 
      23             : /* "Methods" required for an expanded object */
      24             : static Size EA_get_flat_size(ExpandedObjectHeader *eohptr);
      25             : static void EA_flatten_into(ExpandedObjectHeader *eohptr,
      26             :                             void *result, Size allocated_size);
      27             : 
      28             : static const ExpandedObjectMethods EA_methods =
      29             : {
      30             :     EA_get_flat_size,
      31             :     EA_flatten_into
      32             : };
      33             : 
      34             : /* Other local functions */
      35             : static void copy_byval_expanded_array(ExpandedArrayHeader *eah,
      36             :                                       ExpandedArrayHeader *oldeah);
      37             : 
      38             : 
      39             : /*
      40             :  * expand_array: convert an array Datum into an expanded array
      41             :  *
      42             :  * The expanded object will be a child of parentcontext.
      43             :  *
      44             :  * Some callers can provide cache space to avoid repeated lookups of element
      45             :  * type data across calls; if so, pass a metacache pointer, making sure that
      46             :  * metacache->element_type is initialized to InvalidOid before first call.
      47             :  * If no cross-call caching is required, pass NULL for metacache.
      48             :  */
      49             : Datum
      50       16448 : expand_array(Datum arraydatum, MemoryContext parentcontext,
      51             :              ArrayMetaState *metacache)
      52             : {
      53             :     ArrayType  *array;
      54             :     ExpandedArrayHeader *eah;
      55             :     MemoryContext objcxt;
      56             :     MemoryContext oldcxt;
      57             :     ArrayMetaState fakecache;
      58             : 
      59             :     /*
      60             :      * Allocate private context for expanded object.  We start by assuming
      61             :      * that the array won't be very large; but if it does grow a lot, don't
      62             :      * constrain aset.c's large-context behavior.
      63             :      */
      64       16448 :     objcxt = AllocSetContextCreate(parentcontext,
      65             :                                    "expanded array",
      66             :                                    ALLOCSET_START_SMALL_SIZES);
      67             : 
      68             :     /* Set up expanded array header */
      69             :     eah = (ExpandedArrayHeader *)
      70       16448 :         MemoryContextAlloc(objcxt, sizeof(ExpandedArrayHeader));
      71             : 
      72       16448 :     EOH_init_header(&eah->hdr, &EA_methods, objcxt);
      73       16448 :     eah->ea_magic = EA_MAGIC;
      74             : 
      75             :     /* If the source is an expanded array, we may be able to optimize */
      76       16448 :     if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
      77             :     {
      78         112 :         ExpandedArrayHeader *oldeah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
      79             : 
      80             :         Assert(oldeah->ea_magic == EA_MAGIC);
      81             : 
      82             :         /*
      83             :          * Update caller's cache if provided; we don't need it this time, but
      84             :          * next call might be for a non-expanded source array.  Furthermore,
      85             :          * if the caller didn't provide a cache area, use some local storage
      86             :          * to cache anyway, thereby avoiding a catalog lookup in the case
      87             :          * where we fall through to the flat-copy code path.
      88             :          */
      89         112 :         if (metacache == NULL)
      90          46 :             metacache = &fakecache;
      91         112 :         metacache->element_type = oldeah->element_type;
      92         112 :         metacache->typlen = oldeah->typlen;
      93         112 :         metacache->typbyval = oldeah->typbyval;
      94         112 :         metacache->typalign = oldeah->typalign;
      95             : 
      96             :         /*
      97             :          * If element type is pass-by-value and we have a Datum-array
      98             :          * representation, just copy the source's metadata and Datum/isnull
      99             :          * arrays.  The original flat array, if present at all, adds no
     100             :          * additional information so we need not copy it.
     101             :          */
     102         112 :         if (oldeah->typbyval && oldeah->dvalues != NULL)
     103             :         {
     104          36 :             copy_byval_expanded_array(eah, oldeah);
     105             :             /* return a R/W pointer to the expanded array */
     106          36 :             return EOHPGetRWDatum(&eah->hdr);
     107             :         }
     108             : 
     109             :         /*
     110             :          * Otherwise, either we have only a flat representation or the
     111             :          * elements are pass-by-reference.  In either case, the best thing
     112             :          * seems to be to copy the source as a flat representation and then
     113             :          * deconstruct that later if necessary.  For the pass-by-ref case, we
     114             :          * could perhaps save some cycles with custom code that generates the
     115             :          * deconstructed representation in parallel with copying the values,
     116             :          * but it would be a lot of extra code for fairly marginal gain.  So,
     117             :          * fall through into the flat-source code path.
     118             :          */
     119             :     }
     120             : 
     121             :     /*
     122             :      * Detoast and copy source array into private context, as a flat array.
     123             :      *
     124             :      * Note that this coding risks leaking some memory in the private context
     125             :      * if we have to fetch data from a TOAST table; however, experimentation
     126             :      * says that the leak is minimal.  Doing it this way saves a copy step,
     127             :      * which seems worthwhile, especially if the array is large enough to need
     128             :      * external storage.
     129             :      */
     130       16412 :     oldcxt = MemoryContextSwitchTo(objcxt);
     131       16412 :     array = DatumGetArrayTypePCopy(arraydatum);
     132       16412 :     MemoryContextSwitchTo(oldcxt);
     133             : 
     134       16412 :     eah->ndims = ARR_NDIM(array);
     135             :     /* note these pointers point into the fvalue header! */
     136       16412 :     eah->dims = ARR_DIMS(array);
     137       16412 :     eah->lbound = ARR_LBOUND(array);
     138             : 
     139             :     /* Save array's element-type data for possible use later */
     140       16412 :     eah->element_type = ARR_ELEMTYPE(array);
     141       16412 :     if (metacache && metacache->element_type == eah->element_type)
     142             :     {
     143             :         /* We have a valid cache of representational data */
     144         658 :         eah->typlen = metacache->typlen;
     145         658 :         eah->typbyval = metacache->typbyval;
     146         658 :         eah->typalign = metacache->typalign;
     147             :     }
     148             :     else
     149             :     {
     150             :         /* No, so look it up */
     151       15754 :         get_typlenbyvalalign(eah->element_type,
     152             :                              &eah->typlen,
     153             :                              &eah->typbyval,
     154             :                              &eah->typalign);
     155             :         /* Update cache if provided */
     156       15754 :         if (metacache)
     157             :         {
     158        1038 :             metacache->element_type = eah->element_type;
     159        1038 :             metacache->typlen = eah->typlen;
     160        1038 :             metacache->typbyval = eah->typbyval;
     161        1038 :             metacache->typalign = eah->typalign;
     162             :         }
     163             :     }
     164             : 
     165             :     /* we don't make a deconstructed representation now */
     166       16412 :     eah->dvalues = NULL;
     167       16412 :     eah->dnulls = NULL;
     168       16412 :     eah->dvalueslen = 0;
     169       16412 :     eah->nelems = 0;
     170       16412 :     eah->flat_size = 0;
     171             : 
     172             :     /* remember we have a flat representation */
     173       16412 :     eah->fvalue = array;
     174       16412 :     eah->fstartptr = ARR_DATA_PTR(array);
     175       16412 :     eah->fendptr = ((char *) array) + ARR_SIZE(array);
     176             : 
     177             :     /* return a R/W pointer to the expanded array */
     178       16412 :     return EOHPGetRWDatum(&eah->hdr);
     179             : }
     180             : 
     181             : /*
     182             :  * helper for expand_array(): copy pass-by-value Datum-array representation
     183             :  */
     184             : static void
     185          36 : copy_byval_expanded_array(ExpandedArrayHeader *eah,
     186             :                           ExpandedArrayHeader *oldeah)
     187             : {
     188          36 :     MemoryContext objcxt = eah->hdr.eoh_context;
     189          36 :     int         ndims = oldeah->ndims;
     190          36 :     int         dvalueslen = oldeah->dvalueslen;
     191             : 
     192             :     /* Copy array dimensionality information */
     193          36 :     eah->ndims = ndims;
     194             :     /* We can alloc both dimensionality arrays with one palloc */
     195          36 :     eah->dims = (int *) MemoryContextAlloc(objcxt, ndims * 2 * sizeof(int));
     196          36 :     eah->lbound = eah->dims + ndims;
     197             :     /* .. but don't assume the source's arrays are contiguous */
     198          36 :     memcpy(eah->dims, oldeah->dims, ndims * sizeof(int));
     199          36 :     memcpy(eah->lbound, oldeah->lbound, ndims * sizeof(int));
     200             : 
     201             :     /* Copy element-type data */
     202          36 :     eah->element_type = oldeah->element_type;
     203          36 :     eah->typlen = oldeah->typlen;
     204          36 :     eah->typbyval = oldeah->typbyval;
     205          36 :     eah->typalign = oldeah->typalign;
     206             : 
     207             :     /* Copy the deconstructed representation */
     208          36 :     eah->dvalues = (Datum *) MemoryContextAlloc(objcxt,
     209             :                                                 dvalueslen * sizeof(Datum));
     210          36 :     memcpy(eah->dvalues, oldeah->dvalues, dvalueslen * sizeof(Datum));
     211          36 :     if (oldeah->dnulls)
     212             :     {
     213           0 :         eah->dnulls = (bool *) MemoryContextAlloc(objcxt,
     214             :                                                   dvalueslen * sizeof(bool));
     215           0 :         memcpy(eah->dnulls, oldeah->dnulls, dvalueslen * sizeof(bool));
     216             :     }
     217             :     else
     218          36 :         eah->dnulls = NULL;
     219          36 :     eah->dvalueslen = dvalueslen;
     220          36 :     eah->nelems = oldeah->nelems;
     221          36 :     eah->flat_size = oldeah->flat_size;
     222             : 
     223             :     /* we don't make a flat representation */
     224          36 :     eah->fvalue = NULL;
     225          36 :     eah->fstartptr = NULL;
     226          36 :     eah->fendptr = NULL;
     227          36 : }
     228             : 
     229             : /*
     230             :  * get_flat_size method for expanded arrays
     231             :  */
     232             : static Size
     233       11994 : EA_get_flat_size(ExpandedObjectHeader *eohptr)
     234             : {
     235       11994 :     ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr;
     236             :     int         nelems;
     237             :     int         ndims;
     238             :     Datum      *dvalues;
     239             :     bool       *dnulls;
     240             :     Size        nbytes;
     241             :     int         i;
     242             : 
     243             :     Assert(eah->ea_magic == EA_MAGIC);
     244             : 
     245             :     /* Easy if we have a valid flattened value */
     246       11994 :     if (eah->fvalue)
     247        5976 :         return ARR_SIZE(eah->fvalue);
     248             : 
     249             :     /* If we have a cached size value, believe that */
     250        6018 :     if (eah->flat_size)
     251        4256 :         return eah->flat_size;
     252             : 
     253             :     /*
     254             :      * Compute space needed by examining dvalues/dnulls.  Note that the result
     255             :      * array will have a nulls bitmap if dnulls isn't NULL, even if the array
     256             :      * doesn't actually contain any nulls now.
     257             :      */
     258        1762 :     nelems = eah->nelems;
     259        1762 :     ndims = eah->ndims;
     260             :     Assert(nelems == ArrayGetNItems(ndims, eah->dims));
     261        1762 :     dvalues = eah->dvalues;
     262        1762 :     dnulls = eah->dnulls;
     263        1762 :     nbytes = 0;
     264        6424 :     for (i = 0; i < nelems; i++)
     265             :     {
     266        4662 :         if (dnulls && dnulls[i])
     267           0 :             continue;
     268        4662 :         nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]);
     269        4662 :         nbytes = att_align_nominal(nbytes, eah->typalign);
     270             :         /* check for overflow of total request */
     271        4662 :         if (!AllocSizeIsValid(nbytes))
     272           0 :             ereport(ERROR,
     273             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     274             :                      errmsg("array size exceeds the maximum allowed (%d)",
     275             :                             (int) MaxAllocSize)));
     276             :     }
     277             : 
     278        1762 :     if (dnulls)
     279           0 :         nbytes += ARR_OVERHEAD_WITHNULLS(ndims, nelems);
     280             :     else
     281        1762 :         nbytes += ARR_OVERHEAD_NONULLS(ndims);
     282             : 
     283             :     /* cache for next time */
     284        1762 :     eah->flat_size = nbytes;
     285             : 
     286        1762 :     return nbytes;
     287             : }
     288             : 
     289             : /*
     290             :  * flatten_into method for expanded arrays
     291             :  */
     292             : static void
     293        9028 : EA_flatten_into(ExpandedObjectHeader *eohptr,
     294             :                 void *result, Size allocated_size)
     295             : {
     296        9028 :     ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr;
     297        9028 :     ArrayType  *aresult = (ArrayType *) result;
     298             :     int         nelems;
     299             :     int         ndims;
     300             :     int32       dataoffset;
     301             : 
     302             :     Assert(eah->ea_magic == EA_MAGIC);
     303             : 
     304             :     /* Easy if we have a valid flattened value */
     305        9028 :     if (eah->fvalue)
     306             :     {
     307             :         Assert(allocated_size == ARR_SIZE(eah->fvalue));
     308        5922 :         memcpy(result, eah->fvalue, allocated_size);
     309        5922 :         return;
     310             :     }
     311             : 
     312             :     /* Else allocation should match previous get_flat_size result */
     313             :     Assert(allocated_size == eah->flat_size);
     314             : 
     315             :     /* Fill result array from dvalues/dnulls */
     316        3106 :     nelems = eah->nelems;
     317        3106 :     ndims = eah->ndims;
     318             : 
     319        3106 :     if (eah->dnulls)
     320           0 :         dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
     321             :     else
     322        3106 :         dataoffset = 0;         /* marker for no null bitmap */
     323             : 
     324             :     /* We must ensure that any pad space is zero-filled */
     325        3106 :     memset(aresult, 0, allocated_size);
     326             : 
     327        3106 :     SET_VARSIZE(aresult, allocated_size);
     328        3106 :     aresult->ndim = ndims;
     329        3106 :     aresult->dataoffset = dataoffset;
     330        3106 :     aresult->elemtype = eah->element_type;
     331        3106 :     memcpy(ARR_DIMS(aresult), eah->dims, ndims * sizeof(int));
     332        3106 :     memcpy(ARR_LBOUND(aresult), eah->lbound, ndims * sizeof(int));
     333             : 
     334        3106 :     CopyArrayEls(aresult,
     335             :                  eah->dvalues, eah->dnulls, nelems,
     336        3106 :                  eah->typlen, eah->typbyval, eah->typalign,
     337             :                  false);
     338             : }
     339             : 
     340             : /*
     341             :  * Argument fetching support code
     342             :  */
     343             : 
     344             : /*
     345             :  * DatumGetExpandedArray: get a writable expanded array from an input argument
     346             :  *
     347             :  * Caution: if the input is a read/write pointer, this returns the input
     348             :  * argument; so callers must be sure that their changes are "safe", that is
     349             :  * they cannot leave the array in a corrupt state.
     350             :  */
     351             : ExpandedArrayHeader *
     352        3654 : DatumGetExpandedArray(Datum d)
     353             : {
     354             :     /* If it's a writable expanded array already, just return it */
     355        3654 :     if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
     356             :     {
     357        1964 :         ExpandedArrayHeader *eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
     358             : 
     359             :         Assert(eah->ea_magic == EA_MAGIC);
     360        1964 :         return eah;
     361             :     }
     362             : 
     363             :     /* Else expand the hard way */
     364        1690 :     d = expand_array(d, CurrentMemoryContext, NULL);
     365        1690 :     return (ExpandedArrayHeader *) DatumGetEOHP(d);
     366             : }
     367             : 
     368             : /*
     369             :  * As above, when caller has the ability to cache element type info
     370             :  */
     371             : ExpandedArrayHeader *
     372        1916 : DatumGetExpandedArrayX(Datum d, ArrayMetaState *metacache)
     373             : {
     374             :     /* If it's a writable expanded array already, just return it */
     375        1916 :     if (VARATT_IS_EXTERNAL_EXPANDED_RW(DatumGetPointer(d)))
     376             :     {
     377         254 :         ExpandedArrayHeader *eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
     378             : 
     379             :         Assert(eah->ea_magic == EA_MAGIC);
     380             :         /* Update cache if provided */
     381         254 :         if (metacache)
     382             :         {
     383         254 :             metacache->element_type = eah->element_type;
     384         254 :             metacache->typlen = eah->typlen;
     385         254 :             metacache->typbyval = eah->typbyval;
     386         254 :             metacache->typalign = eah->typalign;
     387             :         }
     388         254 :         return eah;
     389             :     }
     390             : 
     391             :     /* Else expand using caller's cache if any */
     392        1662 :     d = expand_array(d, CurrentMemoryContext, metacache);
     393        1662 :     return (ExpandedArrayHeader *) DatumGetEOHP(d);
     394             : }
     395             : 
     396             : /*
     397             :  * DatumGetAnyArrayP: return either an expanded array or a detoasted varlena
     398             :  * array.  The result must not be modified in-place.
     399             :  */
     400             : AnyArrayType *
     401    16656114 : DatumGetAnyArrayP(Datum d)
     402             : {
     403             :     ExpandedArrayHeader *eah;
     404             : 
     405             :     /*
     406             :      * If it's an expanded array (RW or RO), return the header pointer.
     407             :      */
     408    16656114 :     if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(d)))
     409             :     {
     410       18246 :         eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
     411             :         Assert(eah->ea_magic == EA_MAGIC);
     412       18246 :         return (AnyArrayType *) eah;
     413             :     }
     414             : 
     415             :     /* Else do regular detoasting as needed */
     416    16637868 :     return (AnyArrayType *) PG_DETOAST_DATUM(d);
     417             : }
     418             : 
     419             : /*
     420             :  * Create the Datum/isnull representation of an expanded array object
     421             :  * if we didn't do so previously
     422             :  */
     423             : void
     424       13764 : deconstruct_expanded_array(ExpandedArrayHeader *eah)
     425             : {
     426       13764 :     if (eah->dvalues == NULL)
     427             :     {
     428       11000 :         MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
     429             :         Datum      *dvalues;
     430             :         bool       *dnulls;
     431             :         int         nelems;
     432             : 
     433       11000 :         dnulls = NULL;
     434       11000 :         deconstruct_array(eah->fvalue,
     435             :                           eah->element_type,
     436       11000 :                           eah->typlen, eah->typbyval, eah->typalign,
     437             :                           &dvalues,
     438       11000 :                           ARR_HASNULL(eah->fvalue) ? &dnulls : NULL,
     439             :                           &nelems);
     440             : 
     441             :         /*
     442             :          * Update header only after successful completion of this step.  If
     443             :          * deconstruct_array fails partway through, worst consequence is some
     444             :          * leaked memory in the object's context.  If the caller fails at a
     445             :          * later point, that's fine, since the deconstructed representation is
     446             :          * valid anyhow.
     447             :          */
     448       11000 :         eah->dvalues = dvalues;
     449       11000 :         eah->dnulls = dnulls;
     450       11000 :         eah->dvalueslen = eah->nelems = nelems;
     451       11000 :         MemoryContextSwitchTo(oldcxt);
     452             :     }
     453       13764 : }

Generated by: LCOV version 1.14