PostgreSQL Source Code git master
Loading...
Searching...
No Matches
array_expanded.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * array_expanded.c
4 * Basic functions for manipulating expanded arrays.
5 *
6 * Portions Copyright (c) 1996-2026, 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 */
25static void EA_flatten_into(ExpandedObjectHeader *eohptr,
26 void *result, Size allocated_size);
27
33
34/* Other local functions */
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 */
52{
53 ArrayType *array;
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 */
65 "expanded array",
67
68 /* Set up expanded array header */
71
73 eah->ea_magic = EA_MAGIC;
74
75 /* If the source is an expanded array, we may be able to optimize */
77 {
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 if (metacache == NULL)
91 metacache->element_type = oldeah->element_type;
92 metacache->typlen = oldeah->typlen;
93 metacache->typbyval = oldeah->typbyval;
94 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 if (oldeah->typbyval && oldeah->dvalues != NULL)
103 {
105 /* return a R/W pointer to the expanded array */
106 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 */
133
134 eah->ndims = ARR_NDIM(array);
135 /* note these pointers point into the fvalue header! */
136 eah->dims = ARR_DIMS(array);
137 eah->lbound = ARR_LBOUND(array);
138
139 /* Save array's element-type data for possible use later */
140 eah->element_type = ARR_ELEMTYPE(array);
141 if (metacache && metacache->element_type == eah->element_type)
142 {
143 /* We have a valid cache of representational data */
144 eah->typlen = metacache->typlen;
145 eah->typbyval = metacache->typbyval;
146 eah->typalign = metacache->typalign;
147 }
148 else
149 {
150 /* No, so look it up */
151 get_typlenbyvalalign(eah->element_type,
152 &eah->typlen,
153 &eah->typbyval,
154 &eah->typalign);
155 /* Update cache if provided */
156 if (metacache)
157 {
158 metacache->element_type = eah->element_type;
159 metacache->typlen = eah->typlen;
160 metacache->typbyval = eah->typbyval;
161 metacache->typalign = eah->typalign;
162 }
163 }
164
165 /* we don't make a deconstructed representation now */
166 eah->dvalues = NULL;
167 eah->dnulls = NULL;
168 eah->dvalueslen = 0;
169 eah->nelems = 0;
170 eah->flat_size = 0;
171
172 /* remember we have a flat representation */
173 eah->fvalue = array;
174 eah->fstartptr = ARR_DATA_PTR(array);
175 eah->fendptr = ((char *) array) + ARR_SIZE(array);
176
177 /* return a R/W pointer to the expanded array */
178 return EOHPGetRWDatum(&eah->hdr);
179}
180
181/*
182 * helper for expand_array(): copy pass-by-value Datum-array representation
183 */
184static void
187{
188 MemoryContext objcxt = eah->hdr.eoh_context;
189 int ndims = oldeah->ndims;
190 int dvalueslen = oldeah->dvalueslen;
191
192 /* Copy array dimensionality information */
193 eah->ndims = ndims;
194 /* We can alloc both dimensionality arrays with one palloc */
195 eah->dims = (int *) MemoryContextAlloc(objcxt, ndims * 2 * sizeof(int));
196 eah->lbound = eah->dims + ndims;
197 /* .. but don't assume the source's arrays are contiguous */
198 memcpy(eah->dims, oldeah->dims, ndims * sizeof(int));
199 memcpy(eah->lbound, oldeah->lbound, ndims * sizeof(int));
200
201 /* Copy element-type data */
202 eah->element_type = oldeah->element_type;
203 eah->typlen = oldeah->typlen;
204 eah->typbyval = oldeah->typbyval;
205 eah->typalign = oldeah->typalign;
206
207 /* Copy the deconstructed representation */
208 eah->dvalues = (Datum *) MemoryContextAlloc(objcxt,
209 dvalueslen * sizeof(Datum));
210 memcpy(eah->dvalues, oldeah->dvalues, dvalueslen * sizeof(Datum));
211 if (oldeah->dnulls)
212 {
213 eah->dnulls = (bool *) MemoryContextAlloc(objcxt,
214 dvalueslen * sizeof(bool));
215 memcpy(eah->dnulls, oldeah->dnulls, dvalueslen * sizeof(bool));
216 }
217 else
218 eah->dnulls = NULL;
219 eah->dvalueslen = dvalueslen;
220 eah->nelems = oldeah->nelems;
221 eah->flat_size = oldeah->flat_size;
222
223 /* we don't make a flat representation */
224 eah->fvalue = NULL;
225 eah->fstartptr = NULL;
226 eah->fendptr = NULL;
227}
228
229/*
230 * get_flat_size method for expanded arrays
231 */
232static Size
234{
236 int nelems;
237 int ndims;
238 Datum *dvalues;
239 bool *dnulls;
240 Size nbytes;
241 uint8 typalignby;
242 int i;
243
244 Assert(eah->ea_magic == EA_MAGIC);
245
246 /* Easy if we have a valid flattened value */
247 if (eah->fvalue)
248 return ARR_SIZE(eah->fvalue);
249
250 /* If we have a cached size value, believe that */
251 if (eah->flat_size)
252 return eah->flat_size;
253
254 /*
255 * Compute space needed by examining dvalues/dnulls. Note that the result
256 * array will have a nulls bitmap if dnulls isn't NULL, even if the array
257 * doesn't actually contain any nulls now.
258 */
259 nelems = eah->nelems;
260 ndims = eah->ndims;
261 Assert(nelems == ArrayGetNItems(ndims, eah->dims));
262 dvalues = eah->dvalues;
263 dnulls = eah->dnulls;
264 nbytes = 0;
265 typalignby = typalign_to_alignby(eah->typalign);
266 for (i = 0; i < nelems; i++)
267 {
268 if (dnulls && dnulls[i])
269 continue;
270 nbytes = att_addlength_datum(nbytes, eah->typlen, dvalues[i]);
271 nbytes = att_nominal_alignby(nbytes, typalignby);
272 /* check for overflow of total request */
273 if (!AllocSizeIsValid(nbytes))
276 errmsg("array size exceeds the maximum allowed (%zu)",
277 MaxAllocSize)));
278 }
279
280 if (dnulls)
281 nbytes += ARR_OVERHEAD_WITHNULLS(ndims, nelems);
282 else
283 nbytes += ARR_OVERHEAD_NONULLS(ndims);
284
285 /* cache for next time */
286 eah->flat_size = nbytes;
287
288 return nbytes;
289}
290
291/*
292 * flatten_into method for expanded arrays
293 */
294static void
296 void *result, Size allocated_size)
297{
299 ArrayType *aresult = (ArrayType *) result;
300 int nelems;
301 int ndims;
302 int32 dataoffset;
303
304 Assert(eah->ea_magic == EA_MAGIC);
305
306 /* Easy if we have a valid flattened value */
307 if (eah->fvalue)
308 {
309 Assert(allocated_size == ARR_SIZE(eah->fvalue));
310 memcpy(result, eah->fvalue, allocated_size);
311 return;
312 }
313
314 /* Else allocation should match previous get_flat_size result */
315 Assert(allocated_size == eah->flat_size);
316
317 /* Fill result array from dvalues/dnulls */
318 nelems = eah->nelems;
319 ndims = eah->ndims;
320
321 if (eah->dnulls)
322 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
323 else
324 dataoffset = 0; /* marker for no null bitmap */
325
326 /* We must ensure that any pad space is zero-filled */
327 memset(aresult, 0, allocated_size);
328
329 SET_VARSIZE(aresult, allocated_size);
330 aresult->ndim = ndims;
331 aresult->dataoffset = dataoffset;
332 aresult->elemtype = eah->element_type;
333 memcpy(ARR_DIMS(aresult), eah->dims, ndims * sizeof(int));
334 memcpy(ARR_LBOUND(aresult), eah->lbound, ndims * sizeof(int));
335
337 eah->dvalues, eah->dnulls, nelems,
338 eah->typlen, eah->typbyval, eah->typalign,
339 false);
340}
341
342/*
343 * Argument fetching support code
344 */
345
346/*
347 * DatumGetExpandedArray: get a writable expanded array from an input argument
348 *
349 * Caution: if the input is a read/write pointer, this returns the input
350 * argument; so callers must be sure that their changes are "safe", that is
351 * they cannot leave the array in a corrupt state.
352 */
355{
356 /* If it's a writable expanded array already, just return it */
358 {
360
361 Assert(eah->ea_magic == EA_MAGIC);
362 return eah;
363 }
364
365 /* Else expand the hard way */
367 return (ExpandedArrayHeader *) DatumGetEOHP(d);
368}
369
370/*
371 * As above, when caller has the ability to cache element type info
372 */
375{
376 /* If it's a writable expanded array already, just return it */
378 {
380
381 Assert(eah->ea_magic == EA_MAGIC);
382 /* Update cache if provided */
383 if (metacache)
384 {
385 metacache->element_type = eah->element_type;
386 metacache->typlen = eah->typlen;
387 metacache->typbyval = eah->typbyval;
388 metacache->typalign = eah->typalign;
389 }
390 return eah;
391 }
392
393 /* Else expand using caller's cache if any */
395 return (ExpandedArrayHeader *) DatumGetEOHP(d);
396}
397
398/*
399 * DatumGetAnyArrayP: return either an expanded array or a detoasted varlena
400 * array. The result must not be modified in-place.
401 */
404{
406
407 /*
408 * If it's an expanded array (RW or RO), return the header pointer.
409 */
411 {
413 Assert(eah->ea_magic == EA_MAGIC);
414 return (AnyArrayType *) eah;
415 }
416
417 /* Else do regular detoasting as needed */
418 return (AnyArrayType *) PG_DETOAST_DATUM(d);
419}
420
421/*
422 * Create the Datum/isnull representation of an expanded array object
423 * if we didn't do so previously
424 */
425void
427{
428 if (eah->dvalues == NULL)
429 {
430 MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
431 Datum *dvalues;
432 bool *dnulls;
433 int nelems;
434
435 dnulls = NULL;
436 deconstruct_array(eah->fvalue,
437 eah->element_type,
438 eah->typlen, eah->typbyval, eah->typalign,
439 &dvalues,
440 ARR_HASNULL(eah->fvalue) ? &dnulls : NULL,
441 &nelems);
442
443 /*
444 * Update header only after successful completion of this step. If
445 * deconstruct_array fails partway through, worst consequence is some
446 * leaked memory in the object's context. If the caller fails at a
447 * later point, that's fine, since the deconstructed representation is
448 * valid anyhow.
449 */
450 eah->dvalues = dvalues;
451 eah->dnulls = dnulls;
452 eah->dvalueslen = eah->nelems = nelems;
454 }
455}
#define DatumGetArrayTypePCopy(X)
Definition array.h:262
#define ARR_NDIM(a)
Definition array.h:290
#define ARR_DATA_PTR(a)
Definition array.h:322
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems)
Definition array.h:312
#define ARR_ELEMTYPE(a)
Definition array.h:292
#define ARR_SIZE(a)
Definition array.h:289
#define ARR_OVERHEAD_NONULLS(ndims)
Definition array.h:310
#define ARR_DIMS(a)
Definition array.h:294
#define ARR_HASNULL(a)
Definition array.h:291
#define ARR_LBOUND(a)
Definition array.h:296
#define EA_MAGIC
Definition array.h:113
static void copy_byval_expanded_array(ExpandedArrayHeader *eah, ExpandedArrayHeader *oldeah)
ExpandedArrayHeader * DatumGetExpandedArrayX(Datum d, ArrayMetaState *metacache)
Datum expand_array(Datum arraydatum, MemoryContext parentcontext, ArrayMetaState *metacache)
void deconstruct_expanded_array(ExpandedArrayHeader *eah)
static Size EA_get_flat_size(ExpandedObjectHeader *eohptr)
ExpandedArrayHeader * DatumGetExpandedArray(Datum d)
AnyArrayType * DatumGetAnyArrayP(Datum d)
static void EA_flatten_into(ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
static const ExpandedObjectMethods EA_methods
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
void CopyArrayEls(ArrayType *array, const Datum *values, const bool *nulls, int nitems, int typlen, bool typbyval, char typalign, bool freedata)
Definition arrayfuncs.c:965
int ArrayGetNItems(int ndim, const int *dims)
Definition arrayutils.c:57
uint8_t uint8
Definition c.h:544
#define Assert(condition)
Definition c.h:873
int32_t int32
Definition c.h:542
size_t Size
Definition c.h:619
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
ExpandedObjectHeader * DatumGetEOHP(Datum d)
void EOH_init_header(ExpandedObjectHeader *eohptr, const ExpandedObjectMethods *methods, MemoryContext obj_context)
static Datum EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
#define MaxAllocSize
Definition fe_memutils.h:22
#define PG_DETOAST_DATUM(datum)
Definition fmgr.h:240
int i
Definition isn.c:77
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition lsyscache.c:2421
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition mcxt.c:1232
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_START_SMALL_SIZES
Definition memutils.h:177
#define AllocSizeIsValid(size)
Definition memutils.h:42
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
uint64_t Datum
Definition postgres.h:70
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
static int fb(int x)
#define att_nominal_alignby(cur_offset, attalignby)
Definition tupmacs.h:189
static uint8 typalign_to_alignby(char typalign)
Definition tupmacs.h:80
#define att_addlength_datum(cur_offset, attlen, attdatum)
Definition tupmacs.h:197
static bool VARATT_IS_EXTERNAL_EXPANDED_RW(const void *PTR)
Definition varatt.h:382
static bool VARATT_IS_EXTERNAL_EXPANDED(const void *PTR)
Definition varatt.h:389
static void SET_VARSIZE(void *PTR, Size len)
Definition varatt.h:432