PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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-2015, 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 */
25 static void EA_flatten_into(ExpandedObjectHeader *eohptr,
26  void *result, Size allocated_size);
27 
29 {
32 };
33 
34 /* Other local functions */
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 expand_array(Datum arraydatum, MemoryContext parentcontext,
51  ArrayMetaState *metacache)
52 {
53  ArrayType *array;
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  objcxt = AllocSetContextCreate(parentcontext,
65  "expanded array",
69 
70  /* Set up expanded array header */
71  eah = (ExpandedArrayHeader *)
72  MemoryContextAlloc(objcxt, sizeof(ExpandedArrayHeader));
73 
74  EOH_init_header(&eah->hdr, &EA_methods, objcxt);
75  eah->ea_magic = EA_MAGIC;
76 
77  /* If the source is an expanded array, we may be able to optimize */
79  {
80  ExpandedArrayHeader *oldeah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
81 
82  Assert(oldeah->ea_magic == EA_MAGIC);
83 
84  /*
85  * Update caller's cache if provided; we don't need it this time, but
86  * next call might be for a non-expanded source array. Furthermore,
87  * if the caller didn't provide a cache area, use some local storage
88  * to cache anyway, thereby avoiding a catalog lookup in the case
89  * where we fall through to the flat-copy code path.
90  */
91  if (metacache == NULL)
92  metacache = &fakecache;
93  metacache->element_type = oldeah->element_type;
94  metacache->typlen = oldeah->typlen;
95  metacache->typbyval = oldeah->typbyval;
96  metacache->typalign = oldeah->typalign;
97 
98  /*
99  * If element type is pass-by-value and we have a Datum-array
100  * representation, just copy the source's metadata and Datum/isnull
101  * arrays. The original flat array, if present at all, adds no
102  * additional information so we need not copy it.
103  */
104  if (oldeah->typbyval && oldeah->dvalues != NULL)
105  {
106  copy_byval_expanded_array(eah, oldeah);
107  /* return a R/W pointer to the expanded array */
108  return EOHPGetRWDatum(&eah->hdr);
109  }
110 
111  /*
112  * Otherwise, either we have only a flat representation or the
113  * elements are pass-by-reference. In either case, the best thing
114  * seems to be to copy the source as a flat representation and then
115  * deconstruct that later if necessary. For the pass-by-ref case, we
116  * could perhaps save some cycles with custom code that generates the
117  * deconstructed representation in parallel with copying the values,
118  * but it would be a lot of extra code for fairly marginal gain. So,
119  * fall through into the flat-source code path.
120  */
121  }
122 
123  /*
124  * Detoast and copy source array into private context, as a flat array.
125  *
126  * Note that this coding risks leaking some memory in the private context
127  * if we have to fetch data from a TOAST table; however, experimentation
128  * says that the leak is minimal. Doing it this way saves a copy step,
129  * which seems worthwhile, especially if the array is large enough to need
130  * external storage.
131  */
132  oldcxt = MemoryContextSwitchTo(objcxt);
133  array = DatumGetArrayTypePCopy(arraydatum);
134  MemoryContextSwitchTo(oldcxt);
135 
136  eah->ndims = ARR_NDIM(array);
137  /* note these pointers point into the fvalue header! */
138  eah->dims = ARR_DIMS(array);
139  eah->lbound = ARR_LBOUND(array);
140 
141  /* Save array's element-type data for possible use later */
142  eah->element_type = ARR_ELEMTYPE(array);
143  if (metacache && metacache->element_type == eah->element_type)
144  {
145  /* We have a valid cache of representational data */
146  eah->typlen = metacache->typlen;
147  eah->typbyval = metacache->typbyval;
148  eah->typalign = metacache->typalign;
149  }
150  else
151  {
152  /* No, so look it up */
154  &eah->typlen,
155  &eah->typbyval,
156  &eah->typalign);
157  /* Update cache if provided */
158  if (metacache)
159  {
160  metacache->element_type = eah->element_type;
161  metacache->typlen = eah->typlen;
162  metacache->typbyval = eah->typbyval;
163  metacache->typalign = eah->typalign;
164  }
165  }
166 
167  /* we don't make a deconstructed representation now */
168  eah->dvalues = NULL;
169  eah->dnulls = NULL;
170  eah->dvalueslen = 0;
171  eah->nelems = 0;
172  eah->flat_size = 0;
173 
174  /* remember we have a flat representation */
175  eah->fvalue = array;
176  eah->fstartptr = ARR_DATA_PTR(array);
177  eah->fendptr = ((char *) array) + ARR_SIZE(array);
178 
179  /* return a R/W pointer to the expanded array */
180  return EOHPGetRWDatum(&eah->hdr);
181 }
182 
183 /*
184  * helper for expand_array(): copy pass-by-value Datum-array representation
185  */
186 static void
188  ExpandedArrayHeader *oldeah)
189 {
190  MemoryContext objcxt = eah->hdr.eoh_context;
191  int ndims = oldeah->ndims;
192  int dvalueslen = oldeah->dvalueslen;
193 
194  /* Copy array dimensionality information */
195  eah->ndims = ndims;
196  /* We can alloc both dimensionality arrays with one palloc */
197  eah->dims = (int *) MemoryContextAlloc(objcxt, ndims * 2 * sizeof(int));
198  eah->lbound = eah->dims + ndims;
199  /* .. but don't assume the source's arrays are contiguous */
200  memcpy(eah->dims, oldeah->dims, ndims * sizeof(int));
201  memcpy(eah->lbound, oldeah->lbound, ndims * sizeof(int));
202 
203  /* Copy element-type data */
204  eah->element_type = oldeah->element_type;
205  eah->typlen = oldeah->typlen;
206  eah->typbyval = oldeah->typbyval;
207  eah->typalign = oldeah->typalign;
208 
209  /* Copy the deconstructed representation */
210  eah->dvalues = (Datum *) MemoryContextAlloc(objcxt,
211  dvalueslen * sizeof(Datum));
212  memcpy(eah->dvalues, oldeah->dvalues, dvalueslen * sizeof(Datum));
213  if (oldeah->dnulls)
214  {
215  eah->dnulls = (bool *) MemoryContextAlloc(objcxt,
216  dvalueslen * sizeof(bool));
217  memcpy(eah->dnulls, oldeah->dnulls, dvalueslen * sizeof(bool));
218  }
219  else
220  eah->dnulls = NULL;
221  eah->dvalueslen = dvalueslen;
222  eah->nelems = oldeah->nelems;
223  eah->flat_size = oldeah->flat_size;
224 
225  /* we don't make a flat representation */
226  eah->fvalue = NULL;
227  eah->fstartptr = NULL;
228  eah->fendptr = NULL;
229 }
230 
231 /*
232  * get_flat_size method for expanded arrays
233  */
234 static Size
236 {
237  ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr;
238  int nelems;
239  int ndims;
240  Datum *dvalues;
241  bool *dnulls;
242  Size nbytes;
243  int i;
244 
245  Assert(eah->ea_magic == EA_MAGIC);
246 
247  /* Easy if we have a valid flattened value */
248  if (eah->fvalue)
249  return ARR_SIZE(eah->fvalue);
250 
251  /* If we have a cached size value, believe that */
252  if (eah->flat_size)
253  return eah->flat_size;
254 
255  /*
256  * Compute space needed by examining dvalues/dnulls. Note that the result
257  * array will have a nulls bitmap if dnulls isn't NULL, even if the array
258  * doesn't actually contain any nulls now.
259  */
260  nelems = eah->nelems;
261  ndims = eah->ndims;
262  Assert(nelems == ArrayGetNItems(ndims, eah->dims));
263  dvalues = eah->dvalues;
264  dnulls = eah->dnulls;
265  nbytes = 0;
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_align_nominal(nbytes, eah->typalign);
272  /* check for overflow of total request */
273  if (!AllocSizeIsValid(nbytes))
274  ereport(ERROR,
275  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
276  errmsg("array size exceeds the maximum allowed (%d)",
277  (int) 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  */
294 static void
296  void *result, Size allocated_size)
297 {
298  ExpandedArrayHeader *eah = (ExpandedArrayHeader *) eohptr;
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 
336  CopyArrayEls(aresult,
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 */
394  d = expand_array(d, CurrentMemoryContext, metacache);
395  return (ExpandedArrayHeader *) DatumGetEOHP(d);
396 }
397 
398 /*
399  * DatumGetAnyArray: return either an expanded array or a detoasted varlena
400  * array. The result must not be modified in-place.
401  */
402 AnyArrayType *
404 {
405  ExpandedArrayHeader *eah;
406 
407  /*
408  * If it's an expanded array (RW or RO), return the header pointer.
409  */
411  {
412  eah = (ExpandedArrayHeader *) DatumGetEOHP(d);
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  */
425 void
427 {
428  if (eah->dvalues == NULL)
429  {
431  Datum *dvalues;
432  bool *dnulls;
433  int nelems;
434 
435  dnulls = NULL;
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;
453  MemoryContextSwitchTo(oldcxt);
454  }
455 }
static const ExpandedObjectMethods EA_methods
static void EA_flatten_into(ExpandedObjectHeader *eohptr, void *result, Size allocated_size)
#define ARR_OVERHEAD_NONULLS(ndims)
Definition: array.h:291
static void copy_byval_expanded_array(ExpandedArrayHeader *eah, ExpandedArrayHeader *oldeah)
#define att_align_nominal(cur_offset, attalign)
Definition: tupmacs.h:144
void deconstruct_expanded_array(ExpandedArrayHeader *eah)
#define ARR_SIZE(a)
Definition: array.h:270
#define VARATT_IS_EXTERNAL_EXPANDED(PTR)
Definition: postgres.h:325
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:1943
int32 dataoffset
Definition: array.h:80
int ArrayGetNItems(int ndim, const int *dims)
Definition: arrayutils.c:75
Datum expand_array(Datum arraydatum, MemoryContext parentcontext, ArrayMetaState *metacache)
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
int errcode(int sqlerrcode)
Definition: elog.c:573
Datum * dvalues
Definition: array.h:130
char * fendptr
Definition: array.h:151
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems)
Definition: array.h:293
#define ALLOCSET_SMALL_MINSIZE
Definition: memutils.h:150
signed int int32
Definition: c.h:242
#define EA_MAGIC
Definition: array.h:97
#define ARR_LBOUND(a)
Definition: array.h:277
bool typbyval
Definition: array.h:221
#define ERROR
Definition: elog.h:41
ExpandedArrayHeader * DatumGetExpandedArray(Datum d)
ExpandedObjectHeader hdr
Definition: array.h:102
Oid elemtype
Definition: array.h:81
#define ARR_DIMS(a)
Definition: array.h:275
#define ARR_DATA_PTR(a)
Definition: array.h:303
int16 typlen
Definition: array.h:220
void EOH_init_header(ExpandedObjectHeader *eohptr, const ExpandedObjectMethods *methods, MemoryContext obj_context)
Definition: expandeddatum.c:48
#define ARR_HASNULL(a)
Definition: array.h:272
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
static Size EA_get_flat_size(ExpandedObjectHeader *eohptr)
#define ereport(elevel, rest)
Definition: elog.h:132
#define AllocSizeIsValid(size)
Definition: memutils.h:42
#define MaxAllocSize
Definition: memutils.h:40
ExpandedObjectHeader * DatumGetEOHP(Datum d)
Definition: expandeddatum.c:29
#define ALLOCSET_SMALL_INITSIZE
Definition: memutils.h:151
ArrayType * fvalue
Definition: array.h:149
ExpandedArrayHeader * DatumGetExpandedArrayX(Datum d, ArrayMetaState *metacache)
MemoryContext AllocSetContextCreate(MemoryContext parent, const char *name, Size minContextSize, Size initBlockSize, Size maxBlockSize)
Definition: aset.c:436
uintptr_t Datum
Definition: postgres.h:374
#define DatumGetArrayTypePCopy(X)
Definition: array.h:243
#define NULL
Definition: c.h:215
#define Assert(condition)
Definition: c.h:656
size_t Size
Definition: c.h:341
#define ARR_NDIM(a)
Definition: array.h:271
void CopyArrayEls(ArrayType *array, Datum *values, bool *nulls, int nitems, int typlen, bool typbyval, char typalign, bool freedata)
Definition: arrayfuncs.c:956
#define DatumGetPointer(X)
Definition: postgres.h:557
char typalign
Definition: array.h:222
#define EOHPGetRWDatum(eohptr)
void deconstruct_array(ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
Definition: arrayfuncs.c:3446
#define att_addlength_datum(cur_offset, attlen, attdatum)
Definition: tupmacs.h:160
int errmsg(const char *fmt,...)
Definition: elog.c:795
AnyArrayType * DatumGetAnyArray(Datum d)
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:750
char * fstartptr
Definition: array.h:150
int i
Oid element_type
Definition: array.h:219
#define VARATT_IS_EXTERNAL_EXPANDED_RW(PTR)
Definition: postgres.h:323
#define PG_DETOAST_DATUM(datum)
Definition: fmgr.h:196
#define ALLOCSET_DEFAULT_MAXSIZE
Definition: memutils.h:144
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:330
#define ARR_ELEMTYPE(a)
Definition: array.h:273
MemoryContext eoh_context
int ndim
Definition: array.h:79