| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * array.h |
| 4 | * Declarations for Postgres arrays. |
| 5 | * |
| 6 | * A standard varlena array has the following internal structure: |
| 7 | * <vl_len_> - standard varlena header word |
| 8 | * <ndim> - number of dimensions of the array |
| 9 | * <dataoffset> - offset to stored data, or 0 if no nulls bitmap |
| 10 | * <elemtype> - element type OID |
| 11 | * <dimensions> - length of each array axis (C array of int) |
| 12 | * <lower bnds> - lower boundary of each dimension (C array of int) |
| 13 | * <null bitmap> - bitmap showing locations of nulls (OPTIONAL) |
| 14 | * <actual data> - whatever is the stored data |
| 15 | * |
| 16 | * The <dimensions> and <lower bnds> arrays each have ndim elements. |
| 17 | * |
| 18 | * The <null bitmap> may be omitted if the array contains no NULL elements. |
| 19 | * If it is absent, the <dataoffset> field is zero and the offset to the |
| 20 | * stored data must be computed on-the-fly. If the bitmap is present, |
| 21 | * <dataoffset> is nonzero and is equal to the offset from the array start |
| 22 | * to the first data element (including any alignment padding). The bitmap |
| 23 | * follows the same conventions as tuple null bitmaps, ie, a 1 indicates |
| 24 | * a non-null entry and the LSB of each bitmap byte is used first. |
| 25 | * |
| 26 | * The actual data starts on a MAXALIGN boundary. Individual items in the |
| 27 | * array are aligned as specified by the array element type. They are |
| 28 | * stored in row-major order (last subscript varies most rapidly). |
| 29 | * |
| 30 | * NOTE: it is important that array elements of toastable datatypes NOT be |
| 31 | * toasted, since the tupletoaster won't know they are there. (We could |
| 32 | * support compressed toasted items; only out-of-line items are dangerous. |
| 33 | * However, it seems preferable to store such items uncompressed and allow |
| 34 | * the toaster to compress the whole array as one input.) |
| 35 | * |
| 36 | * |
| 37 | * The OIDVECTOR and INT2VECTOR datatypes are storage-compatible with |
| 38 | * generic arrays, but they support only one-dimensional arrays with no |
| 39 | * nulls (and no null bitmap). They don't support being toasted, either. |
| 40 | * |
| 41 | * There are also some "fixed-length array" datatypes, such as NAME and |
| 42 | * POINT. These are simply a sequence of a fixed number of items each |
| 43 | * of a fixed-length datatype, with no overhead; the item size must be |
| 44 | * a multiple of its alignment requirement, because we do no padding. |
| 45 | * We support subscripting on these types, but array_in() and array_out() |
| 46 | * only work with varlena arrays. |
| 47 | * |
| 48 | * In addition, arrays are a major user of the "expanded object" TOAST |
| 49 | * infrastructure. This allows a varlena array to be converted to a |
| 50 | * separate representation that may include "deconstructed" Datum/isnull |
| 51 | * arrays holding the elements. |
| 52 | * |
| 53 | * |
| 54 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 55 | * Portions Copyright (c) 1994, Regents of the University of California |
| 56 | * |
| 57 | * src/include/utils/array.h |
| 58 | * |
| 59 | *------------------------------------------------------------------------- |
| 60 | */ |
| 61 | #ifndef ARRAY_H |
| 62 | #define ARRAY_H |
| 63 | |
| 64 | #include "fmgr.h" |
| 65 | #include "utils/expandeddatum.h" |
| 66 | |
| 67 | /* avoid including execnodes.h here */ |
| 68 | struct ExprState; |
| 69 | struct ExprContext; |
| 70 | |
| 71 | |
| 72 | /* |
| 73 | * Arrays are varlena objects, so must meet the varlena convention that |
| 74 | * the first int32 of the object contains the total object size in bytes. |
| 75 | * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though! |
| 76 | * |
| 77 | * CAUTION: if you change the header for ordinary arrays you will also |
| 78 | * need to change the headers for oidvector and int2vector! |
| 79 | */ |
| 80 | typedef struct |
| 81 | { |
| 82 | int32 vl_len_; /* varlena header (do not touch directly!) */ |
| 83 | int ndim; /* # of dimensions */ |
| 84 | int32 dataoffset; /* offset to data, or 0 if no bitmap */ |
| 85 | Oid elemtype; /* element type OID */ |
| 86 | } ArrayType; |
| 87 | |
| 88 | /* |
| 89 | * An expanded array is contained within a private memory context (as |
| 90 | * all expanded objects must be) and has a control structure as below. |
| 91 | * |
| 92 | * The expanded array might contain a regular "flat" array if that was the |
| 93 | * original input and we've not modified it significantly. Otherwise, the |
| 94 | * contents are represented by Datum/isnull arrays plus dimensionality and |
| 95 | * type information. We could also have both forms, if we've deconstructed |
| 96 | * the original array for access purposes but not yet changed it. For pass- |
| 97 | * by-reference element types, the Datums would point into the flat array in |
| 98 | * this situation. Once we start modifying array elements, new pass-by-ref |
| 99 | * elements are separately palloc'd within the memory context. |
| 100 | */ |
| 101 | #define EA_MAGIC 689375833 /* ID for debugging crosschecks */ |
| 102 | |
| 103 | typedef struct ExpandedArrayHeader |
| 104 | { |
| 105 | /* Standard header for expanded objects */ |
| 106 | ExpandedObjectHeader hdr; |
| 107 | |
| 108 | /* Magic value identifying an expanded array (for debugging only) */ |
| 109 | int ea_magic; |
| 110 | |
| 111 | /* Dimensionality info (always valid) */ |
| 112 | int ndims; /* # of dimensions */ |
| 113 | int *dims; /* array dimensions */ |
| 114 | int *lbound; /* index lower bounds for each dimension */ |
| 115 | |
| 116 | /* Element type info (always valid) */ |
| 117 | Oid element_type; /* element type OID */ |
| 118 | int16 typlen; /* needed info about element datatype */ |
| 119 | bool typbyval; |
| 120 | char typalign; |
| 121 | |
| 122 | /* |
| 123 | * If we have a Datum-array representation of the array, it's kept here; |
| 124 | * else dvalues/dnulls are NULL. The dvalues and dnulls arrays are always |
| 125 | * palloc'd within the object private context, but may change size from |
| 126 | * time to time. For pass-by-ref element types, dvalues entries might |
| 127 | * point either into the fstartptr..fendptr area, or to separately |
| 128 | * palloc'd chunks. Elements should always be fully detoasted, as they |
| 129 | * are in the standard flat representation. |
| 130 | * |
| 131 | * Even when dvalues is valid, dnulls can be NULL if there are no null |
| 132 | * elements. |
| 133 | */ |
| 134 | Datum *dvalues; /* array of Datums */ |
| 135 | bool *dnulls; /* array of is-null flags for Datums */ |
| 136 | int dvalueslen; /* allocated length of above arrays */ |
| 137 | int nelems; /* number of valid entries in above arrays */ |
| 138 | |
| 139 | /* |
| 140 | * flat_size is the current space requirement for the flat equivalent of |
| 141 | * the expanded array, if known; otherwise it's 0. We store this to make |
| 142 | * consecutive calls of get_flat_size cheap. |
| 143 | */ |
| 144 | Size flat_size; |
| 145 | |
| 146 | /* |
| 147 | * fvalue points to the flat representation if it is valid, else it is |
| 148 | * NULL. If we have or ever had a flat representation then |
| 149 | * fstartptr/fendptr point to the start and end+1 of its data area; this |
| 150 | * is so that we can tell which Datum pointers point into the flat |
| 151 | * representation rather than being pointers to separately palloc'd data. |
| 152 | */ |
| 153 | ArrayType *fvalue; /* must be a fully detoasted array */ |
| 154 | char *fstartptr; /* start of its data area */ |
| 155 | char *fendptr; /* end+1 of its data area */ |
| 156 | } ExpandedArrayHeader; |
| 157 | |
| 158 | /* |
| 159 | * Functions that can handle either a "flat" varlena array or an expanded |
| 160 | * array use this union to work with their input. Don't refer to "flt"; |
| 161 | * instead, cast to ArrayType. This struct nominally requires 8-byte |
| 162 | * alignment on 64-bit, but it's often used for an ArrayType having 4-byte |
| 163 | * alignment. UBSan complains about referencing "flt" in such cases. |
| 164 | */ |
| 165 | typedef union AnyArrayType |
| 166 | { |
| 167 | ArrayType flt; |
| 168 | ExpandedArrayHeader xpn; |
| 169 | } AnyArrayType; |
| 170 | |
| 171 | /* |
| 172 | * working state for accumArrayResult() and friends |
| 173 | * note that the input must be scalars (legal array elements) |
| 174 | */ |
| 175 | typedef struct ArrayBuildState |
| 176 | { |
| 177 | MemoryContext mcontext; /* where all the temp stuff is kept */ |
| 178 | Datum *dvalues; /* array of accumulated Datums */ |
| 179 | bool *dnulls; /* array of is-null flags for Datums */ |
| 180 | int alen; /* allocated length of above arrays */ |
| 181 | int nelems; /* number of valid entries in above arrays */ |
| 182 | Oid element_type; /* data type of the Datums */ |
| 183 | int16 typlen; /* needed info about datatype */ |
| 184 | bool typbyval; |
| 185 | char typalign; |
| 186 | bool private_cxt; /* use private memory context */ |
| 187 | } ArrayBuildState; |
| 188 | |
| 189 | /* |
| 190 | * working state for accumArrayResultArr() and friends |
| 191 | * note that the input must be arrays, and the same array type is returned |
| 192 | */ |
| 193 | typedef struct ArrayBuildStateArr |
| 194 | { |
| 195 | MemoryContext mcontext; /* where all the temp stuff is kept */ |
| 196 | char *data; /* accumulated data */ |
| 197 | bits8 *nullbitmap; /* bitmap of is-null flags, or NULL if none */ |
| 198 | int abytes; /* allocated length of "data" */ |
| 199 | int nbytes; /* number of bytes used so far */ |
| 200 | int aitems; /* allocated length of bitmap (in elements) */ |
| 201 | int nitems; /* total number of elements in result */ |
| 202 | int ndims; /* current dimensions of result */ |
| 203 | int dims[MAXDIM]; |
| 204 | int lbs[MAXDIM]; |
| 205 | Oid array_type; /* data type of the arrays */ |
| 206 | Oid element_type; /* data type of the array elements */ |
| 207 | bool private_cxt; /* use private memory context */ |
| 208 | } ArrayBuildStateArr; |
| 209 | |
| 210 | /* |
| 211 | * working state for accumArrayResultAny() and friends |
| 212 | * these functions handle both cases |
| 213 | */ |
| 214 | typedef struct ArrayBuildStateAny |
| 215 | { |
| 216 | /* Exactly one of these is not NULL: */ |
| 217 | ArrayBuildState *scalarstate; |
| 218 | ArrayBuildStateArr *arraystate; |
| 219 | } ArrayBuildStateAny; |
| 220 | |
| 221 | /* |
| 222 | * structure to cache type metadata needed for array manipulation |
| 223 | */ |
| 224 | typedef struct ArrayMetaState |
| 225 | { |
| 226 | Oid element_type; |
| 227 | int16 typlen; |
| 228 | bool typbyval; |
| 229 | char typalign; |
| 230 | char typdelim; |
| 231 | Oid typioparam; |
| 232 | Oid typiofunc; |
| 233 | FmgrInfo proc; |
| 234 | } ArrayMetaState; |
| 235 | |
| 236 | /* |
| 237 | * private state needed by array_map (here because caller must provide it) |
| 238 | */ |
| 239 | typedef struct ArrayMapState |
| 240 | { |
| 241 | ArrayMetaState ; |
| 242 | ArrayMetaState ; |
| 243 | } ArrayMapState; |
| 244 | |
| 245 | /* ArrayIteratorData is private in arrayfuncs.c */ |
| 246 | typedef struct ArrayIteratorData *ArrayIterator; |
| 247 | |
| 248 | /* fmgr macros for regular varlena array objects */ |
| 249 | #define DatumGetArrayTypeP(X) ((ArrayType *) PG_DETOAST_DATUM(X)) |
| 250 | #define DatumGetArrayTypePCopy(X) ((ArrayType *) PG_DETOAST_DATUM_COPY(X)) |
| 251 | #define PG_GETARG_ARRAYTYPE_P(n) DatumGetArrayTypeP(PG_GETARG_DATUM(n)) |
| 252 | #define PG_GETARG_ARRAYTYPE_P_COPY(n) DatumGetArrayTypePCopy(PG_GETARG_DATUM(n)) |
| 253 | #define PG_RETURN_ARRAYTYPE_P(x) PG_RETURN_POINTER(x) |
| 254 | |
| 255 | /* fmgr macros for expanded array objects */ |
| 256 | #define PG_GETARG_EXPANDED_ARRAY(n) DatumGetExpandedArray(PG_GETARG_DATUM(n)) |
| 257 | #define PG_GETARG_EXPANDED_ARRAYX(n, metacache) \ |
| 258 | DatumGetExpandedArrayX(PG_GETARG_DATUM(n), metacache) |
| 259 | #define PG_RETURN_EXPANDED_ARRAY(x) PG_RETURN_DATUM(EOHPGetRWDatum(&(x)->hdr)) |
| 260 | |
| 261 | /* fmgr macros for AnyArrayType (ie, get either varlena or expanded form) */ |
| 262 | #define PG_GETARG_ANY_ARRAY_P(n) DatumGetAnyArrayP(PG_GETARG_DATUM(n)) |
| 263 | |
| 264 | /* |
| 265 | * Access macros for varlena array header fields. |
| 266 | * |
| 267 | * ARR_DIMS returns a pointer to an array of array dimensions (number of |
| 268 | * elements along the various array axes). |
| 269 | * |
| 270 | * ARR_LBOUND returns a pointer to an array of array lower bounds. |
| 271 | * |
| 272 | * That is: if the third axis of an array has elements 5 through 8, then |
| 273 | * ARR_DIMS(a)[2] == 4 and ARR_LBOUND(a)[2] == 5. |
| 274 | * |
| 275 | * Unlike C, the default lower bound is 1. |
| 276 | */ |
| 277 | #define ARR_SIZE(a) VARSIZE(a) |
| 278 | #define ARR_NDIM(a) ((a)->ndim) |
| 279 | #define ARR_HASNULL(a) ((a)->dataoffset != 0) |
| 280 | #define ARR_ELEMTYPE(a) ((a)->elemtype) |
| 281 | |
| 282 | #define ARR_DIMS(a) \ |
| 283 | ((int *) (((char *) (a)) + sizeof(ArrayType))) |
| 284 | #define ARR_LBOUND(a) \ |
| 285 | ((int *) (((char *) (a)) + sizeof(ArrayType) + \ |
| 286 | sizeof(int) * ARR_NDIM(a))) |
| 287 | |
| 288 | #define ARR_NULLBITMAP(a) \ |
| 289 | (ARR_HASNULL(a) ? \ |
| 290 | (bits8 *) (((char *) (a)) + sizeof(ArrayType) + \ |
| 291 | 2 * sizeof(int) * ARR_NDIM(a)) \ |
| 292 | : (bits8 *) NULL) |
| 293 | |
| 294 | /* |
| 295 | * The total array header size (in bytes) for an array with the specified |
| 296 | * number of dimensions and total number of items. |
| 297 | */ |
| 298 | #define ARR_OVERHEAD_NONULLS(ndims) \ |
| 299 | MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims)) |
| 300 | #define ARR_OVERHEAD_WITHNULLS(ndims, nitems) \ |
| 301 | MAXALIGN(sizeof(ArrayType) + 2 * sizeof(int) * (ndims) + \ |
| 302 | ((nitems) + 7) / 8) |
| 303 | |
| 304 | #define ARR_DATA_OFFSET(a) \ |
| 305 | (ARR_HASNULL(a) ? (a)->dataoffset : ARR_OVERHEAD_NONULLS(ARR_NDIM(a))) |
| 306 | |
| 307 | /* |
| 308 | * Returns a pointer to the actual array data. |
| 309 | */ |
| 310 | #define ARR_DATA_PTR(a) \ |
| 311 | (((char *) (a)) + ARR_DATA_OFFSET(a)) |
| 312 | |
| 313 | /* |
| 314 | * Macros for working with AnyArrayType inputs. Beware multiple references! |
| 315 | */ |
| 316 | #define AARR_NDIM(a) \ |
| 317 | (VARATT_IS_EXPANDED_HEADER(a) ? \ |
| 318 | (a)->xpn.ndims : ARR_NDIM((ArrayType *) (a))) |
| 319 | #define AARR_HASNULL(a) \ |
| 320 | (VARATT_IS_EXPANDED_HEADER(a) ? \ |
| 321 | ((a)->xpn.dvalues != NULL ? (a)->xpn.dnulls != NULL : ARR_HASNULL((a)->xpn.fvalue)) : \ |
| 322 | ARR_HASNULL((ArrayType *) (a))) |
| 323 | #define AARR_ELEMTYPE(a) \ |
| 324 | (VARATT_IS_EXPANDED_HEADER(a) ? \ |
| 325 | (a)->xpn.element_type : ARR_ELEMTYPE((ArrayType *) (a))) |
| 326 | #define AARR_DIMS(a) \ |
| 327 | (VARATT_IS_EXPANDED_HEADER(a) ? \ |
| 328 | (a)->xpn.dims : ARR_DIMS((ArrayType *) (a))) |
| 329 | #define AARR_LBOUND(a) \ |
| 330 | (VARATT_IS_EXPANDED_HEADER(a) ? \ |
| 331 | (a)->xpn.lbound : ARR_LBOUND((ArrayType *) (a))) |
| 332 | |
| 333 | |
| 334 | /* |
| 335 | * GUC parameter |
| 336 | */ |
| 337 | extern bool Array_nulls; |
| 338 | |
| 339 | /* |
| 340 | * prototypes for functions defined in arrayfuncs.c |
| 341 | */ |
| 342 | extern void CopyArrayEls(ArrayType *array, |
| 343 | Datum *values, |
| 344 | bool *nulls, |
| 345 | int nitems, |
| 346 | int typlen, |
| 347 | bool typbyval, |
| 348 | char typalign, |
| 349 | bool freedata); |
| 350 | |
| 351 | extern Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, |
| 352 | int arraytyplen, int elmlen, bool elmbyval, char elmalign, |
| 353 | bool *isNull); |
| 354 | extern Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx, |
| 355 | Datum dataValue, bool isNull, |
| 356 | int arraytyplen, int elmlen, bool elmbyval, char elmalign); |
| 357 | extern Datum array_get_slice(Datum arraydatum, int nSubscripts, |
| 358 | int *upperIndx, int *lowerIndx, |
| 359 | bool *upperProvided, bool *lowerProvided, |
| 360 | int arraytyplen, int elmlen, bool elmbyval, char elmalign); |
| 361 | extern Datum array_set_slice(Datum arraydatum, int nSubscripts, |
| 362 | int *upperIndx, int *lowerIndx, |
| 363 | bool *upperProvided, bool *lowerProvided, |
| 364 | Datum srcArrayDatum, bool isNull, |
| 365 | int arraytyplen, int elmlen, bool elmbyval, char elmalign); |
| 366 | |
| 367 | extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx, |
| 368 | int arraytyplen, int elmlen, bool elmbyval, char elmalign, |
| 369 | bool *isNull); |
| 370 | extern ArrayType *array_set(ArrayType *array, int nSubscripts, int *indx, |
| 371 | Datum dataValue, bool isNull, |
| 372 | int arraytyplen, int elmlen, bool elmbyval, char elmalign); |
| 373 | |
| 374 | extern Datum array_map(Datum arrayd, |
| 375 | struct ExprState *exprstate, struct ExprContext *econtext, |
| 376 | Oid retType, ArrayMapState *amstate); |
| 377 | |
| 378 | extern void array_bitmap_copy(bits8 *destbitmap, int destoffset, |
| 379 | const bits8 *srcbitmap, int srcoffset, |
| 380 | int nitems); |
| 381 | |
| 382 | extern ArrayType *construct_array(Datum *elems, int nelems, |
| 383 | Oid elmtype, |
| 384 | int elmlen, bool elmbyval, char elmalign); |
| 385 | extern ArrayType *construct_md_array(Datum *elems, |
| 386 | bool *nulls, |
| 387 | int ndims, |
| 388 | int *dims, |
| 389 | int *lbs, |
| 390 | Oid elmtype, int elmlen, bool elmbyval, char elmalign); |
| 391 | extern ArrayType *construct_empty_array(Oid elmtype); |
| 392 | extern ExpandedArrayHeader *construct_empty_expanded_array(Oid element_type, |
| 393 | MemoryContext parentcontext, |
| 394 | ArrayMetaState *metacache); |
| 395 | extern void deconstruct_array(ArrayType *array, |
| 396 | Oid elmtype, |
| 397 | int elmlen, bool elmbyval, char elmalign, |
| 398 | Datum **elemsp, bool **nullsp, int *nelemsp); |
| 399 | extern bool array_contains_nulls(ArrayType *array); |
| 400 | |
| 401 | extern ArrayBuildState *initArrayResult(Oid element_type, |
| 402 | MemoryContext rcontext, bool subcontext); |
| 403 | extern ArrayBuildState *accumArrayResult(ArrayBuildState *astate, |
| 404 | Datum dvalue, bool disnull, |
| 405 | Oid element_type, |
| 406 | MemoryContext rcontext); |
| 407 | extern Datum makeArrayResult(ArrayBuildState *astate, |
| 408 | MemoryContext rcontext); |
| 409 | extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, |
| 410 | int *dims, int *lbs, MemoryContext rcontext, bool release); |
| 411 | |
| 412 | extern ArrayBuildStateArr *initArrayResultArr(Oid array_type, Oid element_type, |
| 413 | MemoryContext rcontext, bool subcontext); |
| 414 | extern ArrayBuildStateArr *accumArrayResultArr(ArrayBuildStateArr *astate, |
| 415 | Datum dvalue, bool disnull, |
| 416 | Oid array_type, |
| 417 | MemoryContext rcontext); |
| 418 | extern Datum makeArrayResultArr(ArrayBuildStateArr *astate, |
| 419 | MemoryContext rcontext, bool release); |
| 420 | |
| 421 | extern ArrayBuildStateAny *initArrayResultAny(Oid input_type, |
| 422 | MemoryContext rcontext, bool subcontext); |
| 423 | extern ArrayBuildStateAny *accumArrayResultAny(ArrayBuildStateAny *astate, |
| 424 | Datum dvalue, bool disnull, |
| 425 | Oid input_type, |
| 426 | MemoryContext rcontext); |
| 427 | extern Datum makeArrayResultAny(ArrayBuildStateAny *astate, |
| 428 | MemoryContext rcontext, bool release); |
| 429 | |
| 430 | extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate); |
| 431 | extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull); |
| 432 | extern void array_free_iterator(ArrayIterator iterator); |
| 433 | |
| 434 | /* |
| 435 | * prototypes for functions defined in arrayutils.c |
| 436 | */ |
| 437 | |
| 438 | extern int ArrayGetOffset(int n, const int *dim, const int *lb, const int *indx); |
| 439 | extern int ArrayGetOffset0(int n, const int *tup, const int *scale); |
| 440 | extern int ArrayGetNItems(int ndim, const int *dims); |
| 441 | extern void mda_get_range(int n, int *span, const int *st, const int *endp); |
| 442 | extern void mda_get_prod(int n, const int *range, int *prod); |
| 443 | extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span); |
| 444 | extern int mda_next_tuple(int n, int *curr, const int *span); |
| 445 | extern int32 *ArrayGetIntegerTypmods(ArrayType *arr, int *n); |
| 446 | |
| 447 | /* |
| 448 | * prototypes for functions defined in array_expanded.c |
| 449 | */ |
| 450 | extern Datum expand_array(Datum arraydatum, MemoryContext parentcontext, |
| 451 | ArrayMetaState *metacache); |
| 452 | extern ExpandedArrayHeader *DatumGetExpandedArray(Datum d); |
| 453 | extern ExpandedArrayHeader *DatumGetExpandedArrayX(Datum d, |
| 454 | ArrayMetaState *metacache); |
| 455 | extern AnyArrayType *DatumGetAnyArrayP(Datum d); |
| 456 | extern void deconstruct_expanded_array(ExpandedArrayHeader *eah); |
| 457 | |
| 458 | #endif /* ARRAY_H */ |
| 459 | |