Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/arrayfuncs.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * arrayfuncs.c
4
 *    Support functions for 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/arrayfuncs.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "postgres.h"
16
17
#include <ctype.h>
18
#include <math.h>
19
20
#include "access/transam.h"
21
#include "catalog/pg_type.h"
22
#include "common/int.h"
23
#include "funcapi.h"
24
#include "libpq/pqformat.h"
25
#include "nodes/nodeFuncs.h"
26
#include "nodes/supportnodes.h"
27
#include "optimizer/optimizer.h"
28
#include "parser/scansup.h"
29
#include "port/pg_bitutils.h"
30
#include "utils/array.h"
31
#include "utils/arrayaccess.h"
32
#include "utils/builtins.h"
33
#include "utils/datum.h"
34
#include "utils/fmgroids.h"
35
#include "utils/lsyscache.h"
36
#include "utils/memutils.h"
37
#include "utils/selfuncs.h"
38
#include "utils/typcache.h"
39
40
41
/*
42
 * GUC parameter
43
 */
44
bool    Array_nulls = true;
45
46
/*
47
 * Local definitions
48
 */
49
0
#define ASSGN  "="
50
51
#define AARR_FREE_IF_COPY(array,n) \
52
0
  do { \
53
0
    if (!VARATT_IS_EXPANDED_HEADER(array)) \
54
0
      PG_FREE_IF_COPY(array, n); \
55
0
  } while (0)
56
57
/* ReadArrayToken return type */
58
typedef enum
59
{
60
  ATOK_LEVEL_START,
61
  ATOK_LEVEL_END,
62
  ATOK_DELIM,
63
  ATOK_ELEM,
64
  ATOK_ELEM_NULL,
65
  ATOK_ERROR,
66
} ArrayToken;
67
68
/* Working state for array_iterate() */
69
typedef struct ArrayIteratorData
70
{
71
  /* basic info about the array, set up during array_create_iterator() */
72
  ArrayType  *arr;      /* array we're iterating through */
73
  uint8    *nullbitmap;   /* its null bitmap, if any */
74
  int     nitems;     /* total number of elements in array */
75
  int16   typlen;     /* element type's length */
76
  bool    typbyval;   /* element type's byval property */
77
  char    typalign;   /* element type's align property */
78
  uint8   typalignby;   /* typalign mapped to numeric alignment */
79
80
  /* information about the requested slice size */
81
  int     slice_ndim;   /* slice dimension, or 0 if not slicing */
82
  int     slice_len;    /* number of elements per slice */
83
  int      *slice_dims;   /* slice dims array */
84
  int      *slice_lbound; /* slice lbound array */
85
  Datum    *slice_values; /* workspace of length slice_len */
86
  bool     *slice_nulls;  /* workspace of length slice_len */
87
88
  /* current position information, updated on each iteration */
89
  char     *data_ptr;   /* our current position in the array */
90
  int     current_item; /* the item # we're at in the array */
91
} ArrayIteratorData;
92
93
static bool ReadArrayDimensions(char **srcptr, int *ndim_p,
94
                int *dim, int *lBound,
95
                const char *origStr, Node *escontext);
96
static bool ReadDimensionInt(char **srcptr, int *result,
97
               const char *origStr, Node *escontext);
98
static bool ReadArrayStr(char **srcptr,
99
             FmgrInfo *inputproc, Oid typioparam, int32 typmod,
100
             char typdelim,
101
             int typlen, bool typbyval, char typalign,
102
             int *ndim_p, int *dim,
103
             int *nitems_p,
104
             Datum **values_p, bool **nulls_p,
105
             const char *origStr, Node *escontext);
106
static ArrayToken ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
107
                 const char *origStr, Node *escontext);
108
static void ReadArrayBinary(StringInfo buf, int nitems,
109
              FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
110
              int typlen, bool typbyval, char typalign,
111
              Datum *values, bool *nulls,
112
              bool *hasnulls, int32 *nbytes);
113
static Datum array_get_element_expanded(Datum arraydatum,
114
                    int nSubscripts, int *indx,
115
                    int arraytyplen,
116
                    int elmlen, bool elmbyval, char elmalign,
117
                    bool *isNull);
118
static Datum array_set_element_expanded(Datum arraydatum,
119
                    int nSubscripts, int *indx,
120
                    Datum dataValue, bool isNull,
121
                    int arraytyplen,
122
                    int elmlen, bool elmbyval, char elmalign);
123
static bool array_get_isnull(const uint8 *nullbitmap, int offset);
124
static void array_set_isnull(uint8 *nullbitmap, int offset, bool isNull);
125
static Datum ArrayCast(char *value, bool byval, int len);
126
static int  ArrayCastAndSet(Datum src,
127
              int typlen, bool typbyval, uint8 typalignby,
128
              char *dest);
129
static char *array_seek(char *ptr, int offset, uint8 *nullbitmap, int nitems,
130
            int typlen, bool typbyval, char typalign);
131
static int  array_nelems_size(char *ptr, int offset, uint8 *nullbitmap,
132
                int nitems, int typlen, bool typbyval, char typalign);
133
static int  array_copy(char *destptr, int nitems,
134
             char *srcptr, int offset, uint8 *nullbitmap,
135
             int typlen, bool typbyval, char typalign);
136
static int  array_slice_size(char *arraydataptr, uint8 *arraynullsptr,
137
               int ndim, int *dim, int *lb,
138
               int *st, int *endp,
139
               int typlen, bool typbyval, char typalign);
140
static void array_extract_slice(ArrayType *newarray,
141
                int ndim, int *dim, int *lb,
142
                char *arraydataptr, uint8 *arraynullsptr,
143
                int *st, int *endp,
144
                int typlen, bool typbyval, char typalign);
145
static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
146
                 ArrayType *srcArray,
147
                 int ndim, int *dim, int *lb,
148
                 int *st, int *endp,
149
                 int typlen, bool typbyval, char typalign);
150
static int  array_cmp(FunctionCallInfo fcinfo);
151
static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
152
                    Oid elmtype, int dataoffset);
153
static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
154
                    Datum value, bool isnull, Oid elmtype,
155
                    FunctionCallInfo fcinfo);
156
static ArrayType *array_replace_internal(ArrayType *array,
157
                     Datum search, bool search_isnull,
158
                     Datum replace, bool replace_isnull,
159
                     bool remove, Oid collation,
160
                     FunctionCallInfo fcinfo);
161
static int  width_bucket_array_float8(Datum operand, ArrayType *thresholds);
162
static int  width_bucket_array_fixed(Datum operand,
163
                   ArrayType *thresholds,
164
                   Oid collation,
165
                   TypeCacheEntry *typentry);
166
static int  width_bucket_array_variable(Datum operand,
167
                    ArrayType *thresholds,
168
                    Oid collation,
169
                    TypeCacheEntry *typentry);
170
171
172
/*
173
 * array_in :
174
 *      converts an array from the external format in "string" to
175
 *      its internal format.
176
 *
177
 * return value :
178
 *      the internal representation of the input array
179
 */
180
Datum
181
array_in(PG_FUNCTION_ARGS)
182
0
{
183
0
  char     *string = PG_GETARG_CSTRING(0);  /* external form */
184
0
  Oid     element_type = PG_GETARG_OID(1);  /* type of an array
185
                           * element */
186
0
  int32   typmod = PG_GETARG_INT32(2);  /* typmod for array elements */
187
0
  Node     *escontext = fcinfo->context;
188
0
  int     typlen;
189
0
  bool    typbyval;
190
0
  char    typalign;
191
0
  uint8   typalignby;
192
0
  char    typdelim;
193
0
  Oid     typioparam;
194
0
  char     *p;
195
0
  int     nitems;
196
0
  Datum    *values;
197
0
  bool     *nulls;
198
0
  bool    hasnulls;
199
0
  int32   nbytes;
200
0
  int32   dataoffset;
201
0
  ArrayType  *retval;
202
0
  int     ndim,
203
0
        dim[MAXDIM],
204
0
        lBound[MAXDIM];
205
0
  ArrayMetaState *my_extra;
206
207
  /*
208
   * We arrange to look up info about element type, including its input
209
   * conversion proc, only once per series of calls, assuming the element
210
   * type doesn't change underneath us.
211
   */
212
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
213
0
  if (my_extra == NULL)
214
0
  {
215
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
216
0
                            sizeof(ArrayMetaState));
217
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
218
0
    my_extra->element_type = ~element_type;
219
0
  }
220
221
0
  if (my_extra->element_type != element_type)
222
0
  {
223
    /*
224
     * Get info about element type, including its input conversion proc
225
     */
226
0
    get_type_io_data(element_type, IOFunc_input,
227
0
             &my_extra->typlen, &my_extra->typbyval,
228
0
             &my_extra->typalign, &my_extra->typdelim,
229
0
             &my_extra->typioparam, &my_extra->typiofunc);
230
0
    fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
231
0
            fcinfo->flinfo->fn_mcxt);
232
0
    my_extra->element_type = element_type;
233
0
  }
234
0
  typlen = my_extra->typlen;
235
0
  typbyval = my_extra->typbyval;
236
0
  typalign = my_extra->typalign;
237
0
  typalignby = typalign_to_alignby(typalign);
238
0
  typdelim = my_extra->typdelim;
239
0
  typioparam = my_extra->typioparam;
240
241
  /*
242
   * Initialize dim[] and lBound[] for ReadArrayStr, in case there is no
243
   * explicit dimension info.  (If there is, ReadArrayDimensions will
244
   * overwrite this.)
245
   */
246
0
  for (int i = 0; i < MAXDIM; i++)
247
0
  {
248
0
    dim[i] = -1;      /* indicates "not yet known" */
249
0
    lBound[i] = 1;      /* default lower bound */
250
0
  }
251
252
  /*
253
   * Start processing the input string.
254
   *
255
   * If the input string starts with dimension info, read and use that.
256
   * Otherwise, we'll determine the dimensions during ReadArrayStr.
257
   */
258
0
  p = string;
259
0
  if (!ReadArrayDimensions(&p, &ndim, dim, lBound, string, escontext))
260
0
    return (Datum) 0;
261
262
0
  if (ndim == 0)
263
0
  {
264
    /* No array dimensions, so next character should be a left brace */
265
0
    if (*p != '{')
266
0
      ereturn(escontext, (Datum) 0,
267
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
268
0
           errmsg("malformed array literal: \"%s\"", string),
269
0
           errdetail("Array value must start with \"{\" or dimension information.")));
270
0
  }
271
0
  else
272
0
  {
273
    /* If array dimensions are given, expect '=' operator */
274
0
    if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
275
0
      ereturn(escontext, (Datum) 0,
276
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
277
0
           errmsg("malformed array literal: \"%s\"", string),
278
0
           errdetail("Missing \"%s\" after array dimensions.",
279
0
                 ASSGN)));
280
0
    p += strlen(ASSGN);
281
    /* Allow whitespace after it */
282
0
    while (scanner_isspace(*p))
283
0
      p++;
284
285
0
    if (*p != '{')
286
0
      ereturn(escontext, (Datum) 0,
287
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
288
0
           errmsg("malformed array literal: \"%s\"", string),
289
0
           errdetail("Array contents must start with \"{\".")));
290
0
  }
291
292
  /* Parse the value part, in the curly braces: { ... } */
293
0
  if (!ReadArrayStr(&p,
294
0
            &my_extra->proc, typioparam, typmod,
295
0
            typdelim,
296
0
            typlen, typbyval, typalign,
297
0
            &ndim,
298
0
            dim,
299
0
            &nitems,
300
0
            &values, &nulls,
301
0
            string,
302
0
            escontext))
303
0
    return (Datum) 0;
304
305
  /* only whitespace is allowed after the closing brace */
306
0
  while (*p)
307
0
  {
308
0
    if (!scanner_isspace(*p++))
309
0
      ereturn(escontext, (Datum) 0,
310
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
311
0
           errmsg("malformed array literal: \"%s\"", string),
312
0
           errdetail("Junk after closing right brace.")));
313
0
  }
314
315
  /* Empty array? */
316
0
  if (nitems == 0)
317
0
    PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
318
319
  /*
320
   * Check for nulls, compute total data space needed
321
   */
322
0
  hasnulls = false;
323
0
  nbytes = 0;
324
0
  for (int i = 0; i < nitems; i++)
325
0
  {
326
0
    if (nulls[i])
327
0
      hasnulls = true;
328
0
    else
329
0
    {
330
      /* let's just make sure data is not toasted */
331
0
      if (typlen == -1)
332
0
        values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
333
0
      nbytes = att_addlength_datum(nbytes, typlen, values[i]);
334
0
      nbytes = att_nominal_alignby(nbytes, typalignby);
335
      /* check for overflow of total request */
336
0
      if (!AllocSizeIsValid(nbytes))
337
0
        ereturn(escontext, (Datum) 0,
338
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
339
0
             errmsg("array size exceeds the maximum allowed (%zu)",
340
0
                MaxAllocSize)));
341
0
    }
342
0
  }
343
0
  if (hasnulls)
344
0
  {
345
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
346
0
    nbytes += dataoffset;
347
0
  }
348
0
  else
349
0
  {
350
0
    dataoffset = 0;     /* marker for no null bitmap */
351
0
    nbytes += ARR_OVERHEAD_NONULLS(ndim);
352
0
  }
353
354
  /*
355
   * Construct the final array datum
356
   */
357
0
  retval = (ArrayType *) palloc0(nbytes);
358
0
  SET_VARSIZE(retval, nbytes);
359
0
  retval->ndim = ndim;
360
0
  retval->dataoffset = dataoffset;
361
362
  /*
363
   * This comes from the array's pg_type.typelem (which points to the base
364
   * data type's pg_type.oid) and stores system oids in user tables. This
365
   * oid must be preserved by binary upgrades.
366
   */
367
0
  retval->elemtype = element_type;
368
0
  memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
369
0
  memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
370
371
0
  CopyArrayEls(retval,
372
0
         values, nulls, nitems,
373
0
         typlen, typbyval, typalign,
374
0
         true);
375
376
0
  pfree(values);
377
0
  pfree(nulls);
378
379
0
  PG_RETURN_ARRAYTYPE_P(retval);
380
0
}
381
382
/*
383
 * ReadArrayDimensions
384
 *   parses the array dimensions part of the input and converts the values
385
 *   to internal format.
386
 *
387
 * On entry, *srcptr points to the string to parse. It is advanced to point
388
 * after whitespace (if any) and dimension info (if any).
389
 *
390
 * *ndim_p, dim[], and lBound[] are output variables. They are filled with the
391
 * number of dimensions (<= MAXDIM), the lengths of each dimension, and the
392
 * lower subscript bounds, respectively.  If no dimension info appears,
393
 * *ndim_p will be set to zero, and dim[] and lBound[] are unchanged.
394
 *
395
 * 'origStr' is the original input string, used only in error messages.
396
 * If *escontext points to an ErrorSaveContext, details of any error are
397
 * reported there.
398
 *
399
 * Result:
400
 *  true for success, false for failure (if escontext is provided).
401
 *
402
 * Note that dim[] and lBound[] are allocated by the caller, and must have
403
 * MAXDIM elements.
404
 */
405
static bool
406
ReadArrayDimensions(char **srcptr, int *ndim_p, int *dim, int *lBound,
407
          const char *origStr, Node *escontext)
408
0
{
409
0
  char     *p = *srcptr;
410
0
  int     ndim;
411
412
  /*
413
   * Dimension info takes the form of one or more [n] or [m:n] items.  This
414
   * loop iterates once per dimension item.
415
   */
416
0
  ndim = 0;
417
0
  for (;;)
418
0
  {
419
0
    char     *q;
420
0
    int     ub;
421
0
    int     i;
422
423
    /*
424
     * Note: we currently allow whitespace between, but not within,
425
     * dimension items.
426
     */
427
0
    while (scanner_isspace(*p))
428
0
      p++;
429
0
    if (*p != '[')
430
0
      break;       /* no more dimension items */
431
0
    p++;
432
0
    if (ndim >= MAXDIM)
433
0
      ereturn(escontext, false,
434
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
435
0
           errmsg("number of array dimensions exceeds the maximum allowed (%d)",
436
0
              MAXDIM)));
437
438
0
    q = p;
439
0
    if (!ReadDimensionInt(&p, &i, origStr, escontext))
440
0
      return false;
441
0
    if (p == q)       /* no digits? */
442
0
      ereturn(escontext, false,
443
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444
0
           errmsg("malformed array literal: \"%s\"", origStr),
445
0
           errdetail("\"[\" must introduce explicitly-specified array dimensions.")));
446
447
0
    if (*p == ':')
448
0
    {
449
      /* [m:n] format */
450
0
      lBound[ndim] = i;
451
0
      p++;
452
0
      q = p;
453
0
      if (!ReadDimensionInt(&p, &ub, origStr, escontext))
454
0
        return false;
455
0
      if (p == q)     /* no digits? */
456
0
        ereturn(escontext, false,
457
0
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
458
0
             errmsg("malformed array literal: \"%s\"", origStr),
459
0
             errdetail("Missing array dimension value.")));
460
0
    }
461
0
    else
462
0
    {
463
      /* [n] format */
464
0
      lBound[ndim] = 1;
465
0
      ub = i;
466
0
    }
467
0
    if (*p != ']')
468
0
      ereturn(escontext, false,
469
0
          (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
470
0
           errmsg("malformed array literal: \"%s\"", origStr),
471
0
           errdetail("Missing \"%s\" after array dimensions.",
472
0
                 "]")));
473
0
    p++;
474
475
    /*
476
     * Note: we could accept ub = lb-1 to represent a zero-length
477
     * dimension.  However, that would result in an empty array, for which
478
     * we don't keep any dimension data, so that e.g. [1:0] and [101:100]
479
     * would be equivalent.  Given the lack of field demand, there seems
480
     * little point in allowing such cases.
481
     */
482
0
    if (ub < lBound[ndim])
483
0
      ereturn(escontext, false,
484
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
485
0
           errmsg("upper bound cannot be less than lower bound")));
486
487
    /* Upper bound of INT_MAX must be disallowed, cf ArrayCheckBounds() */
488
0
    if (ub == INT_MAX)
489
0
      ereturn(escontext, false,
490
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
491
0
           errmsg("array upper bound is too large: %d", ub)));
492
493
    /* Compute "ub - lBound[ndim] + 1", detecting overflow */
494
0
    if (pg_sub_s32_overflow(ub, lBound[ndim], &ub) ||
495
0
      pg_add_s32_overflow(ub, 1, &ub))
496
0
      ereturn(escontext, false,
497
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
498
0
           errmsg("array size exceeds the maximum allowed (%zu)",
499
0
              MaxArraySize)));
500
501
0
    dim[ndim] = ub;
502
0
    ndim++;
503
0
  }
504
505
0
  *srcptr = p;
506
0
  *ndim_p = ndim;
507
0
  return true;
508
0
}
509
510
/*
511
 * ReadDimensionInt
512
 *   parse an integer, for the array dimensions
513
 *
514
 * On entry, *srcptr points to the string to parse. It is advanced past the
515
 * digits of the integer. If there are no digits, returns true and leaves
516
 * *srcptr unchanged.
517
 *
518
 * Result:
519
 *  true for success, false for failure (if escontext is provided).
520
 *  On success, the parsed integer is returned in *result.
521
 */
522
static bool
523
ReadDimensionInt(char **srcptr, int *result,
524
         const char *origStr, Node *escontext)
525
0
{
526
0
  char     *p = *srcptr;
527
0
  long    l;
528
529
  /* don't accept leading whitespace */
530
0
  if (!isdigit((unsigned char) *p) && *p != '-' && *p != '+')
531
0
  {
532
0
    *result = 0;
533
0
    return true;
534
0
  }
535
536
0
  errno = 0;
537
0
  l = strtol(p, srcptr, 10);
538
539
0
  if (errno == ERANGE || l > PG_INT32_MAX || l < PG_INT32_MIN)
540
0
    ereturn(escontext, false,
541
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
542
0
         errmsg("array bound is out of integer range")));
543
544
0
  *result = (int) l;
545
0
  return true;
546
0
}
547
548
/*
549
 * ReadArrayStr :
550
 *   parses the array string pointed to by *srcptr and converts the values
551
 *   to internal format.  Determines the array dimensions as it goes.
552
 *
553
 * On entry, *srcptr points to the string to parse (it must point to a '{').
554
 * On successful return, it is advanced to point past the closing '}'.
555
 *
556
 * If dimensions were specified explicitly, they are passed in *ndim_p and
557
 * dim[].  This function will check that the array values match the specified
558
 * dimensions.  If dimensions were not given, caller must pass *ndim_p == 0
559
 * and initialize all elements of dim[] to -1.  Then this function will
560
 * deduce the dimensions from the structure of the input and store them in
561
 * *ndim_p and the dim[] array.
562
 *
563
 * Element type information:
564
 *  inputproc: type-specific input procedure for element datatype.
565
 *  typioparam, typmod: auxiliary values to pass to inputproc.
566
 *  typdelim: the value delimiter (type-specific).
567
 *  typlen, typbyval, typalign: storage parameters of element datatype.
568
 *
569
 * Outputs:
570
 *  *ndim_p, dim: dimensions deduced from the input structure.
571
 *  *nitems_p: total number of elements.
572
 *  *values_p[]: palloc'd array, filled with converted data values.
573
 *  *nulls_p[]: palloc'd array, filled with is-null markers.
574
 *
575
 * 'origStr' is the original input string, used only in error messages.
576
 * If *escontext points to an ErrorSaveContext, details of any error are
577
 * reported there.
578
 *
579
 * Result:
580
 *  true for success, false for failure (if escontext is provided).
581
 */
582
static bool
583
ReadArrayStr(char **srcptr,
584
       FmgrInfo *inputproc,
585
       Oid typioparam,
586
       int32 typmod,
587
       char typdelim,
588
       int typlen,
589
       bool typbyval,
590
       char typalign,
591
       int *ndim_p,
592
       int *dim,
593
       int *nitems_p,
594
       Datum **values_p,
595
       bool **nulls_p,
596
       const char *origStr,
597
       Node *escontext)
598
0
{
599
0
  int     ndim = *ndim_p;
600
0
  bool    dimensions_specified = (ndim != 0);
601
0
  int     maxitems;
602
0
  Datum    *values;
603
0
  bool     *nulls;
604
0
  StringInfoData elembuf;
605
0
  int     nest_level;
606
0
  int     nitems;
607
0
  bool    ndim_frozen;
608
0
  bool    expect_delim;
609
0
  int     nelems[MAXDIM];
610
611
  /* Allocate some starting output workspace; we'll enlarge as needed */
612
0
  maxitems = 16;
613
0
  values = palloc_array(Datum, maxitems);
614
0
  nulls = palloc_array(bool, maxitems);
615
616
  /* Allocate workspace to hold (string representation of) one element */
617
0
  initStringInfo(&elembuf);
618
619
  /* Loop below assumes first token is ATOK_LEVEL_START */
620
0
  Assert(**srcptr == '{');
621
622
  /* Parse tokens until we reach the matching right brace */
623
0
  nest_level = 0;
624
0
  nitems = 0;
625
0
  ndim_frozen = dimensions_specified;
626
0
  expect_delim = false;
627
0
  do
628
0
  {
629
0
    ArrayToken  tok;
630
631
0
    tok = ReadArrayToken(srcptr, &elembuf, typdelim, origStr, escontext);
632
633
0
    switch (tok)
634
0
    {
635
0
      case ATOK_LEVEL_START:
636
        /* Can't write left brace where delim is expected */
637
0
        if (expect_delim)
638
0
          ereturn(escontext, false,
639
0
              (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
640
0
               errmsg("malformed array literal: \"%s\"", origStr),
641
0
               errdetail("Unexpected \"%c\" character.", '{')));
642
643
        /* Initialize element counting in the new level */
644
0
        if (nest_level >= MAXDIM)
645
0
          ereturn(escontext, false,
646
0
              (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
647
0
               errmsg("number of array dimensions exceeds the maximum allowed (%d)",
648
0
                  MAXDIM)));
649
650
0
        nelems[nest_level] = 0;
651
0
        nest_level++;
652
0
        if (nest_level > ndim)
653
0
        {
654
          /* Can't increase ndim once it's frozen */
655
0
          if (ndim_frozen)
656
0
            goto dimension_error;
657
0
          ndim = nest_level;
658
0
        }
659
0
        break;
660
661
0
      case ATOK_LEVEL_END:
662
        /* Can't get here with nest_level == 0 */
663
0
        Assert(nest_level > 0);
664
665
        /*
666
         * We allow a right brace to terminate an empty sub-array,
667
         * otherwise it must occur where we expect a delimiter.
668
         */
669
0
        if (nelems[nest_level - 1] > 0 && !expect_delim)
670
0
          ereturn(escontext, false,
671
0
              (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
672
0
               errmsg("malformed array literal: \"%s\"", origStr),
673
0
               errdetail("Unexpected \"%c\" character.",
674
0
                     '}')));
675
0
        nest_level--;
676
        /* Nested sub-arrays count as elements of outer level */
677
0
        if (nest_level > 0)
678
0
          nelems[nest_level - 1]++;
679
680
        /*
681
         * Note: if we had dimensionality info, then dim[nest_level]
682
         * is initially non-negative, and we'll check each sub-array's
683
         * length against that.
684
         */
685
0
        if (dim[nest_level] < 0)
686
0
        {
687
          /* Save length of first sub-array of this level */
688
0
          dim[nest_level] = nelems[nest_level];
689
0
        }
690
0
        else if (nelems[nest_level] != dim[nest_level])
691
0
        {
692
          /* Subsequent sub-arrays must have same length */
693
0
          goto dimension_error;
694
0
        }
695
696
        /*
697
         * Must have a delim or another right brace following, unless
698
         * we have reached nest_level 0, where this won't matter.
699
         */
700
0
        expect_delim = true;
701
0
        break;
702
703
0
      case ATOK_DELIM:
704
0
        if (!expect_delim)
705
0
          ereturn(escontext, false,
706
0
              (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
707
0
               errmsg("malformed array literal: \"%s\"", origStr),
708
0
               errdetail("Unexpected \"%c\" character.",
709
0
                     typdelim)));
710
0
        expect_delim = false;
711
0
        break;
712
713
0
      case ATOK_ELEM:
714
0
      case ATOK_ELEM_NULL:
715
        /* Can't get here with nest_level == 0 */
716
0
        Assert(nest_level > 0);
717
718
        /* Disallow consecutive ELEM tokens */
719
0
        if (expect_delim)
720
0
          ereturn(escontext, false,
721
0
              (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
722
0
               errmsg("malformed array literal: \"%s\"", origStr),
723
0
               errdetail("Unexpected array element.")));
724
725
        /* Enlarge the values/nulls arrays if needed */
726
0
        if (nitems >= maxitems)
727
0
        {
728
0
          if (maxitems >= MaxArraySize)
729
0
            ereturn(escontext, false,
730
0
                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
731
0
                 errmsg("array size exceeds the maximum allowed (%zu)",
732
0
                    MaxArraySize)));
733
0
          maxitems = Min(maxitems * 2, MaxArraySize);
734
0
          values = repalloc_array(values, Datum, maxitems);
735
0
          nulls = repalloc_array(nulls, bool, maxitems);
736
0
        }
737
738
        /* Read the element's value, or check that NULL is allowed */
739
0
        if (!InputFunctionCallSafe(inputproc,
740
0
                       (tok == ATOK_ELEM_NULL) ? NULL : elembuf.data,
741
0
                       typioparam, typmod,
742
0
                       escontext,
743
0
                       &values[nitems]))
744
0
          return false;
745
0
        nulls[nitems] = (tok == ATOK_ELEM_NULL);
746
0
        nitems++;
747
748
        /*
749
         * Once we have found an element, the number of dimensions can
750
         * no longer increase, and subsequent elements must all be at
751
         * the same nesting depth.
752
         */
753
0
        ndim_frozen = true;
754
0
        if (nest_level != ndim)
755
0
          goto dimension_error;
756
        /* Count the new element */
757
0
        nelems[nest_level - 1]++;
758
759
        /* Must have a delim or a right brace following */
760
0
        expect_delim = true;
761
0
        break;
762
763
0
      case ATOK_ERROR:
764
0
        return false;
765
0
    }
766
0
  } while (nest_level > 0);
767
768
  /* Clean up and return results */
769
0
  pfree(elembuf.data);
770
771
0
  *ndim_p = ndim;
772
0
  *nitems_p = nitems;
773
0
  *values_p = values;
774
0
  *nulls_p = nulls;
775
0
  return true;
776
777
0
dimension_error:
778
0
  if (dimensions_specified)
779
0
    ereturn(escontext, false,
780
0
        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
781
0
         errmsg("malformed array literal: \"%s\"", origStr),
782
0
         errdetail("Specified array dimensions do not match array contents.")));
783
0
  else
784
0
    ereturn(escontext, false,
785
0
        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
786
0
         errmsg("malformed array literal: \"%s\"", origStr),
787
0
         errdetail("Multidimensional arrays must have sub-arrays with matching dimensions.")));
788
0
}
789
790
/*
791
 * ReadArrayToken
792
 *   read one token from an array value string
793
 *
794
 * Starts scanning from *srcptr.  On non-error return, *srcptr is
795
 * advanced past the token.
796
 *
797
 * If the token is ATOK_ELEM, the de-escaped string is returned in elembuf.
798
 */
799
static ArrayToken
800
ReadArrayToken(char **srcptr, StringInfo elembuf, char typdelim,
801
         const char *origStr, Node *escontext)
802
0
{
803
0
  char     *p = *srcptr;
804
0
  int     dstlen;
805
0
  bool    has_escapes;
806
807
0
  resetStringInfo(elembuf);
808
809
  /* Identify token type.  Loop advances over leading whitespace. */
810
0
  for (;;)
811
0
  {
812
0
    switch (*p)
813
0
    {
814
0
      case '\0':
815
0
        goto ending_error;
816
0
      case '{':
817
0
        *srcptr = p + 1;
818
0
        return ATOK_LEVEL_START;
819
0
      case '}':
820
0
        *srcptr = p + 1;
821
0
        return ATOK_LEVEL_END;
822
0
      case '"':
823
0
        p++;
824
0
        goto quoted_element;
825
0
      default:
826
0
        if (*p == typdelim)
827
0
        {
828
0
          *srcptr = p + 1;
829
0
          return ATOK_DELIM;
830
0
        }
831
0
        if (scanner_isspace(*p))
832
0
        {
833
0
          p++;
834
0
          continue;
835
0
        }
836
0
        goto unquoted_element;
837
0
    }
838
0
  }
839
840
0
quoted_element:
841
0
  for (;;)
842
0
  {
843
0
    switch (*p)
844
0
    {
845
0
      case '\0':
846
0
        goto ending_error;
847
0
      case '\\':
848
        /* Skip backslash, copy next character as-is. */
849
0
        p++;
850
0
        if (*p == '\0')
851
0
          goto ending_error;
852
0
        appendStringInfoChar(elembuf, *p++);
853
0
        break;
854
0
      case '"':
855
856
        /*
857
         * If next non-whitespace isn't typdelim or a brace, complain
858
         * about incorrect quoting.  While we could leave such cases
859
         * to be detected as incorrect token sequences, the resulting
860
         * message wouldn't be as helpful.  (We could also give the
861
         * incorrect-quoting error when next is '{', but treating that
862
         * as a token sequence error seems better.)
863
         */
864
0
        while (*(++p) != '\0')
865
0
        {
866
0
          if (*p == typdelim || *p == '}' || *p == '{')
867
0
          {
868
0
            *srcptr = p;
869
0
            return ATOK_ELEM;
870
0
          }
871
0
          if (!scanner_isspace(*p))
872
0
            ereturn(escontext, ATOK_ERROR,
873
0
                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
874
0
                 errmsg("malformed array literal: \"%s\"", origStr),
875
0
                 errdetail("Incorrectly quoted array element.")));
876
0
        }
877
0
        goto ending_error;
878
0
      default:
879
0
        appendStringInfoChar(elembuf, *p++);
880
0
        break;
881
0
    }
882
0
  }
883
884
0
unquoted_element:
885
886
  /*
887
   * We don't include trailing whitespace in the result.  dstlen tracks how
888
   * much of the output string is known to not be trailing whitespace.
889
   */
890
0
  dstlen = 0;
891
0
  has_escapes = false;
892
0
  for (;;)
893
0
  {
894
0
    switch (*p)
895
0
    {
896
0
      case '\0':
897
0
        goto ending_error;
898
0
      case '{':
899
0
        ereturn(escontext, ATOK_ERROR,
900
0
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
901
0
             errmsg("malformed array literal: \"%s\"", origStr),
902
0
             errdetail("Unexpected \"%c\" character.",
903
0
                   '{')));
904
0
      case '"':
905
        /* Must double-quote all or none of an element. */
906
0
        ereturn(escontext, ATOK_ERROR,
907
0
            (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
908
0
             errmsg("malformed array literal: \"%s\"", origStr),
909
0
             errdetail("Incorrectly quoted array element.")));
910
0
      case '\\':
911
        /* Skip backslash, copy next character as-is. */
912
0
        p++;
913
0
        if (*p == '\0')
914
0
          goto ending_error;
915
0
        appendStringInfoChar(elembuf, *p++);
916
0
        dstlen = elembuf->len;  /* treat it as non-whitespace */
917
0
        has_escapes = true;
918
0
        break;
919
0
      default:
920
        /* End of elem? */
921
0
        if (*p == typdelim || *p == '}')
922
0
        {
923
          /* hack: truncate the output string to dstlen */
924
0
          elembuf->data[dstlen] = '\0';
925
0
          elembuf->len = dstlen;
926
0
          *srcptr = p;
927
          /* Check if it's unquoted "NULL" */
928
0
          if (Array_nulls && !has_escapes &&
929
0
            pg_strcasecmp(elembuf->data, "NULL") == 0)
930
0
            return ATOK_ELEM_NULL;
931
0
          else
932
0
            return ATOK_ELEM;
933
0
        }
934
0
        appendStringInfoChar(elembuf, *p);
935
0
        if (!scanner_isspace(*p))
936
0
          dstlen = elembuf->len;
937
0
        p++;
938
0
        break;
939
0
    }
940
0
  }
941
942
0
ending_error:
943
0
  ereturn(escontext, ATOK_ERROR,
944
0
      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
945
0
       errmsg("malformed array literal: \"%s\"", origStr),
946
0
       errdetail("Unexpected end of input.")));
947
0
}
948
949
/*
950
 * Copy data into an array object from a temporary array of Datums.
951
 *
952
 * array: array object (with header fields already filled in)
953
 * values: array of Datums to be copied
954
 * nulls: array of is-null flags (can be NULL if no nulls)
955
 * nitems: number of Datums to be copied
956
 * typbyval, typlen, typalign: info about element datatype
957
 * freedata: if true and element type is pass-by-ref, pfree data values
958
 * referenced by Datums after copying them.
959
 *
960
 * If the input data is of varlena type, the caller must have ensured that
961
 * the values are not toasted.  (Doing it here doesn't work since the
962
 * caller has already allocated space for the array...)
963
 */
964
void
965
CopyArrayEls(ArrayType *array,
966
       const Datum *values,
967
       const bool *nulls,
968
       int nitems,
969
       int typlen,
970
       bool typbyval,
971
       char typalign,
972
       bool freedata)
973
0
{
974
0
  char     *p = ARR_DATA_PTR(array);
975
0
  uint8    *bitmap = ARR_NULLBITMAP(array);
976
0
  int     bitval = 0;
977
0
  int     bitmask = 1;
978
0
  uint8   typalignby = typalign_to_alignby(typalign);
979
0
  int     i;
980
981
0
  if (typbyval)
982
0
    freedata = false;
983
984
0
  for (i = 0; i < nitems; i++)
985
0
  {
986
0
    if (nulls && nulls[i])
987
0
    {
988
0
      if (!bitmap)   /* shouldn't happen */
989
0
        elog(ERROR, "null array element where not supported");
990
      /* bitmap bit stays 0 */
991
0
    }
992
0
    else
993
0
    {
994
0
      bitval |= bitmask;
995
0
      p += ArrayCastAndSet(values[i], typlen, typbyval, typalignby, p);
996
0
      if (freedata)
997
0
        pfree(DatumGetPointer(values[i]));
998
0
    }
999
0
    if (bitmap)
1000
0
    {
1001
0
      bitmask <<= 1;
1002
0
      if (bitmask == 0x100)
1003
0
      {
1004
0
        *bitmap++ = bitval;
1005
0
        bitval = 0;
1006
0
        bitmask = 1;
1007
0
      }
1008
0
    }
1009
0
  }
1010
1011
0
  if (bitmap && bitmask != 1)
1012
0
    *bitmap = bitval;
1013
0
}
1014
1015
/*
1016
 * array_out :
1017
 *       takes the internal representation of an array and returns a string
1018
 *      containing the array in its external format.
1019
 */
1020
Datum
1021
array_out(PG_FUNCTION_ARGS)
1022
0
{
1023
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1024
0
  Oid     element_type = AARR_ELEMTYPE(v);
1025
0
  int     typlen;
1026
0
  bool    typbyval;
1027
0
  char    typalign;
1028
0
  char    typdelim;
1029
0
  char     *p,
1030
0
         *tmp,
1031
0
         *retval,
1032
0
        **values,
1033
0
        dims_str[(MAXDIM * 33) + 2];
1034
1035
  /*
1036
   * 33 per dim since we assume 15 digits per number + ':' +'[]'
1037
   *
1038
   * +2 allows for assignment operator + trailing null
1039
   */
1040
0
  bool     *needquotes,
1041
0
        needdims = false;
1042
0
  size_t    overall_length;
1043
0
  int     nitems,
1044
0
        i,
1045
0
        j,
1046
0
        k,
1047
0
        indx[MAXDIM];
1048
0
  int     ndim,
1049
0
         *dims,
1050
0
         *lb;
1051
0
  array_iter  iter;
1052
0
  ArrayMetaState *my_extra;
1053
1054
  /*
1055
   * We arrange to look up info about element type, including its output
1056
   * conversion proc, only once per series of calls, assuming the element
1057
   * type doesn't change underneath us.
1058
   */
1059
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1060
0
  if (my_extra == NULL)
1061
0
  {
1062
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1063
0
                            sizeof(ArrayMetaState));
1064
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1065
0
    my_extra->element_type = ~element_type;
1066
0
  }
1067
1068
0
  if (my_extra->element_type != element_type)
1069
0
  {
1070
    /*
1071
     * Get info about element type, including its output conversion proc
1072
     */
1073
0
    get_type_io_data(element_type, IOFunc_output,
1074
0
             &my_extra->typlen, &my_extra->typbyval,
1075
0
             &my_extra->typalign, &my_extra->typdelim,
1076
0
             &my_extra->typioparam, &my_extra->typiofunc);
1077
0
    fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1078
0
            fcinfo->flinfo->fn_mcxt);
1079
0
    my_extra->element_type = element_type;
1080
0
  }
1081
0
  typlen = my_extra->typlen;
1082
0
  typbyval = my_extra->typbyval;
1083
0
  typalign = my_extra->typalign;
1084
0
  typdelim = my_extra->typdelim;
1085
1086
0
  ndim = AARR_NDIM(v);
1087
0
  dims = AARR_DIMS(v);
1088
0
  lb = AARR_LBOUND(v);
1089
0
  nitems = ArrayGetNItems(ndim, dims);
1090
1091
0
  if (nitems == 0)
1092
0
  {
1093
0
    retval = pstrdup("{}");
1094
0
    PG_RETURN_CSTRING(retval);
1095
0
  }
1096
1097
  /*
1098
   * we will need to add explicit dimensions if any dimension has a lower
1099
   * bound other than one
1100
   */
1101
0
  for (i = 0; i < ndim; i++)
1102
0
  {
1103
0
    if (lb[i] != 1)
1104
0
    {
1105
0
      needdims = true;
1106
0
      break;
1107
0
    }
1108
0
  }
1109
1110
  /*
1111
   * Convert all values to string form, count total space needed (including
1112
   * any overhead such as escaping backslashes), and detect whether each
1113
   * item needs double quotes.
1114
   */
1115
0
  values = (char **) palloc(nitems * sizeof(char *));
1116
0
  needquotes = (bool *) palloc(nitems * sizeof(bool));
1117
0
  overall_length = 0;
1118
1119
0
  array_iter_setup(&iter, v, typlen, typbyval, typalign);
1120
1121
0
  for (i = 0; i < nitems; i++)
1122
0
  {
1123
0
    Datum   itemvalue;
1124
0
    bool    isnull;
1125
0
    bool    needquote;
1126
1127
    /* Get source element, checking for NULL */
1128
0
    itemvalue = array_iter_next(&iter, &isnull, i);
1129
1130
0
    if (isnull)
1131
0
    {
1132
0
      values[i] = pstrdup("NULL");
1133
0
      overall_length += 4;
1134
0
      needquote = false;
1135
0
    }
1136
0
    else
1137
0
    {
1138
0
      values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
1139
1140
      /* count data plus backslashes; detect chars needing quotes */
1141
0
      if (values[i][0] == '\0')
1142
0
        needquote = true; /* force quotes for empty string */
1143
0
      else if (pg_strcasecmp(values[i], "NULL") == 0)
1144
0
        needquote = true; /* force quotes for literal NULL */
1145
0
      else
1146
0
        needquote = false;
1147
1148
0
      for (tmp = values[i]; *tmp != '\0'; tmp++)
1149
0
      {
1150
0
        char    ch = *tmp;
1151
1152
0
        overall_length += 1;
1153
0
        if (ch == '"' || ch == '\\')
1154
0
        {
1155
0
          needquote = true;
1156
0
          overall_length += 1;
1157
0
        }
1158
0
        else if (ch == '{' || ch == '}' || ch == typdelim ||
1159
0
             scanner_isspace(ch))
1160
0
          needquote = true;
1161
0
      }
1162
0
    }
1163
1164
0
    needquotes[i] = needquote;
1165
1166
    /* Count the pair of double quotes, if needed */
1167
0
    if (needquote)
1168
0
      overall_length += 2;
1169
    /* and the comma (or other typdelim delimiter) */
1170
0
    overall_length += 1;
1171
0
  }
1172
1173
  /*
1174
   * The very last array element doesn't have a typdelim delimiter after it,
1175
   * but that's OK; that space is needed for the trailing '\0'.
1176
   *
1177
   * Now count total number of curly brace pairs in output string.
1178
   */
1179
0
  for (i = j = 0, k = 1; i < ndim; i++)
1180
0
  {
1181
0
    j += k, k *= dims[i];
1182
0
  }
1183
0
  overall_length += 2 * j;
1184
1185
  /* Format explicit dimensions if required */
1186
0
  dims_str[0] = '\0';
1187
0
  if (needdims)
1188
0
  {
1189
0
    char     *ptr = dims_str;
1190
1191
0
    for (i = 0; i < ndim; i++)
1192
0
    {
1193
0
      sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
1194
0
      ptr += strlen(ptr);
1195
0
    }
1196
0
    *ptr++ = *ASSGN;
1197
0
    *ptr = '\0';
1198
0
    overall_length += ptr - dims_str;
1199
0
  }
1200
1201
  /* Now construct the output string */
1202
0
  retval = (char *) palloc(overall_length);
1203
0
  p = retval;
1204
1205
0
#define APPENDSTR(str)  (strcpy(p, (str)), p += strlen(p))
1206
0
#define APPENDCHAR(ch)  (*p++ = (ch), *p = '\0')
1207
1208
0
  if (needdims)
1209
0
    APPENDSTR(dims_str);
1210
0
  APPENDCHAR('{');
1211
0
  for (i = 0; i < ndim; i++)
1212
0
    indx[i] = 0;
1213
0
  j = 0;
1214
0
  k = 0;
1215
0
  do
1216
0
  {
1217
0
    for (i = j; i < ndim - 1; i++)
1218
0
      APPENDCHAR('{');
1219
1220
0
    if (needquotes[k])
1221
0
    {
1222
0
      APPENDCHAR('"');
1223
0
      for (tmp = values[k]; *tmp; tmp++)
1224
0
      {
1225
0
        char    ch = *tmp;
1226
1227
0
        if (ch == '"' || ch == '\\')
1228
0
          *p++ = '\\';
1229
0
        *p++ = ch;
1230
0
      }
1231
0
      *p = '\0';
1232
0
      APPENDCHAR('"');
1233
0
    }
1234
0
    else
1235
0
      APPENDSTR(values[k]);
1236
0
    pfree(values[k++]);
1237
1238
0
    for (i = ndim - 1; i >= 0; i--)
1239
0
    {
1240
0
      if (++(indx[i]) < dims[i])
1241
0
      {
1242
0
        APPENDCHAR(typdelim);
1243
0
        break;
1244
0
      }
1245
0
      else
1246
0
      {
1247
0
        indx[i] = 0;
1248
0
        APPENDCHAR('}');
1249
0
      }
1250
0
    }
1251
0
    j = i;
1252
0
  } while (j != -1);
1253
1254
0
#undef APPENDSTR
1255
0
#undef APPENDCHAR
1256
1257
  /* Assert that we calculated the string length accurately */
1258
0
  Assert(overall_length == (p - retval + 1));
1259
1260
0
  pfree(values);
1261
0
  pfree(needquotes);
1262
1263
0
  PG_RETURN_CSTRING(retval);
1264
0
}
1265
1266
/*
1267
 * array_recv :
1268
 *      converts an array from the external binary format to
1269
 *      its internal format.
1270
 *
1271
 * return value :
1272
 *      the internal representation of the input array
1273
 */
1274
Datum
1275
array_recv(PG_FUNCTION_ARGS)
1276
0
{
1277
0
  StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
1278
0
  Oid     spec_element_type = PG_GETARG_OID(1);  /* type of an array
1279
                             * element */
1280
0
  int32   typmod = PG_GETARG_INT32(2);  /* typmod for array elements */
1281
0
  Oid     element_type;
1282
0
  int     typlen;
1283
0
  bool    typbyval;
1284
0
  char    typalign;
1285
0
  Oid     typioparam;
1286
0
  int     i,
1287
0
        nitems;
1288
0
  Datum    *dataPtr;
1289
0
  bool     *nullsPtr;
1290
0
  bool    hasnulls;
1291
0
  int32   nbytes;
1292
0
  int32   dataoffset;
1293
0
  ArrayType  *retval;
1294
0
  int     ndim,
1295
0
        flags,
1296
0
        dim[MAXDIM],
1297
0
        lBound[MAXDIM];
1298
0
  ArrayMetaState *my_extra;
1299
1300
  /* Get the array header information */
1301
0
  ndim = pq_getmsgint(buf, 4);
1302
0
  if (ndim < 0)       /* we do allow zero-dimension arrays */
1303
0
    ereport(ERROR,
1304
0
        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1305
0
         errmsg("invalid number of dimensions: %d", ndim)));
1306
0
  if (ndim > MAXDIM)
1307
0
    ereport(ERROR,
1308
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1309
0
         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
1310
0
            ndim, MAXDIM)));
1311
1312
0
  flags = pq_getmsgint(buf, 4);
1313
0
  if (flags != 0 && flags != 1)
1314
0
    ereport(ERROR,
1315
0
        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1316
0
         errmsg("invalid array flags")));
1317
1318
  /* Check element type recorded in the data */
1319
0
  element_type = pq_getmsgint(buf, sizeof(Oid));
1320
1321
  /*
1322
   * From a security standpoint, it doesn't matter whether the input's
1323
   * element type matches what we expect: the element type's receive
1324
   * function has to be robust enough to cope with invalid data.  However,
1325
   * from a user-friendliness standpoint, it's nicer to complain about type
1326
   * mismatches than to throw "improper binary format" errors.  But there's
1327
   * a problem: only built-in types have OIDs that are stable enough to
1328
   * believe that a mismatch is a real issue.  So complain only if both OIDs
1329
   * are in the built-in range.  Otherwise, carry on with the element type
1330
   * we "should" be getting.
1331
   */
1332
0
  if (element_type != spec_element_type)
1333
0
  {
1334
0
    if (element_type < FirstGenbkiObjectId &&
1335
0
      spec_element_type < FirstGenbkiObjectId)
1336
0
      ereport(ERROR,
1337
0
          (errcode(ERRCODE_DATATYPE_MISMATCH),
1338
0
           errmsg("binary data has array element type %u (%s) instead of expected %u (%s)",
1339
0
              element_type,
1340
0
              format_type_extended(element_type, -1,
1341
0
                         FORMAT_TYPE_ALLOW_INVALID),
1342
0
              spec_element_type,
1343
0
              format_type_extended(spec_element_type, -1,
1344
0
                         FORMAT_TYPE_ALLOW_INVALID))));
1345
0
    element_type = spec_element_type;
1346
0
  }
1347
1348
0
  for (i = 0; i < ndim; i++)
1349
0
  {
1350
0
    dim[i] = pq_getmsgint(buf, 4);
1351
0
    lBound[i] = pq_getmsgint(buf, 4);
1352
0
  }
1353
1354
  /* This checks for overflow of array dimensions */
1355
0
  nitems = ArrayGetNItems(ndim, dim);
1356
0
  ArrayCheckBounds(ndim, dim, lBound);
1357
1358
  /*
1359
   * We arrange to look up info about element type, including its receive
1360
   * conversion proc, only once per series of calls, assuming the element
1361
   * type doesn't change underneath us.
1362
   */
1363
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1364
0
  if (my_extra == NULL)
1365
0
  {
1366
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1367
0
                            sizeof(ArrayMetaState));
1368
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1369
0
    my_extra->element_type = ~element_type;
1370
0
  }
1371
1372
0
  if (my_extra->element_type != element_type)
1373
0
  {
1374
    /* Get info about element type, including its receive proc */
1375
0
    get_type_io_data(element_type, IOFunc_receive,
1376
0
             &my_extra->typlen, &my_extra->typbyval,
1377
0
             &my_extra->typalign, &my_extra->typdelim,
1378
0
             &my_extra->typioparam, &my_extra->typiofunc);
1379
0
    if (!OidIsValid(my_extra->typiofunc))
1380
0
      ereport(ERROR,
1381
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
1382
0
           errmsg("no binary input function available for type %s",
1383
0
              format_type_be(element_type))));
1384
0
    fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1385
0
            fcinfo->flinfo->fn_mcxt);
1386
0
    my_extra->element_type = element_type;
1387
0
  }
1388
1389
0
  if (nitems == 0)
1390
0
  {
1391
    /* Return empty array ... but not till we've validated element_type */
1392
0
    PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
1393
0
  }
1394
1395
0
  typlen = my_extra->typlen;
1396
0
  typbyval = my_extra->typbyval;
1397
0
  typalign = my_extra->typalign;
1398
0
  typioparam = my_extra->typioparam;
1399
1400
0
  dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
1401
0
  nullsPtr = (bool *) palloc(nitems * sizeof(bool));
1402
0
  ReadArrayBinary(buf, nitems,
1403
0
          &my_extra->proc, typioparam, typmod,
1404
0
          typlen, typbyval, typalign,
1405
0
          dataPtr, nullsPtr,
1406
0
          &hasnulls, &nbytes);
1407
0
  if (hasnulls)
1408
0
  {
1409
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
1410
0
    nbytes += dataoffset;
1411
0
  }
1412
0
  else
1413
0
  {
1414
0
    dataoffset = 0;     /* marker for no null bitmap */
1415
0
    nbytes += ARR_OVERHEAD_NONULLS(ndim);
1416
0
  }
1417
0
  retval = (ArrayType *) palloc0(nbytes);
1418
0
  SET_VARSIZE(retval, nbytes);
1419
0
  retval->ndim = ndim;
1420
0
  retval->dataoffset = dataoffset;
1421
0
  retval->elemtype = element_type;
1422
0
  memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
1423
0
  memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
1424
1425
0
  CopyArrayEls(retval,
1426
0
         dataPtr, nullsPtr, nitems,
1427
0
         typlen, typbyval, typalign,
1428
0
         true);
1429
1430
0
  pfree(dataPtr);
1431
0
  pfree(nullsPtr);
1432
1433
0
  PG_RETURN_ARRAYTYPE_P(retval);
1434
0
}
1435
1436
/*
1437
 * ReadArrayBinary:
1438
 *   collect the data elements of an array being read in binary style.
1439
 *
1440
 * Inputs:
1441
 *  buf: the data buffer to read from.
1442
 *  nitems: total number of array elements (already read).
1443
 *  receiveproc: type-specific receive procedure for element datatype.
1444
 *  typioparam, typmod: auxiliary values to pass to receiveproc.
1445
 *  typlen, typbyval, typalign: storage parameters of element datatype.
1446
 *
1447
 * Outputs:
1448
 *  values[]: filled with converted data values.
1449
 *  nulls[]: filled with is-null markers.
1450
 *  *hasnulls: set true iff there are any null elements.
1451
 *  *nbytes: set to total size of data area needed (including alignment
1452
 *    padding but not including array header overhead).
1453
 *
1454
 * Note that values[] and nulls[] are allocated by the caller, and must have
1455
 * nitems elements.
1456
 */
1457
static void
1458
ReadArrayBinary(StringInfo buf,
1459
        int nitems,
1460
        FmgrInfo *receiveproc,
1461
        Oid typioparam,
1462
        int32 typmod,
1463
        int typlen,
1464
        bool typbyval,
1465
        char typalign,
1466
        Datum *values,
1467
        bool *nulls,
1468
        bool *hasnulls,
1469
        int32 *nbytes)
1470
0
{
1471
0
  int     i;
1472
0
  bool    hasnull;
1473
0
  int32   totbytes;
1474
0
  uint8   typalignby = typalign_to_alignby(typalign);
1475
1476
0
  for (i = 0; i < nitems; i++)
1477
0
  {
1478
0
    int     itemlen;
1479
0
    StringInfoData elem_buf;
1480
1481
    /* Get and check the item length */
1482
0
    itemlen = pq_getmsgint(buf, 4);
1483
0
    if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
1484
0
      ereport(ERROR,
1485
0
          (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1486
0
           errmsg("insufficient data left in message")));
1487
1488
0
    if (itemlen == -1)
1489
0
    {
1490
      /* -1 length means NULL */
1491
0
      values[i] = ReceiveFunctionCall(receiveproc, NULL,
1492
0
                      typioparam, typmod);
1493
0
      nulls[i] = true;
1494
0
      continue;
1495
0
    }
1496
1497
    /*
1498
     * Rather than copying data around, we just initialize a StringInfo
1499
     * pointing to the correct portion of the message buffer.
1500
     */
1501
0
    initReadOnlyStringInfo(&elem_buf, &buf->data[buf->cursor], itemlen);
1502
1503
0
    buf->cursor += itemlen;
1504
1505
    /* Now call the element's receiveproc */
1506
0
    values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
1507
0
                    typioparam, typmod);
1508
0
    nulls[i] = false;
1509
1510
    /* Trouble if it didn't eat the whole buffer */
1511
0
    if (elem_buf.cursor != itemlen)
1512
0
      ereport(ERROR,
1513
0
          (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
1514
0
           errmsg("improper binary format in array element %d",
1515
0
              i + 1)));
1516
0
  }
1517
1518
  /*
1519
   * Check for nulls, compute total data space needed
1520
   */
1521
0
  hasnull = false;
1522
0
  totbytes = 0;
1523
0
  for (i = 0; i < nitems; i++)
1524
0
  {
1525
0
    if (nulls[i])
1526
0
      hasnull = true;
1527
0
    else
1528
0
    {
1529
      /* let's just make sure data is not toasted */
1530
0
      if (typlen == -1)
1531
0
        values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
1532
0
      totbytes = att_addlength_datum(totbytes, typlen, values[i]);
1533
0
      totbytes = att_nominal_alignby(totbytes, typalignby);
1534
      /* check for overflow of total request */
1535
0
      if (!AllocSizeIsValid(totbytes))
1536
0
        ereport(ERROR,
1537
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1538
0
             errmsg("array size exceeds the maximum allowed (%zu)",
1539
0
                MaxAllocSize)));
1540
0
    }
1541
0
  }
1542
0
  *hasnulls = hasnull;
1543
0
  *nbytes = totbytes;
1544
0
}
1545
1546
1547
/*
1548
 * array_send :
1549
 *      takes the internal representation of an array and returns a bytea
1550
 *      containing the array in its external binary format.
1551
 */
1552
Datum
1553
array_send(PG_FUNCTION_ARGS)
1554
0
{
1555
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1556
0
  Oid     element_type = AARR_ELEMTYPE(v);
1557
0
  int     typlen;
1558
0
  bool    typbyval;
1559
0
  char    typalign;
1560
0
  int     nitems,
1561
0
        i;
1562
0
  int     ndim,
1563
0
         *dim,
1564
0
         *lb;
1565
0
  StringInfoData buf;
1566
0
  array_iter  iter;
1567
0
  ArrayMetaState *my_extra;
1568
1569
  /*
1570
   * We arrange to look up info about element type, including its send
1571
   * conversion proc, only once per series of calls, assuming the element
1572
   * type doesn't change underneath us.
1573
   */
1574
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1575
0
  if (my_extra == NULL)
1576
0
  {
1577
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1578
0
                            sizeof(ArrayMetaState));
1579
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1580
0
    my_extra->element_type = ~element_type;
1581
0
  }
1582
1583
0
  if (my_extra->element_type != element_type)
1584
0
  {
1585
    /* Get info about element type, including its send proc */
1586
0
    get_type_io_data(element_type, IOFunc_send,
1587
0
             &my_extra->typlen, &my_extra->typbyval,
1588
0
             &my_extra->typalign, &my_extra->typdelim,
1589
0
             &my_extra->typioparam, &my_extra->typiofunc);
1590
0
    if (!OidIsValid(my_extra->typiofunc))
1591
0
      ereport(ERROR,
1592
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
1593
0
           errmsg("no binary output function available for type %s",
1594
0
              format_type_be(element_type))));
1595
0
    fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
1596
0
            fcinfo->flinfo->fn_mcxt);
1597
0
    my_extra->element_type = element_type;
1598
0
  }
1599
0
  typlen = my_extra->typlen;
1600
0
  typbyval = my_extra->typbyval;
1601
0
  typalign = my_extra->typalign;
1602
1603
0
  ndim = AARR_NDIM(v);
1604
0
  dim = AARR_DIMS(v);
1605
0
  lb = AARR_LBOUND(v);
1606
0
  nitems = ArrayGetNItems(ndim, dim);
1607
1608
0
  pq_begintypsend(&buf);
1609
1610
  /* Send the array header information */
1611
0
  pq_sendint32(&buf, ndim);
1612
0
  pq_sendint32(&buf, AARR_HASNULL(v) ? 1 : 0);
1613
0
  pq_sendint32(&buf, element_type);
1614
0
  for (i = 0; i < ndim; i++)
1615
0
  {
1616
0
    pq_sendint32(&buf, dim[i]);
1617
0
    pq_sendint32(&buf, lb[i]);
1618
0
  }
1619
1620
  /* Send the array elements using the element's own sendproc */
1621
0
  array_iter_setup(&iter, v, typlen, typbyval, typalign);
1622
1623
0
  for (i = 0; i < nitems; i++)
1624
0
  {
1625
0
    Datum   itemvalue;
1626
0
    bool    isnull;
1627
1628
    /* Get source element, checking for NULL */
1629
0
    itemvalue = array_iter_next(&iter, &isnull, i);
1630
1631
0
    if (isnull)
1632
0
    {
1633
      /* -1 length means a NULL */
1634
0
      pq_sendint32(&buf, -1);
1635
0
    }
1636
0
    else
1637
0
    {
1638
0
      bytea    *outputbytes;
1639
1640
0
      outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
1641
0
      pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
1642
0
      pq_sendbytes(&buf, VARDATA(outputbytes),
1643
0
             VARSIZE(outputbytes) - VARHDRSZ);
1644
0
      pfree(outputbytes);
1645
0
    }
1646
0
  }
1647
1648
0
  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1649
0
}
1650
1651
/*
1652
 * array_ndims :
1653
 *      returns the number of dimensions of the array pointed to by "v"
1654
 */
1655
Datum
1656
array_ndims(PG_FUNCTION_ARGS)
1657
0
{
1658
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1659
1660
  /* Sanity check: does it look like an array at all? */
1661
0
  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1662
0
    PG_RETURN_NULL();
1663
1664
0
  PG_RETURN_INT32(AARR_NDIM(v));
1665
0
}
1666
1667
/*
1668
 * array_dims :
1669
 *      returns the dimensions of the array pointed to by "v", as a "text"
1670
 */
1671
Datum
1672
array_dims(PG_FUNCTION_ARGS)
1673
0
{
1674
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1675
0
  char     *p;
1676
0
  int     i;
1677
0
  int      *dimv,
1678
0
         *lb;
1679
1680
  /*
1681
   * 33 since we assume 15 digits per number + ':' +'[]'
1682
   *
1683
   * +1 for trailing null
1684
   */
1685
0
  char    buf[MAXDIM * 33 + 1];
1686
1687
  /* Sanity check: does it look like an array at all? */
1688
0
  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1689
0
    PG_RETURN_NULL();
1690
1691
0
  dimv = AARR_DIMS(v);
1692
0
  lb = AARR_LBOUND(v);
1693
1694
0
  p = buf;
1695
0
  for (i = 0; i < AARR_NDIM(v); i++)
1696
0
  {
1697
0
    sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
1698
0
    p += strlen(p);
1699
0
  }
1700
1701
0
  PG_RETURN_TEXT_P(cstring_to_text(buf));
1702
0
}
1703
1704
/*
1705
 * array_lower :
1706
 *    returns the lower dimension, of the DIM requested, for
1707
 *    the array pointed to by "v", as an int4
1708
 */
1709
Datum
1710
array_lower(PG_FUNCTION_ARGS)
1711
0
{
1712
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1713
0
  int     reqdim = PG_GETARG_INT32(1);
1714
0
  int      *lb;
1715
0
  int     result;
1716
1717
  /* Sanity check: does it look like an array at all? */
1718
0
  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1719
0
    PG_RETURN_NULL();
1720
1721
  /* Sanity check: was the requested dim valid */
1722
0
  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1723
0
    PG_RETURN_NULL();
1724
1725
0
  lb = AARR_LBOUND(v);
1726
0
  result = lb[reqdim - 1];
1727
1728
0
  PG_RETURN_INT32(result);
1729
0
}
1730
1731
/*
1732
 * array_upper :
1733
 *    returns the upper dimension, of the DIM requested, for
1734
 *    the array pointed to by "v", as an int4
1735
 */
1736
Datum
1737
array_upper(PG_FUNCTION_ARGS)
1738
0
{
1739
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1740
0
  int     reqdim = PG_GETARG_INT32(1);
1741
0
  int      *dimv,
1742
0
         *lb;
1743
0
  int     result;
1744
1745
  /* Sanity check: does it look like an array at all? */
1746
0
  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1747
0
    PG_RETURN_NULL();
1748
1749
  /* Sanity check: was the requested dim valid */
1750
0
  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1751
0
    PG_RETURN_NULL();
1752
1753
0
  lb = AARR_LBOUND(v);
1754
0
  dimv = AARR_DIMS(v);
1755
1756
0
  result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
1757
1758
0
  PG_RETURN_INT32(result);
1759
0
}
1760
1761
/*
1762
 * array_length :
1763
 *    returns the length, of the dimension requested, for
1764
 *    the array pointed to by "v", as an int4
1765
 */
1766
Datum
1767
array_length(PG_FUNCTION_ARGS)
1768
0
{
1769
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1770
0
  int     reqdim = PG_GETARG_INT32(1);
1771
0
  int      *dimv;
1772
0
  int     result;
1773
1774
  /* Sanity check: does it look like an array at all? */
1775
0
  if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
1776
0
    PG_RETURN_NULL();
1777
1778
  /* Sanity check: was the requested dim valid */
1779
0
  if (reqdim <= 0 || reqdim > AARR_NDIM(v))
1780
0
    PG_RETURN_NULL();
1781
1782
0
  dimv = AARR_DIMS(v);
1783
1784
0
  result = dimv[reqdim - 1];
1785
1786
0
  PG_RETURN_INT32(result);
1787
0
}
1788
1789
/*
1790
 * array_cardinality:
1791
 *    returns the total number of elements in an array
1792
 */
1793
Datum
1794
array_cardinality(PG_FUNCTION_ARGS)
1795
0
{
1796
0
  AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
1797
1798
0
  PG_RETURN_INT32(ArrayGetNItems(AARR_NDIM(v), AARR_DIMS(v)));
1799
0
}
1800
1801
1802
/*
1803
 * array_get_element :
1804
 *    This routine takes an array datum and a subscript array and returns
1805
 *    the referenced item as a Datum.  Note that for a pass-by-reference
1806
 *    datatype, the returned Datum is a pointer into the array object.
1807
 *
1808
 * This handles both ordinary varlena arrays and fixed-length arrays.
1809
 *
1810
 * Inputs:
1811
 *  arraydatum: the array object (mustn't be NULL)
1812
 *  nSubscripts: number of subscripts supplied
1813
 *  indx[]: the subscript values
1814
 *  arraytyplen: pg_type.typlen for the array type
1815
 *  elmlen: pg_type.typlen for the array's element type
1816
 *  elmbyval: pg_type.typbyval for the array's element type
1817
 *  elmalign: pg_type.typalign for the array's element type
1818
 *
1819
 * Outputs:
1820
 *  The return value is the element Datum.
1821
 *  *isNull is set to indicate whether the element is NULL.
1822
 */
1823
Datum
1824
array_get_element(Datum arraydatum,
1825
          int nSubscripts,
1826
          int *indx,
1827
          int arraytyplen,
1828
          int elmlen,
1829
          bool elmbyval,
1830
          char elmalign,
1831
          bool *isNull)
1832
0
{
1833
0
  int     i,
1834
0
        ndim,
1835
0
         *dim,
1836
0
         *lb,
1837
0
        offset,
1838
0
        fixedDim[1],
1839
0
        fixedLb[1];
1840
0
  char     *arraydataptr,
1841
0
         *retptr;
1842
0
  uint8    *arraynullsptr;
1843
1844
0
  if (arraytyplen > 0)
1845
0
  {
1846
    /*
1847
     * fixed-length arrays -- these are assumed to be 1-d, 0-based
1848
     */
1849
0
    ndim = 1;
1850
0
    fixedDim[0] = arraytyplen / elmlen;
1851
0
    fixedLb[0] = 0;
1852
0
    dim = fixedDim;
1853
0
    lb = fixedLb;
1854
0
    arraydataptr = (char *) DatumGetPointer(arraydatum);
1855
0
    arraynullsptr = NULL;
1856
0
  }
1857
0
  else if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
1858
0
  {
1859
    /* expanded array: let's do this in a separate function */
1860
0
    return array_get_element_expanded(arraydatum,
1861
0
                      nSubscripts,
1862
0
                      indx,
1863
0
                      arraytyplen,
1864
0
                      elmlen,
1865
0
                      elmbyval,
1866
0
                      elmalign,
1867
0
                      isNull);
1868
0
  }
1869
0
  else
1870
0
  {
1871
    /* detoast array if necessary, producing normal varlena input */
1872
0
    ArrayType  *array = DatumGetArrayTypeP(arraydatum);
1873
1874
0
    ndim = ARR_NDIM(array);
1875
0
    dim = ARR_DIMS(array);
1876
0
    lb = ARR_LBOUND(array);
1877
0
    arraydataptr = ARR_DATA_PTR(array);
1878
0
    arraynullsptr = ARR_NULLBITMAP(array);
1879
0
  }
1880
1881
  /*
1882
   * Return NULL for invalid subscript
1883
   */
1884
0
  if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1885
0
  {
1886
0
    *isNull = true;
1887
0
    return (Datum) 0;
1888
0
  }
1889
0
  for (i = 0; i < ndim; i++)
1890
0
  {
1891
0
    if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1892
0
    {
1893
0
      *isNull = true;
1894
0
      return (Datum) 0;
1895
0
    }
1896
0
  }
1897
1898
  /*
1899
   * Calculate the element number
1900
   */
1901
0
  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1902
1903
  /*
1904
   * Check for NULL array element
1905
   */
1906
0
  if (array_get_isnull(arraynullsptr, offset))
1907
0
  {
1908
0
    *isNull = true;
1909
0
    return (Datum) 0;
1910
0
  }
1911
1912
  /*
1913
   * OK, get the element
1914
   */
1915
0
  *isNull = false;
1916
0
  retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
1917
0
            elmlen, elmbyval, elmalign);
1918
0
  return ArrayCast(retptr, elmbyval, elmlen);
1919
0
}
1920
1921
/*
1922
 * Implementation of array_get_element() for an expanded array
1923
 */
1924
static Datum
1925
array_get_element_expanded(Datum arraydatum,
1926
               int nSubscripts, int *indx,
1927
               int arraytyplen,
1928
               int elmlen, bool elmbyval, char elmalign,
1929
               bool *isNull)
1930
0
{
1931
0
  ExpandedArrayHeader *eah;
1932
0
  int     i,
1933
0
        ndim,
1934
0
         *dim,
1935
0
         *lb,
1936
0
        offset;
1937
0
  Datum    *dvalues;
1938
0
  bool     *dnulls;
1939
1940
0
  eah = (ExpandedArrayHeader *) DatumGetEOHP(arraydatum);
1941
0
  Assert(eah->ea_magic == EA_MAGIC);
1942
1943
  /* sanity-check caller's info against object */
1944
0
  Assert(arraytyplen == -1);
1945
0
  Assert(elmlen == eah->typlen);
1946
0
  Assert(elmbyval == eah->typbyval);
1947
0
  Assert(elmalign == eah->typalign);
1948
1949
0
  ndim = eah->ndims;
1950
0
  dim = eah->dims;
1951
0
  lb = eah->lbound;
1952
1953
  /*
1954
   * Return NULL for invalid subscript
1955
   */
1956
0
  if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
1957
0
  {
1958
0
    *isNull = true;
1959
0
    return (Datum) 0;
1960
0
  }
1961
0
  for (i = 0; i < ndim; i++)
1962
0
  {
1963
0
    if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
1964
0
    {
1965
0
      *isNull = true;
1966
0
      return (Datum) 0;
1967
0
    }
1968
0
  }
1969
1970
  /*
1971
   * Calculate the element number
1972
   */
1973
0
  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
1974
1975
  /*
1976
   * Deconstruct array if we didn't already.  Note that we apply this even
1977
   * if the input is nominally read-only: it should be safe enough.
1978
   */
1979
0
  deconstruct_expanded_array(eah);
1980
1981
0
  dvalues = eah->dvalues;
1982
0
  dnulls = eah->dnulls;
1983
1984
  /*
1985
   * Check for NULL array element
1986
   */
1987
0
  if (dnulls && dnulls[offset])
1988
0
  {
1989
0
    *isNull = true;
1990
0
    return (Datum) 0;
1991
0
  }
1992
1993
  /*
1994
   * OK, get the element.  It's OK to return a pass-by-ref value as a
1995
   * pointer into the expanded array, for the same reason that regular
1996
   * array_get_element can return a pointer into flat arrays: the value is
1997
   * assumed not to change for as long as the Datum reference can exist.
1998
   */
1999
0
  *isNull = false;
2000
0
  return dvalues[offset];
2001
0
}
2002
2003
/*
2004
 * array_get_slice :
2005
 *       This routine takes an array and a range of indices (upperIndx and
2006
 *       lowerIndx), creates a new array structure for the referred elements
2007
 *       and returns a pointer to it.
2008
 *
2009
 * This handles both ordinary varlena arrays and fixed-length arrays.
2010
 *
2011
 * Inputs:
2012
 *  arraydatum: the array object (mustn't be NULL)
2013
 *  nSubscripts: number of subscripts supplied (must be same for upper/lower)
2014
 *  upperIndx[]: the upper subscript values
2015
 *  lowerIndx[]: the lower subscript values
2016
 *  upperProvided[]: true for provided upper subscript values
2017
 *  lowerProvided[]: true for provided lower subscript values
2018
 *  arraytyplen: pg_type.typlen for the array type
2019
 *  elmlen: pg_type.typlen for the array's element type
2020
 *  elmbyval: pg_type.typbyval for the array's element type
2021
 *  elmalign: pg_type.typalign for the array's element type
2022
 *
2023
 * Outputs:
2024
 *  The return value is the new array Datum (it's never NULL)
2025
 *
2026
 * Omitted upper and lower subscript values are replaced by the corresponding
2027
 * array bound.
2028
 *
2029
 * NOTE: we assume it is OK to scribble on the provided subscript arrays
2030
 * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
2031
 * even when nSubscripts is less.  These are generally just temporaries.
2032
 */
2033
Datum
2034
array_get_slice(Datum arraydatum,
2035
        int nSubscripts,
2036
        int *upperIndx,
2037
        int *lowerIndx,
2038
        bool *upperProvided,
2039
        bool *lowerProvided,
2040
        int arraytyplen,
2041
        int elmlen,
2042
        bool elmbyval,
2043
        char elmalign)
2044
0
{
2045
0
  ArrayType  *array;
2046
0
  ArrayType  *newarray;
2047
0
  int     i,
2048
0
        ndim,
2049
0
         *dim,
2050
0
         *lb,
2051
0
         *newlb;
2052
0
  int     fixedDim[1],
2053
0
        fixedLb[1];
2054
0
  Oid     elemtype;
2055
0
  char     *arraydataptr;
2056
0
  uint8    *arraynullsptr;
2057
0
  int32   dataoffset;
2058
0
  int     bytes,
2059
0
        span[MAXDIM];
2060
2061
0
  if (arraytyplen > 0)
2062
0
  {
2063
    /*
2064
     * fixed-length arrays -- currently, cannot slice these because parser
2065
     * labels output as being of the fixed-length array type! Code below
2066
     * shows how we could support it if the parser were changed to label
2067
     * output as a suitable varlena array type.
2068
     */
2069
0
    ereport(ERROR,
2070
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2071
0
         errmsg("slices of fixed-length arrays not implemented")));
2072
2073
    /*
2074
     * fixed-length arrays -- these are assumed to be 1-d, 0-based
2075
     *
2076
     * XXX where would we get the correct ELEMTYPE from?
2077
     */
2078
0
    ndim = 1;
2079
0
    fixedDim[0] = arraytyplen / elmlen;
2080
0
    fixedLb[0] = 0;
2081
0
    dim = fixedDim;
2082
0
    lb = fixedLb;
2083
0
    elemtype = InvalidOid; /* XXX */
2084
0
    arraydataptr = (char *) DatumGetPointer(arraydatum);
2085
0
    arraynullsptr = NULL;
2086
0
  }
2087
0
  else
2088
0
  {
2089
    /* detoast input array if necessary */
2090
0
    array = DatumGetArrayTypeP(arraydatum);
2091
2092
0
    ndim = ARR_NDIM(array);
2093
0
    dim = ARR_DIMS(array);
2094
0
    lb = ARR_LBOUND(array);
2095
0
    elemtype = ARR_ELEMTYPE(array);
2096
0
    arraydataptr = ARR_DATA_PTR(array);
2097
0
    arraynullsptr = ARR_NULLBITMAP(array);
2098
0
  }
2099
2100
  /*
2101
   * Check provided subscripts.  A slice exceeding the current array limits
2102
   * is silently truncated to the array limits.  If we end up with an empty
2103
   * slice, return an empty array.
2104
   */
2105
0
  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2106
0
    return PointerGetDatum(construct_empty_array(elemtype));
2107
2108
0
  for (i = 0; i < nSubscripts; i++)
2109
0
  {
2110
0
    if (!lowerProvided[i] || lowerIndx[i] < lb[i])
2111
0
      lowerIndx[i] = lb[i];
2112
0
    if (!upperProvided[i] || upperIndx[i] >= (dim[i] + lb[i]))
2113
0
      upperIndx[i] = dim[i] + lb[i] - 1;
2114
0
    if (lowerIndx[i] > upperIndx[i])
2115
0
      return PointerGetDatum(construct_empty_array(elemtype));
2116
0
  }
2117
  /* fill any missing subscript positions with full array range */
2118
0
  for (; i < ndim; i++)
2119
0
  {
2120
0
    lowerIndx[i] = lb[i];
2121
0
    upperIndx[i] = dim[i] + lb[i] - 1;
2122
0
    if (lowerIndx[i] > upperIndx[i])
2123
0
      return PointerGetDatum(construct_empty_array(elemtype));
2124
0
  }
2125
2126
0
  mda_get_range(ndim, span, lowerIndx, upperIndx);
2127
2128
0
  bytes = array_slice_size(arraydataptr, arraynullsptr,
2129
0
               ndim, dim, lb,
2130
0
               lowerIndx, upperIndx,
2131
0
               elmlen, elmbyval, elmalign);
2132
2133
  /*
2134
   * Currently, we put a null bitmap in the result if the source has one;
2135
   * could be smarter ...
2136
   */
2137
0
  if (arraynullsptr)
2138
0
  {
2139
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
2140
0
    bytes += dataoffset;
2141
0
  }
2142
0
  else
2143
0
  {
2144
0
    dataoffset = 0;     /* marker for no null bitmap */
2145
0
    bytes += ARR_OVERHEAD_NONULLS(ndim);
2146
0
  }
2147
2148
0
  newarray = (ArrayType *) palloc0(bytes);
2149
0
  SET_VARSIZE(newarray, bytes);
2150
0
  newarray->ndim = ndim;
2151
0
  newarray->dataoffset = dataoffset;
2152
0
  newarray->elemtype = elemtype;
2153
0
  memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
2154
2155
  /*
2156
   * Lower bounds of the new array are set to 1.  Formerly (before 7.3) we
2157
   * copied the given lowerIndx values ... but that seems confusing.
2158
   */
2159
0
  newlb = ARR_LBOUND(newarray);
2160
0
  for (i = 0; i < ndim; i++)
2161
0
    newlb[i] = 1;
2162
2163
0
  array_extract_slice(newarray,
2164
0
            ndim, dim, lb,
2165
0
            arraydataptr, arraynullsptr,
2166
0
            lowerIndx, upperIndx,
2167
0
            elmlen, elmbyval, elmalign);
2168
2169
0
  return PointerGetDatum(newarray);
2170
0
}
2171
2172
/*
2173
 * array_set_element :
2174
 *      This routine sets the value of one array element (specified by
2175
 *      a subscript array) to a new value specified by "dataValue".
2176
 *
2177
 * This handles both ordinary varlena arrays and fixed-length arrays.
2178
 *
2179
 * Inputs:
2180
 *  arraydatum: the initial array object (mustn't be NULL)
2181
 *  nSubscripts: number of subscripts supplied
2182
 *  indx[]: the subscript values
2183
 *  dataValue: the datum to be inserted at the given position
2184
 *  isNull: whether dataValue is NULL
2185
 *  arraytyplen: pg_type.typlen for the array type
2186
 *  elmlen: pg_type.typlen for the array's element type
2187
 *  elmbyval: pg_type.typbyval for the array's element type
2188
 *  elmalign: pg_type.typalign for the array's element type
2189
 *
2190
 * Result:
2191
 *      A new array is returned, just like the old except for the one
2192
 *      modified entry.  The original array object is not changed,
2193
 *      unless what is passed is a read-write reference to an expanded
2194
 *      array object; in that case the expanded array is updated in-place.
2195
 *
2196
 * For one-dimensional arrays only, we allow the array to be extended
2197
 * by assigning to a position outside the existing subscript range; any
2198
 * positions between the existing elements and the new one are set to NULLs.
2199
 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2200
 *
2201
 * NOTE: For assignments, we throw an error for invalid subscripts etc,
2202
 * rather than returning a NULL as the fetch operations do.
2203
 */
2204
Datum
2205
array_set_element(Datum arraydatum,
2206
          int nSubscripts,
2207
          int *indx,
2208
          Datum dataValue,
2209
          bool isNull,
2210
          int arraytyplen,
2211
          int elmlen,
2212
          bool elmbyval,
2213
          char elmalign)
2214
0
{
2215
0
  ArrayType  *array;
2216
0
  ArrayType  *newarray;
2217
0
  int     i,
2218
0
        ndim,
2219
0
        dim[MAXDIM],
2220
0
        lb[MAXDIM],
2221
0
        offset;
2222
0
  char     *elt_ptr;
2223
0
  bool    newhasnulls;
2224
0
  uint8    *oldnullbitmap;
2225
0
  int     oldnitems,
2226
0
        newnitems,
2227
0
        olddatasize,
2228
0
        newsize,
2229
0
        olditemlen,
2230
0
        newitemlen,
2231
0
        overheadlen,
2232
0
        oldoverheadlen,
2233
0
        addedbefore,
2234
0
        addedafter,
2235
0
        lenbefore,
2236
0
        lenafter;
2237
0
  uint8   elmalignby = typalign_to_alignby(elmalign);
2238
2239
0
  if (arraytyplen > 0)
2240
0
  {
2241
    /*
2242
     * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
2243
     * cannot extend them, either.
2244
     */
2245
0
    char     *resultarray;
2246
2247
0
    if (nSubscripts != 1)
2248
0
      ereport(ERROR,
2249
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2250
0
           errmsg("wrong number of array subscripts")));
2251
2252
0
    if (indx[0] < 0 || indx[0] >= arraytyplen / elmlen)
2253
0
      ereport(ERROR,
2254
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2255
0
           errmsg("array subscript out of range")));
2256
2257
0
    if (isNull)
2258
0
      ereport(ERROR,
2259
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
2260
0
           errmsg("cannot assign null value to an element of a fixed-length array")));
2261
2262
0
    resultarray = (char *) palloc(arraytyplen);
2263
0
    memcpy(resultarray, DatumGetPointer(arraydatum), arraytyplen);
2264
0
    elt_ptr = resultarray + indx[0] * elmlen;
2265
0
    ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby, elt_ptr);
2266
0
    return PointerGetDatum(resultarray);
2267
0
  }
2268
2269
0
  if (nSubscripts <= 0 || nSubscripts > MAXDIM)
2270
0
    ereport(ERROR,
2271
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2272
0
         errmsg("wrong number of array subscripts")));
2273
2274
  /* make sure item to be inserted is not toasted */
2275
0
  if (elmlen == -1 && !isNull)
2276
0
    dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
2277
2278
0
  if (VARATT_IS_EXTERNAL_EXPANDED(DatumGetPointer(arraydatum)))
2279
0
  {
2280
    /* expanded array: let's do this in a separate function */
2281
0
    return array_set_element_expanded(arraydatum,
2282
0
                      nSubscripts,
2283
0
                      indx,
2284
0
                      dataValue,
2285
0
                      isNull,
2286
0
                      arraytyplen,
2287
0
                      elmlen,
2288
0
                      elmbyval,
2289
0
                      elmalign);
2290
0
  }
2291
2292
  /* detoast input array if necessary */
2293
0
  array = DatumGetArrayTypeP(arraydatum);
2294
2295
0
  ndim = ARR_NDIM(array);
2296
2297
  /*
2298
   * if number of dims is zero, i.e. an empty array, create an array with
2299
   * nSubscripts dimensions, and set the lower bounds to the supplied
2300
   * subscripts
2301
   */
2302
0
  if (ndim == 0)
2303
0
  {
2304
0
    Oid     elmtype = ARR_ELEMTYPE(array);
2305
2306
0
    for (i = 0; i < nSubscripts; i++)
2307
0
    {
2308
0
      dim[i] = 1;
2309
0
      lb[i] = indx[i];
2310
0
    }
2311
2312
0
    return PointerGetDatum(construct_md_array(&dataValue, &isNull,
2313
0
                          nSubscripts, dim, lb,
2314
0
                          elmtype,
2315
0
                          elmlen, elmbyval, elmalign));
2316
0
  }
2317
2318
0
  if (ndim != nSubscripts)
2319
0
    ereport(ERROR,
2320
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2321
0
         errmsg("wrong number of array subscripts")));
2322
2323
  /* copy dim/lb since we may modify them */
2324
0
  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2325
0
  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2326
2327
0
  newhasnulls = (ARR_HASNULL(array) || isNull);
2328
0
  addedbefore = addedafter = 0;
2329
2330
  /*
2331
   * Check subscripts.  We assume the existing subscripts passed
2332
   * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2333
   * overflow.  But we must beware of other overflows in our calculations of
2334
   * new dim[] values.
2335
   */
2336
0
  if (ndim == 1)
2337
0
  {
2338
0
    if (indx[0] < lb[0])
2339
0
    {
2340
      /* addedbefore = lb[0] - indx[0]; */
2341
      /* dim[0] += addedbefore; */
2342
0
      if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2343
0
        pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2344
0
        ereport(ERROR,
2345
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2346
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2347
0
                MaxArraySize)));
2348
0
      lb[0] = indx[0];
2349
0
      if (addedbefore > 1)
2350
0
        newhasnulls = true; /* will insert nulls */
2351
0
    }
2352
0
    if (indx[0] >= (dim[0] + lb[0]))
2353
0
    {
2354
      /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2355
      /* dim[0] += addedafter; */
2356
0
      if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2357
0
        pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2358
0
        pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2359
0
        ereport(ERROR,
2360
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2361
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2362
0
                MaxArraySize)));
2363
0
      if (addedafter > 1)
2364
0
        newhasnulls = true; /* will insert nulls */
2365
0
    }
2366
0
  }
2367
0
  else
2368
0
  {
2369
    /*
2370
     * XXX currently we do not support extending multi-dimensional arrays
2371
     * during assignment
2372
     */
2373
0
    for (i = 0; i < ndim; i++)
2374
0
    {
2375
0
      if (indx[i] < lb[i] ||
2376
0
        indx[i] >= (dim[i] + lb[i]))
2377
0
        ereport(ERROR,
2378
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2379
0
             errmsg("array subscript out of range")));
2380
0
    }
2381
0
  }
2382
2383
  /* This checks for overflow of the array dimensions */
2384
0
  newnitems = ArrayGetNItems(ndim, dim);
2385
0
  ArrayCheckBounds(ndim, dim, lb);
2386
2387
  /*
2388
   * Compute sizes of items and areas to copy
2389
   */
2390
0
  if (newhasnulls)
2391
0
    overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
2392
0
  else
2393
0
    overheadlen = ARR_OVERHEAD_NONULLS(ndim);
2394
0
  oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
2395
0
  oldnullbitmap = ARR_NULLBITMAP(array);
2396
0
  oldoverheadlen = ARR_DATA_OFFSET(array);
2397
0
  olddatasize = ARR_SIZE(array) - oldoverheadlen;
2398
0
  if (addedbefore)
2399
0
  {
2400
0
    offset = 0;
2401
0
    lenbefore = 0;
2402
0
    olditemlen = 0;
2403
0
    lenafter = olddatasize;
2404
0
  }
2405
0
  else if (addedafter)
2406
0
  {
2407
0
    offset = oldnitems;
2408
0
    lenbefore = olddatasize;
2409
0
    olditemlen = 0;
2410
0
    lenafter = 0;
2411
0
  }
2412
0
  else
2413
0
  {
2414
0
    offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2415
0
    elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
2416
0
               elmlen, elmbyval, elmalign);
2417
0
    lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
2418
0
    if (array_get_isnull(oldnullbitmap, offset))
2419
0
      olditemlen = 0;
2420
0
    else
2421
0
    {
2422
0
      olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
2423
0
      olditemlen = att_nominal_alignby(olditemlen, elmalignby);
2424
0
    }
2425
0
    lenafter = olddatasize - lenbefore - olditemlen;
2426
0
  }
2427
2428
0
  if (isNull)
2429
0
    newitemlen = 0;
2430
0
  else
2431
0
  {
2432
0
    newitemlen = att_addlength_datum(0, elmlen, dataValue);
2433
0
    newitemlen = att_nominal_alignby(newitemlen, elmalignby);
2434
0
  }
2435
2436
0
  newsize = overheadlen + lenbefore + newitemlen + lenafter;
2437
2438
  /*
2439
   * OK, create the new array and fill in header/dimensions
2440
   */
2441
0
  newarray = (ArrayType *) palloc0(newsize);
2442
0
  SET_VARSIZE(newarray, newsize);
2443
0
  newarray->ndim = ndim;
2444
0
  newarray->dataoffset = newhasnulls ? overheadlen : 0;
2445
0
  newarray->elemtype = ARR_ELEMTYPE(array);
2446
0
  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
2447
0
  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
2448
2449
  /*
2450
   * Fill in data
2451
   */
2452
0
  memcpy((char *) newarray + overheadlen,
2453
0
       (char *) array + oldoverheadlen,
2454
0
       lenbefore);
2455
0
  if (!isNull)
2456
0
    ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalignby,
2457
0
            (char *) newarray + overheadlen + lenbefore);
2458
0
  memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
2459
0
       (char *) array + oldoverheadlen + lenbefore + olditemlen,
2460
0
       lenafter);
2461
2462
  /*
2463
   * Fill in nulls bitmap if needed
2464
   *
2465
   * Note: it's possible we just replaced the last NULL with a non-NULL, and
2466
   * could get rid of the bitmap.  Seems not worth testing for though.
2467
   */
2468
0
  if (newhasnulls)
2469
0
  {
2470
0
    uint8    *newnullbitmap = ARR_NULLBITMAP(newarray);
2471
2472
    /* palloc0 above already marked any inserted positions as nulls */
2473
    /* Fix the inserted value */
2474
0
    if (addedafter)
2475
0
      array_set_isnull(newnullbitmap, newnitems - 1, isNull);
2476
0
    else
2477
0
      array_set_isnull(newnullbitmap, offset, isNull);
2478
    /* Fix the copied range(s) */
2479
0
    if (addedbefore)
2480
0
      array_bitmap_copy(newnullbitmap, addedbefore,
2481
0
                oldnullbitmap, 0,
2482
0
                oldnitems);
2483
0
    else
2484
0
    {
2485
0
      array_bitmap_copy(newnullbitmap, 0,
2486
0
                oldnullbitmap, 0,
2487
0
                offset);
2488
0
      if (addedafter == 0)
2489
0
        array_bitmap_copy(newnullbitmap, offset + 1,
2490
0
                  oldnullbitmap, offset + 1,
2491
0
                  oldnitems - offset - 1);
2492
0
    }
2493
0
  }
2494
2495
0
  return PointerGetDatum(newarray);
2496
0
}
2497
2498
/*
2499
 * Implementation of array_set_element() for an expanded array
2500
 *
2501
 * Note: as with any operation on a read/write expanded object, we must
2502
 * take pains not to leave the object in a corrupt state if we fail partway
2503
 * through.
2504
 */
2505
static Datum
2506
array_set_element_expanded(Datum arraydatum,
2507
               int nSubscripts, int *indx,
2508
               Datum dataValue, bool isNull,
2509
               int arraytyplen,
2510
               int elmlen, bool elmbyval, char elmalign)
2511
0
{
2512
0
  ExpandedArrayHeader *eah;
2513
0
  Datum    *dvalues;
2514
0
  bool     *dnulls;
2515
0
  int     i,
2516
0
        ndim,
2517
0
        dim[MAXDIM],
2518
0
        lb[MAXDIM],
2519
0
        offset;
2520
0
  bool    dimschanged,
2521
0
        newhasnulls;
2522
0
  int     addedbefore,
2523
0
        addedafter;
2524
0
  char     *oldValue;
2525
2526
  /* Convert to R/W object if not so already */
2527
0
  eah = DatumGetExpandedArray(arraydatum);
2528
2529
  /* Sanity-check caller's info against object; we don't use it otherwise */
2530
0
  Assert(arraytyplen == -1);
2531
0
  Assert(elmlen == eah->typlen);
2532
0
  Assert(elmbyval == eah->typbyval);
2533
0
  Assert(elmalign == eah->typalign);
2534
2535
  /*
2536
   * Copy dimension info into local storage.  This allows us to modify the
2537
   * dimensions if needed, while not messing up the expanded value if we
2538
   * fail partway through.
2539
   */
2540
0
  ndim = eah->ndims;
2541
0
  Assert(ndim >= 0 && ndim <= MAXDIM);
2542
0
  memcpy(dim, eah->dims, ndim * sizeof(int));
2543
0
  memcpy(lb, eah->lbound, ndim * sizeof(int));
2544
0
  dimschanged = false;
2545
2546
  /*
2547
   * if number of dims is zero, i.e. an empty array, create an array with
2548
   * nSubscripts dimensions, and set the lower bounds to the supplied
2549
   * subscripts.
2550
   */
2551
0
  if (ndim == 0)
2552
0
  {
2553
    /*
2554
     * Allocate adequate space for new dimension info.  This is harmless
2555
     * if we fail later.
2556
     */
2557
0
    Assert(nSubscripts > 0 && nSubscripts <= MAXDIM);
2558
0
    eah->dims = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2559
0
                           nSubscripts * sizeof(int));
2560
0
    eah->lbound = (int *) MemoryContextAllocZero(eah->hdr.eoh_context,
2561
0
                           nSubscripts * sizeof(int));
2562
2563
    /* Update local copies of dimension info */
2564
0
    ndim = nSubscripts;
2565
0
    for (i = 0; i < nSubscripts; i++)
2566
0
    {
2567
0
      dim[i] = 0;
2568
0
      lb[i] = indx[i];
2569
0
    }
2570
0
    dimschanged = true;
2571
0
  }
2572
0
  else if (ndim != nSubscripts)
2573
0
    ereport(ERROR,
2574
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2575
0
         errmsg("wrong number of array subscripts")));
2576
2577
  /*
2578
   * Deconstruct array if we didn't already.  (Someday maybe add a special
2579
   * case path for fixed-length, no-nulls cases, where we can overwrite an
2580
   * element in place without ever deconstructing.  But today is not that
2581
   * day.)
2582
   */
2583
0
  deconstruct_expanded_array(eah);
2584
2585
  /*
2586
   * Copy new element into array's context, if needed (we assume it's
2587
   * already detoasted, so no junk should be created).  Doing this before
2588
   * we've made any significant changes ensures that our behavior is sane
2589
   * even when the source is a reference to some element of this same array.
2590
   * If we fail further down, this memory is leaked, but that's reasonably
2591
   * harmless.
2592
   */
2593
0
  if (!eah->typbyval && !isNull)
2594
0
  {
2595
0
    MemoryContext oldcxt = MemoryContextSwitchTo(eah->hdr.eoh_context);
2596
2597
0
    dataValue = datumCopy(dataValue, false, eah->typlen);
2598
0
    MemoryContextSwitchTo(oldcxt);
2599
0
  }
2600
2601
0
  dvalues = eah->dvalues;
2602
0
  dnulls = eah->dnulls;
2603
2604
0
  newhasnulls = ((dnulls != NULL) || isNull);
2605
0
  addedbefore = addedafter = 0;
2606
2607
  /*
2608
   * Check subscripts (this logic must match array_set_element).  We assume
2609
   * the existing subscripts passed ArrayCheckBounds, so that dim[i] + lb[i]
2610
   * can be computed without overflow.  But we must beware of other
2611
   * overflows in our calculations of new dim[] values.
2612
   */
2613
0
  if (ndim == 1)
2614
0
  {
2615
0
    if (indx[0] < lb[0])
2616
0
    {
2617
      /* addedbefore = lb[0] - indx[0]; */
2618
      /* dim[0] += addedbefore; */
2619
0
      if (pg_sub_s32_overflow(lb[0], indx[0], &addedbefore) ||
2620
0
        pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2621
0
        ereport(ERROR,
2622
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2623
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2624
0
                MaxArraySize)));
2625
0
      lb[0] = indx[0];
2626
0
      dimschanged = true;
2627
0
      if (addedbefore > 1)
2628
0
        newhasnulls = true; /* will insert nulls */
2629
0
    }
2630
0
    if (indx[0] >= (dim[0] + lb[0]))
2631
0
    {
2632
      /* addedafter = indx[0] - (dim[0] + lb[0]) + 1; */
2633
      /* dim[0] += addedafter; */
2634
0
      if (pg_sub_s32_overflow(indx[0], dim[0] + lb[0], &addedafter) ||
2635
0
        pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2636
0
        pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2637
0
        ereport(ERROR,
2638
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2639
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2640
0
                MaxArraySize)));
2641
0
      dimschanged = true;
2642
0
      if (addedafter > 1)
2643
0
        newhasnulls = true; /* will insert nulls */
2644
0
    }
2645
0
  }
2646
0
  else
2647
0
  {
2648
    /*
2649
     * XXX currently we do not support extending multi-dimensional arrays
2650
     * during assignment
2651
     */
2652
0
    for (i = 0; i < ndim; i++)
2653
0
    {
2654
0
      if (indx[i] < lb[i] ||
2655
0
        indx[i] >= (dim[i] + lb[i]))
2656
0
        ereport(ERROR,
2657
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2658
0
             errmsg("array subscript out of range")));
2659
0
    }
2660
0
  }
2661
2662
  /* Check for overflow of the array dimensions */
2663
0
  if (dimschanged)
2664
0
  {
2665
0
    (void) ArrayGetNItems(ndim, dim);
2666
0
    ArrayCheckBounds(ndim, dim, lb);
2667
0
  }
2668
2669
  /* Now we can calculate linear offset of target item in array */
2670
0
  offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
2671
2672
  /* Physically enlarge existing dvalues/dnulls arrays if needed */
2673
0
  if (dim[0] > eah->dvalueslen)
2674
0
  {
2675
    /* We want some extra space if we're enlarging */
2676
0
    int     newlen = dim[0] + dim[0] / 8;
2677
2678
0
    newlen = Max(newlen, dim[0]); /* integer overflow guard */
2679
0
    eah->dvalues = dvalues = (Datum *)
2680
0
      repalloc(dvalues, newlen * sizeof(Datum));
2681
0
    if (dnulls)
2682
0
      eah->dnulls = dnulls = (bool *)
2683
0
        repalloc(dnulls, newlen * sizeof(bool));
2684
0
    eah->dvalueslen = newlen;
2685
0
  }
2686
2687
  /*
2688
   * If we need a nulls bitmap and don't already have one, create it, being
2689
   * sure to mark all existing entries as not null.
2690
   */
2691
0
  if (newhasnulls && dnulls == NULL)
2692
0
    eah->dnulls = dnulls = (bool *)
2693
0
      MemoryContextAllocZero(eah->hdr.eoh_context,
2694
0
                   eah->dvalueslen * sizeof(bool));
2695
2696
  /*
2697
   * We now have all the needed space allocated, so we're ready to make
2698
   * irreversible changes.  Be very wary of allowing failure below here.
2699
   */
2700
2701
  /* Flattened value will no longer represent array accurately */
2702
0
  eah->fvalue = NULL;
2703
  /* And we don't know the flattened size either */
2704
0
  eah->flat_size = 0;
2705
2706
  /* Update dimensionality info if needed */
2707
0
  if (dimschanged)
2708
0
  {
2709
0
    eah->ndims = ndim;
2710
0
    memcpy(eah->dims, dim, ndim * sizeof(int));
2711
0
    memcpy(eah->lbound, lb, ndim * sizeof(int));
2712
0
  }
2713
2714
  /* Reposition items if needed, and fill addedbefore items with nulls */
2715
0
  if (addedbefore > 0)
2716
0
  {
2717
0
    memmove(dvalues + addedbefore, dvalues, eah->nelems * sizeof(Datum));
2718
0
    for (i = 0; i < addedbefore; i++)
2719
0
      dvalues[i] = (Datum) 0;
2720
0
    if (dnulls)
2721
0
    {
2722
0
      memmove(dnulls + addedbefore, dnulls, eah->nelems * sizeof(bool));
2723
0
      for (i = 0; i < addedbefore; i++)
2724
0
        dnulls[i] = true;
2725
0
    }
2726
0
    eah->nelems += addedbefore;
2727
0
  }
2728
2729
  /* fill addedafter items with nulls */
2730
0
  if (addedafter > 0)
2731
0
  {
2732
0
    for (i = 0; i < addedafter; i++)
2733
0
      dvalues[eah->nelems + i] = (Datum) 0;
2734
0
    if (dnulls)
2735
0
    {
2736
0
      for (i = 0; i < addedafter; i++)
2737
0
        dnulls[eah->nelems + i] = true;
2738
0
    }
2739
0
    eah->nelems += addedafter;
2740
0
  }
2741
2742
  /* Grab old element value for pfree'ing, if needed. */
2743
0
  if (!eah->typbyval && (dnulls == NULL || !dnulls[offset]))
2744
0
    oldValue = (char *) DatumGetPointer(dvalues[offset]);
2745
0
  else
2746
0
    oldValue = NULL;
2747
2748
  /* And finally we can insert the new element. */
2749
0
  dvalues[offset] = dataValue;
2750
0
  if (dnulls)
2751
0
    dnulls[offset] = isNull;
2752
2753
  /*
2754
   * Free old element if needed; this keeps repeated element replacements
2755
   * from bloating the array's storage.  If the pfree somehow fails, it
2756
   * won't corrupt the array.
2757
   */
2758
0
  if (oldValue)
2759
0
  {
2760
    /* Don't try to pfree a part of the original flat array */
2761
0
    if (oldValue < eah->fstartptr || oldValue >= eah->fendptr)
2762
0
      pfree(oldValue);
2763
0
  }
2764
2765
  /* Done, return standard TOAST pointer for object */
2766
0
  return EOHPGetRWDatum(&eah->hdr);
2767
0
}
2768
2769
/*
2770
 * array_set_slice :
2771
 *      This routine sets the value of a range of array locations (specified
2772
 *      by upper and lower subscript values) to new values passed as
2773
 *      another array.
2774
 *
2775
 * This handles both ordinary varlena arrays and fixed-length arrays.
2776
 *
2777
 * Inputs:
2778
 *  arraydatum: the initial array object (mustn't be NULL)
2779
 *  nSubscripts: number of subscripts supplied (must be same for upper/lower)
2780
 *  upperIndx[]: the upper subscript values
2781
 *  lowerIndx[]: the lower subscript values
2782
 *  upperProvided[]: true for provided upper subscript values
2783
 *  lowerProvided[]: true for provided lower subscript values
2784
 *  srcArrayDatum: the source for the inserted values
2785
 *  isNull: indicates whether srcArrayDatum is NULL
2786
 *  arraytyplen: pg_type.typlen for the array type
2787
 *  elmlen: pg_type.typlen for the array's element type
2788
 *  elmbyval: pg_type.typbyval for the array's element type
2789
 *  elmalign: pg_type.typalign for the array's element type
2790
 *
2791
 * Result:
2792
 *      A new array is returned, just like the old except for the
2793
 *      modified range.  The original array object is not changed.
2794
 *
2795
 * Omitted upper and lower subscript values are replaced by the corresponding
2796
 * array bound.
2797
 *
2798
 * For one-dimensional arrays only, we allow the array to be extended
2799
 * by assigning to positions outside the existing subscript range; any
2800
 * positions between the existing elements and the new ones are set to NULLs.
2801
 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
2802
 *
2803
 * NOTE: we assume it is OK to scribble on the provided index arrays
2804
 * lowerIndx[] and upperIndx[]; also, these arrays must be of size MAXDIM
2805
 * even when nSubscripts is less.  These are generally just temporaries.
2806
 *
2807
 * NOTE: For assignments, we throw an error for silly subscripts etc,
2808
 * rather than returning a NULL or empty array as the fetch operations do.
2809
 */
2810
Datum
2811
array_set_slice(Datum arraydatum,
2812
        int nSubscripts,
2813
        int *upperIndx,
2814
        int *lowerIndx,
2815
        bool *upperProvided,
2816
        bool *lowerProvided,
2817
        Datum srcArrayDatum,
2818
        bool isNull,
2819
        int arraytyplen,
2820
        int elmlen,
2821
        bool elmbyval,
2822
        char elmalign)
2823
0
{
2824
0
  ArrayType  *array;
2825
0
  ArrayType  *srcArray;
2826
0
  ArrayType  *newarray;
2827
0
  int     i,
2828
0
        ndim,
2829
0
        dim[MAXDIM],
2830
0
        lb[MAXDIM],
2831
0
        span[MAXDIM];
2832
0
  bool    newhasnulls;
2833
0
  int     nitems,
2834
0
        nsrcitems,
2835
0
        olddatasize,
2836
0
        newsize,
2837
0
        olditemsize,
2838
0
        newitemsize,
2839
0
        overheadlen,
2840
0
        oldoverheadlen,
2841
0
        addedbefore,
2842
0
        addedafter,
2843
0
        lenbefore,
2844
0
        lenafter,
2845
0
        itemsbefore,
2846
0
        itemsafter,
2847
0
        nolditems;
2848
2849
  /* Currently, assignment from a NULL source array is a no-op */
2850
0
  if (isNull)
2851
0
    return arraydatum;
2852
2853
0
  if (arraytyplen > 0)
2854
0
  {
2855
    /*
2856
     * fixed-length arrays -- not got round to doing this...
2857
     */
2858
0
    ereport(ERROR,
2859
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2860
0
         errmsg("updates on slices of fixed-length arrays not implemented")));
2861
0
  }
2862
2863
  /* detoast arrays if necessary */
2864
0
  array = DatumGetArrayTypeP(arraydatum);
2865
0
  srcArray = DatumGetArrayTypeP(srcArrayDatum);
2866
2867
  /* note: we assume srcArray contains no toasted elements */
2868
2869
0
  ndim = ARR_NDIM(array);
2870
2871
  /*
2872
   * if number of dims is zero, i.e. an empty array, create an array with
2873
   * nSubscripts dimensions, and set the upper and lower bounds to the
2874
   * supplied subscripts
2875
   */
2876
0
  if (ndim == 0)
2877
0
  {
2878
0
    Datum    *dvalues;
2879
0
    bool     *dnulls;
2880
0
    int     nelems;
2881
0
    Oid     elmtype = ARR_ELEMTYPE(array);
2882
2883
0
    deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
2884
0
              &dvalues, &dnulls, &nelems);
2885
2886
0
    for (i = 0; i < nSubscripts; i++)
2887
0
    {
2888
0
      if (!upperProvided[i] || !lowerProvided[i])
2889
0
        ereport(ERROR,
2890
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2891
0
             errmsg("array slice subscript must provide both boundaries"),
2892
0
             errdetail("When assigning to a slice of an empty array value,"
2893
0
                   " slice boundaries must be fully specified.")));
2894
2895
      /* compute "upperIndx[i] - lowerIndx[i] + 1", detecting overflow */
2896
0
      if (pg_sub_s32_overflow(upperIndx[i], lowerIndx[i], &dim[i]) ||
2897
0
        pg_add_s32_overflow(dim[i], 1, &dim[i]))
2898
0
        ereport(ERROR,
2899
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2900
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2901
0
                MaxArraySize)));
2902
2903
0
      lb[i] = lowerIndx[i];
2904
0
    }
2905
2906
    /* complain if too few source items; we ignore extras, however */
2907
0
    if (nelems < ArrayGetNItems(nSubscripts, dim))
2908
0
      ereport(ERROR,
2909
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2910
0
           errmsg("source array too small")));
2911
2912
0
    return PointerGetDatum(construct_md_array(dvalues, dnulls, nSubscripts,
2913
0
                          dim, lb, elmtype,
2914
0
                          elmlen, elmbyval, elmalign));
2915
0
  }
2916
2917
0
  if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
2918
0
    ereport(ERROR,
2919
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2920
0
         errmsg("wrong number of array subscripts")));
2921
2922
  /* copy dim/lb since we may modify them */
2923
0
  memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
2924
0
  memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
2925
2926
0
  newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
2927
0
  addedbefore = addedafter = 0;
2928
2929
  /*
2930
   * Check subscripts.  We assume the existing subscripts passed
2931
   * ArrayCheckBounds, so that dim[i] + lb[i] can be computed without
2932
   * overflow.  But we must beware of other overflows in our calculations of
2933
   * new dim[] values.
2934
   */
2935
0
  if (ndim == 1)
2936
0
  {
2937
0
    Assert(nSubscripts == 1);
2938
0
    if (!lowerProvided[0])
2939
0
      lowerIndx[0] = lb[0];
2940
0
    if (!upperProvided[0])
2941
0
      upperIndx[0] = dim[0] + lb[0] - 1;
2942
0
    if (lowerIndx[0] > upperIndx[0])
2943
0
      ereport(ERROR,
2944
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2945
0
           errmsg("upper bound cannot be less than lower bound")));
2946
0
    if (lowerIndx[0] < lb[0])
2947
0
    {
2948
      /* addedbefore = lb[0] - lowerIndx[0]; */
2949
      /* dim[0] += addedbefore; */
2950
0
      if (pg_sub_s32_overflow(lb[0], lowerIndx[0], &addedbefore) ||
2951
0
        pg_add_s32_overflow(dim[0], addedbefore, &dim[0]))
2952
0
        ereport(ERROR,
2953
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2954
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2955
0
                MaxArraySize)));
2956
0
      lb[0] = lowerIndx[0];
2957
0
      if (addedbefore > 1)
2958
0
        newhasnulls = true; /* will insert nulls */
2959
0
    }
2960
0
    if (upperIndx[0] >= (dim[0] + lb[0]))
2961
0
    {
2962
      /* addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1; */
2963
      /* dim[0] += addedafter; */
2964
0
      if (pg_sub_s32_overflow(upperIndx[0], dim[0] + lb[0], &addedafter) ||
2965
0
        pg_add_s32_overflow(addedafter, 1, &addedafter) ||
2966
0
        pg_add_s32_overflow(dim[0], addedafter, &dim[0]))
2967
0
        ereport(ERROR,
2968
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2969
0
             errmsg("array size exceeds the maximum allowed (%zu)",
2970
0
                MaxArraySize)));
2971
0
      if (addedafter > 1)
2972
0
        newhasnulls = true; /* will insert nulls */
2973
0
    }
2974
0
  }
2975
0
  else
2976
0
  {
2977
    /*
2978
     * XXX currently we do not support extending multi-dimensional arrays
2979
     * during assignment
2980
     */
2981
0
    for (i = 0; i < nSubscripts; i++)
2982
0
    {
2983
0
      if (!lowerProvided[i])
2984
0
        lowerIndx[i] = lb[i];
2985
0
      if (!upperProvided[i])
2986
0
        upperIndx[i] = dim[i] + lb[i] - 1;
2987
0
      if (lowerIndx[i] > upperIndx[i])
2988
0
        ereport(ERROR,
2989
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2990
0
             errmsg("upper bound cannot be less than lower bound")));
2991
0
      if (lowerIndx[i] < lb[i] ||
2992
0
        upperIndx[i] >= (dim[i] + lb[i]))
2993
0
        ereport(ERROR,
2994
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2995
0
             errmsg("array subscript out of range")));
2996
0
    }
2997
    /* fill any missing subscript positions with full array range */
2998
0
    for (; i < ndim; i++)
2999
0
    {
3000
0
      lowerIndx[i] = lb[i];
3001
0
      upperIndx[i] = dim[i] + lb[i] - 1;
3002
0
      if (lowerIndx[i] > upperIndx[i])
3003
0
        ereport(ERROR,
3004
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3005
0
             errmsg("upper bound cannot be less than lower bound")));
3006
0
    }
3007
0
  }
3008
3009
  /* Do this mainly to check for overflow */
3010
0
  nitems = ArrayGetNItems(ndim, dim);
3011
0
  ArrayCheckBounds(ndim, dim, lb);
3012
3013
  /*
3014
   * Make sure source array has enough entries.  Note we ignore the shape of
3015
   * the source array and just read entries serially.
3016
   */
3017
0
  mda_get_range(ndim, span, lowerIndx, upperIndx);
3018
0
  nsrcitems = ArrayGetNItems(ndim, span);
3019
0
  if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
3020
0
    ereport(ERROR,
3021
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
3022
0
         errmsg("source array too small")));
3023
3024
  /*
3025
   * Compute space occupied by new entries, space occupied by replaced
3026
   * entries, and required space for new array.
3027
   */
3028
0
  if (newhasnulls)
3029
0
    overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3030
0
  else
3031
0
    overheadlen = ARR_OVERHEAD_NONULLS(ndim);
3032
0
  newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
3033
0
                  ARR_NULLBITMAP(srcArray), nsrcitems,
3034
0
                  elmlen, elmbyval, elmalign);
3035
0
  oldoverheadlen = ARR_DATA_OFFSET(array);
3036
0
  olddatasize = ARR_SIZE(array) - oldoverheadlen;
3037
0
  if (ndim > 1)
3038
0
  {
3039
    /*
3040
     * here we do not need to cope with extension of the array; it would
3041
     * be a lot more complicated if we had to do so...
3042
     */
3043
0
    olditemsize = array_slice_size(ARR_DATA_PTR(array),
3044
0
                     ARR_NULLBITMAP(array),
3045
0
                     ndim, dim, lb,
3046
0
                     lowerIndx, upperIndx,
3047
0
                     elmlen, elmbyval, elmalign);
3048
0
    lenbefore = lenafter = 0; /* keep compiler quiet */
3049
0
    itemsbefore = itemsafter = nolditems = 0;
3050
0
  }
3051
0
  else
3052
0
  {
3053
    /*
3054
     * here we must allow for possibility of slice larger than orig array
3055
     * and/or not adjacent to orig array subscripts
3056
     */
3057
0
    int     oldlb = ARR_LBOUND(array)[0];
3058
0
    int     oldub = oldlb + ARR_DIMS(array)[0] - 1;
3059
0
    int     slicelb = Max(oldlb, lowerIndx[0]);
3060
0
    int     sliceub = Min(oldub, upperIndx[0]);
3061
0
    char     *oldarraydata = ARR_DATA_PTR(array);
3062
0
    uint8    *oldarraybitmap = ARR_NULLBITMAP(array);
3063
3064
    /* count/size of old array entries that will go before the slice */
3065
0
    itemsbefore = Min(slicelb, oldub + 1) - oldlb;
3066
0
    lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
3067
0
                    itemsbefore,
3068
0
                    elmlen, elmbyval, elmalign);
3069
    /* count/size of old array entries that will be replaced by slice */
3070
0
    if (slicelb > sliceub)
3071
0
    {
3072
0
      nolditems = 0;
3073
0
      olditemsize = 0;
3074
0
    }
3075
0
    else
3076
0
    {
3077
0
      nolditems = sliceub - slicelb + 1;
3078
0
      olditemsize = array_nelems_size(oldarraydata + lenbefore,
3079
0
                      itemsbefore, oldarraybitmap,
3080
0
                      nolditems,
3081
0
                      elmlen, elmbyval, elmalign);
3082
0
    }
3083
    /* count/size of old array entries that will go after the slice */
3084
0
    itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
3085
0
    lenafter = olddatasize - lenbefore - olditemsize;
3086
0
  }
3087
3088
0
  newsize = overheadlen + olddatasize - olditemsize + newitemsize;
3089
3090
0
  newarray = (ArrayType *) palloc0(newsize);
3091
0
  SET_VARSIZE(newarray, newsize);
3092
0
  newarray->ndim = ndim;
3093
0
  newarray->dataoffset = newhasnulls ? overheadlen : 0;
3094
0
  newarray->elemtype = ARR_ELEMTYPE(array);
3095
0
  memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
3096
0
  memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
3097
3098
0
  if (ndim > 1)
3099
0
  {
3100
    /*
3101
     * here we do not need to cope with extension of the array; it would
3102
     * be a lot more complicated if we had to do so...
3103
     */
3104
0
    array_insert_slice(newarray, array, srcArray,
3105
0
               ndim, dim, lb,
3106
0
               lowerIndx, upperIndx,
3107
0
               elmlen, elmbyval, elmalign);
3108
0
  }
3109
0
  else
3110
0
  {
3111
    /* fill in data */
3112
0
    memcpy((char *) newarray + overheadlen,
3113
0
         (char *) array + oldoverheadlen,
3114
0
         lenbefore);
3115
0
    memcpy((char *) newarray + overheadlen + lenbefore,
3116
0
         ARR_DATA_PTR(srcArray),
3117
0
         newitemsize);
3118
0
    memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
3119
0
         (char *) array + oldoverheadlen + lenbefore + olditemsize,
3120
0
         lenafter);
3121
    /* fill in nulls bitmap if needed */
3122
0
    if (newhasnulls)
3123
0
    {
3124
0
      uint8    *newnullbitmap = ARR_NULLBITMAP(newarray);
3125
0
      uint8    *oldnullbitmap = ARR_NULLBITMAP(array);
3126
3127
      /* palloc0 above already marked any inserted positions as nulls */
3128
0
      array_bitmap_copy(newnullbitmap, addedbefore,
3129
0
                oldnullbitmap, 0,
3130
0
                itemsbefore);
3131
0
      array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
3132
0
                ARR_NULLBITMAP(srcArray), 0,
3133
0
                nsrcitems);
3134
0
      array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
3135
0
                oldnullbitmap, itemsbefore + nolditems,
3136
0
                itemsafter);
3137
0
    }
3138
0
  }
3139
3140
0
  return PointerGetDatum(newarray);
3141
0
}
3142
3143
/*
3144
 * array_ref : backwards compatibility wrapper for array_get_element
3145
 *
3146
 * This only works for detoasted/flattened varlena arrays, since the array
3147
 * argument is declared as "ArrayType *".  However there's enough code like
3148
 * that to justify preserving this API.
3149
 */
3150
Datum
3151
array_ref(ArrayType *array, int nSubscripts, int *indx,
3152
      int arraytyplen, int elmlen, bool elmbyval, char elmalign,
3153
      bool *isNull)
3154
0
{
3155
0
  return array_get_element(PointerGetDatum(array), nSubscripts, indx,
3156
0
               arraytyplen, elmlen, elmbyval, elmalign,
3157
0
               isNull);
3158
0
}
3159
3160
/*
3161
 * array_set : backwards compatibility wrapper for array_set_element
3162
 *
3163
 * This only works for detoasted/flattened varlena arrays, since the array
3164
 * argument and result are declared as "ArrayType *".  However there's enough
3165
 * code like that to justify preserving this API.
3166
 */
3167
ArrayType *
3168
array_set(ArrayType *array, int nSubscripts, int *indx,
3169
      Datum dataValue, bool isNull,
3170
      int arraytyplen, int elmlen, bool elmbyval, char elmalign)
3171
0
{
3172
0
  return DatumGetArrayTypeP(array_set_element(PointerGetDatum(array),
3173
0
                        nSubscripts, indx,
3174
0
                        dataValue, isNull,
3175
0
                        arraytyplen,
3176
0
                        elmlen, elmbyval, elmalign));
3177
0
}
3178
3179
/*
3180
 * array_map()
3181
 *
3182
 * Map an array through an arbitrary expression.  Return a new array with
3183
 * the same dimensions and each source element transformed by the given,
3184
 * already-compiled expression.  Each source element is placed in the
3185
 * innermost_caseval/innermost_casenull fields of the ExprState.
3186
 *
3187
 * Parameters are:
3188
 * * arrayd: Datum representing array argument.
3189
 * * exprstate: ExprState representing the per-element transformation.
3190
 * * econtext: context for expression evaluation.
3191
 * * retType: OID of element type of output array.  This must be the same as,
3192
 *   or binary-compatible with, the result type of the expression.  It might
3193
 *   be different from the input array's element type.
3194
 * * amstate: workspace for array_map.  Must be zeroed by caller before
3195
 *   first call, and not touched after that.
3196
 *
3197
 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
3198
 * but better performance can be had if the state can be preserved across
3199
 * a series of calls.
3200
 *
3201
 * NB: caller must assure that input array is not NULL.  NULL elements in
3202
 * the array are OK however.
3203
 * NB: caller should be running in econtext's per-tuple memory context.
3204
 */
3205
Datum
3206
array_map(Datum arrayd,
3207
      ExprState *exprstate, ExprContext *econtext,
3208
      Oid retType, ArrayMapState *amstate)
3209
0
{
3210
0
  AnyArrayType *v = DatumGetAnyArrayP(arrayd);
3211
0
  ArrayType  *result;
3212
0
  Datum    *values;
3213
0
  bool     *nulls;
3214
0
  int      *dim;
3215
0
  int     ndim;
3216
0
  int     nitems;
3217
0
  int     i;
3218
0
  int32   nbytes = 0;
3219
0
  int32   dataoffset;
3220
0
  bool    hasnulls;
3221
0
  Oid     inpType;
3222
0
  int     inp_typlen;
3223
0
  bool    inp_typbyval;
3224
0
  char    inp_typalign;
3225
0
  int     typlen;
3226
0
  bool    typbyval;
3227
0
  char    typalign;
3228
0
  uint8   typalignby;
3229
0
  array_iter  iter;
3230
0
  ArrayMetaState *inp_extra;
3231
0
  ArrayMetaState *ret_extra;
3232
0
  Datum    *transform_source = exprstate->innermost_caseval;
3233
0
  bool     *transform_source_isnull = exprstate->innermost_casenull;
3234
3235
0
  inpType = AARR_ELEMTYPE(v);
3236
0
  ndim = AARR_NDIM(v);
3237
0
  dim = AARR_DIMS(v);
3238
0
  nitems = ArrayGetNItems(ndim, dim);
3239
3240
  /* Check for empty array */
3241
0
  if (nitems <= 0)
3242
0
  {
3243
    /* Return empty array */
3244
0
    return PointerGetDatum(construct_empty_array(retType));
3245
0
  }
3246
3247
  /*
3248
   * We arrange to look up info about input and return element types only
3249
   * once per series of calls, assuming the element type doesn't change
3250
   * underneath us.
3251
   */
3252
0
  inp_extra = &amstate->inp_extra;
3253
0
  ret_extra = &amstate->ret_extra;
3254
3255
0
  if (inp_extra->element_type != inpType)
3256
0
  {
3257
0
    get_typlenbyvalalign(inpType,
3258
0
               &inp_extra->typlen,
3259
0
               &inp_extra->typbyval,
3260
0
               &inp_extra->typalign);
3261
0
    inp_extra->element_type = inpType;
3262
0
  }
3263
0
  inp_typlen = inp_extra->typlen;
3264
0
  inp_typbyval = inp_extra->typbyval;
3265
0
  inp_typalign = inp_extra->typalign;
3266
3267
0
  if (ret_extra->element_type != retType)
3268
0
  {
3269
0
    get_typlenbyvalalign(retType,
3270
0
               &ret_extra->typlen,
3271
0
               &ret_extra->typbyval,
3272
0
               &ret_extra->typalign);
3273
0
    ret_extra->element_type = retType;
3274
0
  }
3275
0
  typlen = ret_extra->typlen;
3276
0
  typbyval = ret_extra->typbyval;
3277
0
  typalign = ret_extra->typalign;
3278
0
  typalignby = typalign_to_alignby(typalign);
3279
3280
  /* Allocate temporary arrays for new values */
3281
0
  values = (Datum *) palloc(nitems * sizeof(Datum));
3282
0
  nulls = (bool *) palloc(nitems * sizeof(bool));
3283
3284
  /* Loop over source data */
3285
0
  array_iter_setup(&iter, v, inp_typlen, inp_typbyval, inp_typalign);
3286
0
  hasnulls = false;
3287
3288
0
  for (i = 0; i < nitems; i++)
3289
0
  {
3290
    /* Get source element, checking for NULL */
3291
0
    *transform_source =
3292
0
      array_iter_next(&iter, transform_source_isnull, i);
3293
3294
    /* Apply the given expression to source element */
3295
0
    values[i] = ExecEvalExpr(exprstate, econtext, &nulls[i]);
3296
3297
0
    if (nulls[i])
3298
0
      hasnulls = true;
3299
0
    else
3300
0
    {
3301
      /* Ensure data is not toasted */
3302
0
      if (typlen == -1)
3303
0
        values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
3304
      /* Update total result size */
3305
0
      nbytes = att_addlength_datum(nbytes, typlen, values[i]);
3306
0
      nbytes = att_nominal_alignby(nbytes, typalignby);
3307
      /* check for overflow of total request */
3308
0
      if (!AllocSizeIsValid(nbytes))
3309
0
        ereport(ERROR,
3310
0
            (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3311
0
             errmsg("array size exceeds the maximum allowed (%zu)",
3312
0
                MaxAllocSize)));
3313
0
    }
3314
0
  }
3315
3316
  /* Allocate and fill the result array */
3317
0
  if (hasnulls)
3318
0
  {
3319
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
3320
0
    nbytes += dataoffset;
3321
0
  }
3322
0
  else
3323
0
  {
3324
0
    dataoffset = 0;     /* marker for no null bitmap */
3325
0
    nbytes += ARR_OVERHEAD_NONULLS(ndim);
3326
0
  }
3327
0
  result = (ArrayType *) palloc0(nbytes);
3328
0
  SET_VARSIZE(result, nbytes);
3329
0
  result->ndim = ndim;
3330
0
  result->dataoffset = dataoffset;
3331
0
  result->elemtype = retType;
3332
0
  memcpy(ARR_DIMS(result), AARR_DIMS(v), ndim * sizeof(int));
3333
0
  memcpy(ARR_LBOUND(result), AARR_LBOUND(v), ndim * sizeof(int));
3334
3335
0
  CopyArrayEls(result,
3336
0
         values, nulls, nitems,
3337
0
         typlen, typbyval, typalign,
3338
0
         false);
3339
3340
  /*
3341
   * Note: do not risk trying to pfree the results of the called expression
3342
   */
3343
0
  pfree(values);
3344
0
  pfree(nulls);
3345
3346
0
  return PointerGetDatum(result);
3347
0
}
3348
3349
/*
3350
 * construct_array  --- simple method for constructing an array object
3351
 *
3352
 * elems: array of Datum items to become the array contents
3353
 *      (NULL element values are not supported).
3354
 * nelems: number of items
3355
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3356
 *
3357
 * A palloc'd 1-D array object is constructed and returned.  Note that
3358
 * elem values will be copied into the object even if pass-by-ref type.
3359
 * Also note the result will be 0-D not 1-D if nelems = 0.
3360
 *
3361
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3362
 * from the system catalogs, given the elmtype.  However, the caller is
3363
 * in a better position to cache this info across multiple uses, or even
3364
 * to hard-wire values if the element type is hard-wired.
3365
 */
3366
ArrayType *
3367
construct_array(Datum *elems, int nelems,
3368
        Oid elmtype,
3369
        int elmlen, bool elmbyval, char elmalign)
3370
0
{
3371
0
  int     dims[1];
3372
0
  int     lbs[1];
3373
3374
0
  dims[0] = nelems;
3375
0
  lbs[0] = 1;
3376
3377
0
  return construct_md_array(elems, NULL, 1, dims, lbs,
3378
0
                elmtype, elmlen, elmbyval, elmalign);
3379
0
}
3380
3381
/*
3382
 * Like construct_array(), where elmtype must be a built-in type, and
3383
 * elmlen/elmbyval/elmalign is looked up from hardcoded data.  This is often
3384
 * useful when manipulating arrays from/for system catalogs.
3385
 */
3386
ArrayType *
3387
construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
3388
0
{
3389
0
  int     elmlen;
3390
0
  bool    elmbyval;
3391
0
  char    elmalign;
3392
3393
0
  switch (elmtype)
3394
0
  {
3395
0
    case CHAROID:
3396
0
      elmlen = 1;
3397
0
      elmbyval = true;
3398
0
      elmalign = TYPALIGN_CHAR;
3399
0
      break;
3400
3401
0
    case CSTRINGOID:
3402
0
      elmlen = -2;
3403
0
      elmbyval = false;
3404
0
      elmalign = TYPALIGN_CHAR;
3405
0
      break;
3406
3407
0
    case FLOAT4OID:
3408
0
      elmlen = sizeof(float4);
3409
0
      elmbyval = true;
3410
0
      elmalign = TYPALIGN_INT;
3411
0
      break;
3412
3413
0
    case FLOAT8OID:
3414
0
      elmlen = sizeof(float8);
3415
0
      elmbyval = true;
3416
0
      elmalign = TYPALIGN_DOUBLE;
3417
0
      break;
3418
3419
0
    case INT2OID:
3420
0
      elmlen = sizeof(int16);
3421
0
      elmbyval = true;
3422
0
      elmalign = TYPALIGN_SHORT;
3423
0
      break;
3424
3425
0
    case INT4OID:
3426
0
      elmlen = sizeof(int32);
3427
0
      elmbyval = true;
3428
0
      elmalign = TYPALIGN_INT;
3429
0
      break;
3430
3431
0
    case INT8OID:
3432
0
      elmlen = sizeof(int64);
3433
0
      elmbyval = true;
3434
0
      elmalign = TYPALIGN_DOUBLE;
3435
0
      break;
3436
3437
0
    case NAMEOID:
3438
0
      elmlen = NAMEDATALEN;
3439
0
      elmbyval = false;
3440
0
      elmalign = TYPALIGN_CHAR;
3441
0
      break;
3442
3443
0
    case OIDOID:
3444
0
    case REGTYPEOID:
3445
0
      elmlen = sizeof(Oid);
3446
0
      elmbyval = true;
3447
0
      elmalign = TYPALIGN_INT;
3448
0
      break;
3449
3450
0
    case TEXTOID:
3451
0
      elmlen = -1;
3452
0
      elmbyval = false;
3453
0
      elmalign = TYPALIGN_INT;
3454
0
      break;
3455
3456
0
    case TIDOID:
3457
0
      elmlen = sizeof(ItemPointerData);
3458
0
      elmbyval = false;
3459
0
      elmalign = TYPALIGN_SHORT;
3460
0
      break;
3461
3462
0
    case XIDOID:
3463
0
      elmlen = sizeof(TransactionId);
3464
0
      elmbyval = true;
3465
0
      elmalign = TYPALIGN_INT;
3466
0
      break;
3467
3468
0
    default:
3469
0
      elog(ERROR, "type %u not supported by construct_array_builtin()", elmtype);
3470
      /* keep compiler quiet */
3471
0
      elmlen = 0;
3472
0
      elmbyval = false;
3473
0
      elmalign = 0;
3474
0
  }
3475
3476
0
  return construct_array(elems, nelems, elmtype, elmlen, elmbyval, elmalign);
3477
0
}
3478
3479
/*
3480
 * construct_md_array --- simple method for constructing an array object
3481
 *              with arbitrary dimensions and possible NULLs
3482
 *
3483
 * elems: array of Datum items to become the array contents
3484
 * nulls: array of is-null flags (can be NULL if no nulls)
3485
 * ndims: number of dimensions
3486
 * dims: integer array with size of each dimension
3487
 * lbs: integer array with lower bound of each dimension
3488
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3489
 *
3490
 * A palloc'd ndims-D array object is constructed and returned.  Note that
3491
 * elem values will be copied into the object even if pass-by-ref type.
3492
 * Also note the result will be 0-D not ndims-D if any dims[i] = 0.
3493
 *
3494
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3495
 * from the system catalogs, given the elmtype.  However, the caller is
3496
 * in a better position to cache this info across multiple uses, or even
3497
 * to hard-wire values if the element type is hard-wired.
3498
 */
3499
ArrayType *
3500
construct_md_array(Datum *elems,
3501
           bool *nulls,
3502
           int ndims,
3503
           int *dims,
3504
           int *lbs,
3505
           Oid elmtype, int elmlen, bool elmbyval, char elmalign)
3506
0
{
3507
0
  ArrayType  *result;
3508
0
  bool    hasnulls;
3509
0
  int32   nbytes;
3510
0
  int32   dataoffset;
3511
0
  int     i;
3512
0
  int     nelems;
3513
0
  uint8   elmalignby = typalign_to_alignby(elmalign);
3514
3515
0
  if (ndims < 0)       /* we do allow zero-dimension arrays */
3516
0
    ereport(ERROR,
3517
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3518
0
         errmsg("invalid number of dimensions: %d", ndims)));
3519
0
  if (ndims > MAXDIM)
3520
0
    ereport(ERROR,
3521
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3522
0
         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
3523
0
            ndims, MAXDIM)));
3524
3525
  /* This checks for overflow of the array dimensions */
3526
0
  nelems = ArrayGetNItems(ndims, dims);
3527
0
  ArrayCheckBounds(ndims, dims, lbs);
3528
3529
  /* if ndims <= 0 or any dims[i] == 0, return empty array */
3530
0
  if (nelems <= 0)
3531
0
    return construct_empty_array(elmtype);
3532
3533
  /* compute required space */
3534
0
  nbytes = 0;
3535
0
  hasnulls = false;
3536
0
  for (i = 0; i < nelems; i++)
3537
0
  {
3538
0
    if (nulls && nulls[i])
3539
0
    {
3540
0
      hasnulls = true;
3541
0
      continue;
3542
0
    }
3543
    /* make sure data is not toasted */
3544
0
    if (elmlen == -1)
3545
0
      elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
3546
0
    nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
3547
0
    nbytes = att_nominal_alignby(nbytes, elmalignby);
3548
    /* check for overflow of total request */
3549
0
    if (!AllocSizeIsValid(nbytes))
3550
0
      ereport(ERROR,
3551
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3552
0
           errmsg("array size exceeds the maximum allowed (%zu)",
3553
0
              MaxAllocSize)));
3554
0
  }
3555
3556
  /* Allocate and initialize result array */
3557
0
  if (hasnulls)
3558
0
  {
3559
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
3560
0
    nbytes += dataoffset;
3561
0
  }
3562
0
  else
3563
0
  {
3564
0
    dataoffset = 0;     /* marker for no null bitmap */
3565
0
    nbytes += ARR_OVERHEAD_NONULLS(ndims);
3566
0
  }
3567
0
  result = (ArrayType *) palloc0(nbytes);
3568
0
  SET_VARSIZE(result, nbytes);
3569
0
  result->ndim = ndims;
3570
0
  result->dataoffset = dataoffset;
3571
0
  result->elemtype = elmtype;
3572
0
  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
3573
0
  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
3574
3575
0
  CopyArrayEls(result,
3576
0
         elems, nulls, nelems,
3577
0
         elmlen, elmbyval, elmalign,
3578
0
         false);
3579
3580
0
  return result;
3581
0
}
3582
3583
/*
3584
 * construct_empty_array  --- make a zero-dimensional array of given type
3585
 */
3586
ArrayType *
3587
construct_empty_array(Oid elmtype)
3588
0
{
3589
0
  ArrayType  *result;
3590
3591
0
  result = palloc0_object(ArrayType);
3592
0
  SET_VARSIZE(result, sizeof(ArrayType));
3593
0
  result->ndim = 0;
3594
0
  result->dataoffset = 0;
3595
0
  result->elemtype = elmtype;
3596
0
  return result;
3597
0
}
3598
3599
/*
3600
 * construct_empty_expanded_array: make an empty expanded array
3601
 * given only type information.  (metacache can be NULL if not needed.)
3602
 */
3603
ExpandedArrayHeader *
3604
construct_empty_expanded_array(Oid element_type,
3605
                 MemoryContext parentcontext,
3606
                 ArrayMetaState *metacache)
3607
0
{
3608
0
  ArrayType  *array = construct_empty_array(element_type);
3609
0
  Datum   d;
3610
3611
0
  d = expand_array(PointerGetDatum(array), parentcontext, metacache);
3612
0
  pfree(array);
3613
0
  return (ExpandedArrayHeader *) DatumGetEOHP(d);
3614
0
}
3615
3616
/*
3617
 * deconstruct_array  --- simple method for extracting data from an array
3618
 *
3619
 * array: array object to examine (must not be NULL)
3620
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
3621
 * elemsp: return value, set to point to palloc'd array of Datum values
3622
 * nullsp: return value, set to point to palloc'd array of isnull markers
3623
 * nelemsp: return value, set to number of extracted values
3624
 *
3625
 * The caller may pass nullsp == NULL if it does not support NULLs in the
3626
 * array.  Note that this produces a very uninformative error message,
3627
 * so do it only in cases where a NULL is really not expected.
3628
 *
3629
 * If array elements are pass-by-ref data type, the returned Datums will
3630
 * be pointers into the array object.
3631
 *
3632
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
3633
 * from the system catalogs, given the elmtype.  However, the caller is
3634
 * in a better position to cache this info across multiple uses, or even
3635
 * to hard-wire values if the element type is hard-wired.
3636
 */
3637
void
3638
deconstruct_array(const ArrayType *array,
3639
          Oid elmtype,
3640
          int elmlen, bool elmbyval, char elmalign,
3641
          Datum **elemsp, bool **nullsp, int *nelemsp)
3642
0
{
3643
0
  Datum    *elems;
3644
0
  bool     *nulls;
3645
0
  int     nelems;
3646
0
  char     *p;
3647
0
  uint8    *bitmap;
3648
0
  int     bitmask;
3649
0
  int     i;
3650
0
  uint8   elmalignby = typalign_to_alignby(elmalign);
3651
3652
0
  Assert(ARR_ELEMTYPE(array) == elmtype);
3653
3654
0
  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3655
0
  *elemsp = elems = palloc_array(Datum, nelems);
3656
0
  if (nullsp)
3657
0
    *nullsp = nulls = palloc0_array(bool, nelems);
3658
0
  else
3659
0
    nulls = NULL;
3660
0
  *nelemsp = nelems;
3661
3662
0
  p = ARR_DATA_PTR(array);
3663
0
  bitmap = ARR_NULLBITMAP(array);
3664
0
  bitmask = 1;
3665
3666
0
  for (i = 0; i < nelems; i++)
3667
0
  {
3668
    /* Get source element, checking for NULL */
3669
0
    if (bitmap && (*bitmap & bitmask) == 0)
3670
0
    {
3671
0
      elems[i] = (Datum) 0;
3672
0
      if (nulls)
3673
0
        nulls[i] = true;
3674
0
      else
3675
0
        ereport(ERROR,
3676
0
            (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
3677
0
             errmsg("null array element not allowed in this context")));
3678
0
    }
3679
0
    else
3680
0
    {
3681
0
      elems[i] = fetch_att(p, elmbyval, elmlen);
3682
0
      p = att_addlength_pointer(p, elmlen, p);
3683
0
      p = (char *) att_nominal_alignby(p, elmalignby);
3684
0
    }
3685
3686
    /* advance bitmap pointer if any */
3687
0
    if (bitmap)
3688
0
    {
3689
0
      bitmask <<= 1;
3690
0
      if (bitmask == 0x100)
3691
0
      {
3692
0
        bitmap++;
3693
0
        bitmask = 1;
3694
0
      }
3695
0
    }
3696
0
  }
3697
0
}
3698
3699
/*
3700
 * Like deconstruct_array(), where elmtype must be a built-in type, and
3701
 * elmlen/elmbyval/elmalign is looked up from hardcoded data.  This is often
3702
 * useful when manipulating arrays from/for system catalogs.
3703
 */
3704
void
3705
deconstruct_array_builtin(const ArrayType *array,
3706
              Oid elmtype,
3707
              Datum **elemsp, bool **nullsp, int *nelemsp)
3708
0
{
3709
0
  int     elmlen;
3710
0
  bool    elmbyval;
3711
0
  char    elmalign;
3712
3713
0
  switch (elmtype)
3714
0
  {
3715
0
    case CHAROID:
3716
0
      elmlen = 1;
3717
0
      elmbyval = true;
3718
0
      elmalign = TYPALIGN_CHAR;
3719
0
      break;
3720
3721
0
    case CSTRINGOID:
3722
0
      elmlen = -2;
3723
0
      elmbyval = false;
3724
0
      elmalign = TYPALIGN_CHAR;
3725
0
      break;
3726
3727
0
    case FLOAT8OID:
3728
0
      elmlen = sizeof(float8);
3729
0
      elmbyval = true;
3730
0
      elmalign = TYPALIGN_DOUBLE;
3731
0
      break;
3732
3733
0
    case INT2OID:
3734
0
      elmlen = sizeof(int16);
3735
0
      elmbyval = true;
3736
0
      elmalign = TYPALIGN_SHORT;
3737
0
      break;
3738
3739
0
    case INT4OID:
3740
0
      elmlen = sizeof(int32);
3741
0
      elmbyval = true;
3742
0
      elmalign = TYPALIGN_INT;
3743
0
      break;
3744
3745
0
    case OIDOID:
3746
0
      elmlen = sizeof(Oid);
3747
0
      elmbyval = true;
3748
0
      elmalign = TYPALIGN_INT;
3749
0
      break;
3750
3751
0
    case TEXTOID:
3752
0
      elmlen = -1;
3753
0
      elmbyval = false;
3754
0
      elmalign = TYPALIGN_INT;
3755
0
      break;
3756
3757
0
    case TIDOID:
3758
0
      elmlen = sizeof(ItemPointerData);
3759
0
      elmbyval = false;
3760
0
      elmalign = TYPALIGN_SHORT;
3761
0
      break;
3762
3763
0
    default:
3764
0
      elog(ERROR, "type %u not supported by deconstruct_array_builtin()", elmtype);
3765
      /* keep compiler quiet */
3766
0
      elmlen = 0;
3767
0
      elmbyval = false;
3768
0
      elmalign = 0;
3769
0
  }
3770
3771
0
  deconstruct_array(array, elmtype, elmlen, elmbyval, elmalign, elemsp, nullsp, nelemsp);
3772
0
}
3773
3774
/*
3775
 * array_contains_nulls --- detect whether an array has any null elements
3776
 *
3777
 * This gives an accurate answer, whereas testing ARR_HASNULL only tells
3778
 * if the array *might* contain a null.
3779
 */
3780
bool
3781
array_contains_nulls(const ArrayType *array)
3782
0
{
3783
0
  int     nelems;
3784
0
  uint8    *bitmap;
3785
0
  int     bitmask;
3786
3787
  /* Easy answer if there's no null bitmap */
3788
0
  if (!ARR_HASNULL(array))
3789
0
    return false;
3790
3791
0
  nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
3792
3793
0
  bitmap = ARR_NULLBITMAP(array);
3794
3795
  /* check whole bytes of the bitmap byte-at-a-time */
3796
0
  while (nelems >= 8)
3797
0
  {
3798
0
    if (*bitmap != 0xFF)
3799
0
      return true;
3800
0
    bitmap++;
3801
0
    nelems -= 8;
3802
0
  }
3803
3804
  /* check last partial byte */
3805
0
  bitmask = 1;
3806
0
  while (nelems > 0)
3807
0
  {
3808
0
    if ((*bitmap & bitmask) == 0)
3809
0
      return true;
3810
0
    bitmask <<= 1;
3811
0
    nelems--;
3812
0
  }
3813
3814
0
  return false;
3815
0
}
3816
3817
3818
/*
3819
 * array_eq :
3820
 *      compares two arrays for equality
3821
 * result :
3822
 *      returns true if the arrays are equal, false otherwise.
3823
 *
3824
 * Note: we do not use array_cmp here, since equality may be meaningful in
3825
 * datatypes that don't have a total ordering (and hence no btree support).
3826
 */
3827
Datum
3828
array_eq(PG_FUNCTION_ARGS)
3829
0
{
3830
0
  LOCAL_FCINFO(locfcinfo, 2);
3831
0
  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
3832
0
  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
3833
0
  Oid     collation = PG_GET_COLLATION();
3834
0
  int     ndims1 = AARR_NDIM(array1);
3835
0
  int     ndims2 = AARR_NDIM(array2);
3836
0
  int      *dims1 = AARR_DIMS(array1);
3837
0
  int      *dims2 = AARR_DIMS(array2);
3838
0
  int      *lbs1 = AARR_LBOUND(array1);
3839
0
  int      *lbs2 = AARR_LBOUND(array2);
3840
0
  Oid     element_type = AARR_ELEMTYPE(array1);
3841
0
  bool    result = true;
3842
0
  int     nitems;
3843
0
  TypeCacheEntry *typentry;
3844
0
  int     typlen;
3845
0
  bool    typbyval;
3846
0
  char    typalign;
3847
0
  array_iter  it1;
3848
0
  array_iter  it2;
3849
0
  int     i;
3850
3851
0
  if (element_type != AARR_ELEMTYPE(array2))
3852
0
    ereport(ERROR,
3853
0
        (errcode(ERRCODE_DATATYPE_MISMATCH),
3854
0
         errmsg("cannot compare arrays of different element types")));
3855
3856
  /* fast path if the arrays do not have the same dimensionality */
3857
0
  if (ndims1 != ndims2 ||
3858
0
    memcmp(dims1, dims2, ndims1 * sizeof(int)) != 0 ||
3859
0
    memcmp(lbs1, lbs2, ndims1 * sizeof(int)) != 0)
3860
0
    result = false;
3861
0
  else
3862
0
  {
3863
    /*
3864
     * We arrange to look up the equality function only once per series of
3865
     * calls, assuming the element type doesn't change underneath us.  The
3866
     * typcache is used so that we have no memory leakage when being used
3867
     * as an index support function.
3868
     */
3869
0
    typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
3870
0
    if (typentry == NULL ||
3871
0
      typentry->type_id != element_type)
3872
0
    {
3873
0
      typentry = lookup_type_cache(element_type,
3874
0
                     TYPECACHE_EQ_OPR_FINFO);
3875
0
      if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
3876
0
        ereport(ERROR,
3877
0
            (errcode(ERRCODE_UNDEFINED_FUNCTION),
3878
0
             errmsg("could not identify an equality operator for type %s",
3879
0
                format_type_be(element_type))));
3880
0
      fcinfo->flinfo->fn_extra = typentry;
3881
0
    }
3882
0
    typlen = typentry->typlen;
3883
0
    typbyval = typentry->typbyval;
3884
0
    typalign = typentry->typalign;
3885
3886
    /*
3887
     * apply the operator to each pair of array elements.
3888
     */
3889
0
    InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
3890
0
                 collation, NULL, NULL);
3891
3892
    /* Loop over source data */
3893
0
    nitems = ArrayGetNItems(ndims1, dims1);
3894
0
    array_iter_setup(&it1, array1, typlen, typbyval, typalign);
3895
0
    array_iter_setup(&it2, array2, typlen, typbyval, typalign);
3896
3897
0
    for (i = 0; i < nitems; i++)
3898
0
    {
3899
0
      Datum   elt1;
3900
0
      Datum   elt2;
3901
0
      bool    isnull1;
3902
0
      bool    isnull2;
3903
0
      bool    oprresult;
3904
3905
      /* Get elements, checking for NULL */
3906
0
      elt1 = array_iter_next(&it1, &isnull1, i);
3907
0
      elt2 = array_iter_next(&it2, &isnull2, i);
3908
3909
      /*
3910
       * We consider two NULLs equal; NULL and not-NULL are unequal.
3911
       */
3912
0
      if (isnull1 && isnull2)
3913
0
        continue;
3914
0
      if (isnull1 || isnull2)
3915
0
      {
3916
0
        result = false;
3917
0
        break;
3918
0
      }
3919
3920
      /*
3921
       * Apply the operator to the element pair; treat NULL as false
3922
       */
3923
0
      locfcinfo->args[0].value = elt1;
3924
0
      locfcinfo->args[0].isnull = false;
3925
0
      locfcinfo->args[1].value = elt2;
3926
0
      locfcinfo->args[1].isnull = false;
3927
0
      locfcinfo->isnull = false;
3928
0
      oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
3929
0
      if (locfcinfo->isnull || !oprresult)
3930
0
      {
3931
0
        result = false;
3932
0
        break;
3933
0
      }
3934
0
    }
3935
0
  }
3936
3937
  /* Avoid leaking memory when handed toasted input. */
3938
0
  AARR_FREE_IF_COPY(array1, 0);
3939
0
  AARR_FREE_IF_COPY(array2, 1);
3940
3941
0
  PG_RETURN_BOOL(result);
3942
0
}
3943
3944
3945
/*-----------------------------------------------------------------------------
3946
 * array-array bool operators:
3947
 *    Given two arrays, iterate comparison operators
3948
 *    over the array. Uses logic similar to text comparison
3949
 *    functions, except element-by-element instead of
3950
 *    character-by-character.
3951
 *----------------------------------------------------------------------------
3952
 */
3953
3954
Datum
3955
array_ne(PG_FUNCTION_ARGS)
3956
0
{
3957
0
  PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
3958
0
}
3959
3960
Datum
3961
array_lt(PG_FUNCTION_ARGS)
3962
0
{
3963
0
  PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
3964
0
}
3965
3966
Datum
3967
array_gt(PG_FUNCTION_ARGS)
3968
0
{
3969
0
  PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
3970
0
}
3971
3972
Datum
3973
array_le(PG_FUNCTION_ARGS)
3974
0
{
3975
0
  PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
3976
0
}
3977
3978
Datum
3979
array_ge(PG_FUNCTION_ARGS)
3980
0
{
3981
0
  PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
3982
0
}
3983
3984
Datum
3985
btarraycmp(PG_FUNCTION_ARGS)
3986
0
{
3987
0
  PG_RETURN_INT32(array_cmp(fcinfo));
3988
0
}
3989
3990
/*
3991
 * array_cmp()
3992
 * Internal comparison function for arrays.
3993
 *
3994
 * Returns -1, 0 or 1
3995
 */
3996
static int
3997
array_cmp(FunctionCallInfo fcinfo)
3998
0
{
3999
0
  LOCAL_FCINFO(locfcinfo, 2);
4000
0
  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4001
0
  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4002
0
  Oid     collation = PG_GET_COLLATION();
4003
0
  int     ndims1 = AARR_NDIM(array1);
4004
0
  int     ndims2 = AARR_NDIM(array2);
4005
0
  int      *dims1 = AARR_DIMS(array1);
4006
0
  int      *dims2 = AARR_DIMS(array2);
4007
0
  int     nitems1 = ArrayGetNItems(ndims1, dims1);
4008
0
  int     nitems2 = ArrayGetNItems(ndims2, dims2);
4009
0
  Oid     element_type = AARR_ELEMTYPE(array1);
4010
0
  int     result = 0;
4011
0
  TypeCacheEntry *typentry;
4012
0
  int     typlen;
4013
0
  bool    typbyval;
4014
0
  char    typalign;
4015
0
  int     min_nitems;
4016
0
  array_iter  it1;
4017
0
  array_iter  it2;
4018
0
  int     i;
4019
4020
0
  if (element_type != AARR_ELEMTYPE(array2))
4021
0
    ereport(ERROR,
4022
0
        (errcode(ERRCODE_DATATYPE_MISMATCH),
4023
0
         errmsg("cannot compare arrays of different element types")));
4024
4025
  /*
4026
   * We arrange to look up the comparison function only once per series of
4027
   * calls, assuming the element type doesn't change underneath us. The
4028
   * typcache is used so that we have no memory leakage when being used as
4029
   * an index support function.
4030
   */
4031
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4032
0
  if (typentry == NULL ||
4033
0
    typentry->type_id != element_type)
4034
0
  {
4035
0
    typentry = lookup_type_cache(element_type,
4036
0
                   TYPECACHE_CMP_PROC_FINFO);
4037
0
    if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
4038
0
      ereport(ERROR,
4039
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
4040
0
           errmsg("could not identify a comparison function for type %s",
4041
0
              format_type_be(element_type))));
4042
0
    fcinfo->flinfo->fn_extra = typentry;
4043
0
  }
4044
0
  typlen = typentry->typlen;
4045
0
  typbyval = typentry->typbyval;
4046
0
  typalign = typentry->typalign;
4047
4048
  /*
4049
   * apply the operator to each pair of array elements.
4050
   */
4051
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
4052
0
               collation, NULL, NULL);
4053
4054
  /* Loop over source data */
4055
0
  min_nitems = Min(nitems1, nitems2);
4056
0
  array_iter_setup(&it1, array1, typlen, typbyval, typalign);
4057
0
  array_iter_setup(&it2, array2, typlen, typbyval, typalign);
4058
4059
0
  for (i = 0; i < min_nitems; i++)
4060
0
  {
4061
0
    Datum   elt1;
4062
0
    Datum   elt2;
4063
0
    bool    isnull1;
4064
0
    bool    isnull2;
4065
0
    int32   cmpresult;
4066
4067
    /* Get elements, checking for NULL */
4068
0
    elt1 = array_iter_next(&it1, &isnull1, i);
4069
0
    elt2 = array_iter_next(&it2, &isnull2, i);
4070
4071
    /*
4072
     * We consider two NULLs equal; NULL > not-NULL.
4073
     */
4074
0
    if (isnull1 && isnull2)
4075
0
      continue;
4076
0
    if (isnull1)
4077
0
    {
4078
      /* arg1 is greater than arg2 */
4079
0
      result = 1;
4080
0
      break;
4081
0
    }
4082
0
    if (isnull2)
4083
0
    {
4084
      /* arg1 is less than arg2 */
4085
0
      result = -1;
4086
0
      break;
4087
0
    }
4088
4089
    /* Compare the pair of elements */
4090
0
    locfcinfo->args[0].value = elt1;
4091
0
    locfcinfo->args[0].isnull = false;
4092
0
    locfcinfo->args[1].value = elt2;
4093
0
    locfcinfo->args[1].isnull = false;
4094
0
    cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
4095
4096
    /* We don't expect comparison support functions to return null */
4097
0
    Assert(!locfcinfo->isnull);
4098
4099
0
    if (cmpresult == 0)
4100
0
      continue;     /* equal */
4101
4102
0
    if (cmpresult < 0)
4103
0
    {
4104
      /* arg1 is less than arg2 */
4105
0
      result = -1;
4106
0
      break;
4107
0
    }
4108
0
    else
4109
0
    {
4110
      /* arg1 is greater than arg2 */
4111
0
      result = 1;
4112
0
      break;
4113
0
    }
4114
0
  }
4115
4116
  /*
4117
   * If arrays contain same data (up to end of shorter one), apply
4118
   * additional rules to sort by dimensionality.  The relative significance
4119
   * of the different bits of information is historical; mainly we just care
4120
   * that we don't say "equal" for arrays of different dimensionality.
4121
   */
4122
0
  if (result == 0)
4123
0
  {
4124
0
    if (nitems1 != nitems2)
4125
0
      result = (nitems1 < nitems2) ? -1 : 1;
4126
0
    else if (ndims1 != ndims2)
4127
0
      result = (ndims1 < ndims2) ? -1 : 1;
4128
0
    else
4129
0
    {
4130
0
      for (i = 0; i < ndims1; i++)
4131
0
      {
4132
0
        if (dims1[i] != dims2[i])
4133
0
        {
4134
0
          result = (dims1[i] < dims2[i]) ? -1 : 1;
4135
0
          break;
4136
0
        }
4137
0
      }
4138
0
      if (result == 0)
4139
0
      {
4140
0
        int      *lbound1 = AARR_LBOUND(array1);
4141
0
        int      *lbound2 = AARR_LBOUND(array2);
4142
4143
0
        for (i = 0; i < ndims1; i++)
4144
0
        {
4145
0
          if (lbound1[i] != lbound2[i])
4146
0
          {
4147
0
            result = (lbound1[i] < lbound2[i]) ? -1 : 1;
4148
0
            break;
4149
0
          }
4150
0
        }
4151
0
      }
4152
0
    }
4153
0
  }
4154
4155
  /* Avoid leaking memory when handed toasted input. */
4156
0
  AARR_FREE_IF_COPY(array1, 0);
4157
0
  AARR_FREE_IF_COPY(array2, 1);
4158
4159
0
  return result;
4160
0
}
4161
4162
4163
/*-----------------------------------------------------------------------------
4164
 * array hashing
4165
 *    Hash the elements and combine the results.
4166
 *----------------------------------------------------------------------------
4167
 */
4168
4169
Datum
4170
hash_array(PG_FUNCTION_ARGS)
4171
0
{
4172
0
  LOCAL_FCINFO(locfcinfo, 1);
4173
0
  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4174
0
  int     ndims = AARR_NDIM(array);
4175
0
  int      *dims = AARR_DIMS(array);
4176
0
  Oid     element_type = AARR_ELEMTYPE(array);
4177
0
  uint32    result = 1;
4178
0
  int     nitems;
4179
0
  TypeCacheEntry *typentry;
4180
0
  int     typlen;
4181
0
  bool    typbyval;
4182
0
  char    typalign;
4183
0
  int     i;
4184
0
  array_iter  iter;
4185
4186
  /*
4187
   * We arrange to look up the hash function only once per series of calls,
4188
   * assuming the element type doesn't change underneath us.  The typcache
4189
   * is used so that we have no memory leakage when being used as an index
4190
   * support function.
4191
   */
4192
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4193
0
  if (typentry == NULL ||
4194
0
    typentry->type_id != element_type)
4195
0
  {
4196
0
    typentry = lookup_type_cache(element_type,
4197
0
                   TYPECACHE_HASH_PROC_FINFO);
4198
0
    if (!OidIsValid(typentry->hash_proc_finfo.fn_oid) && element_type != RECORDOID)
4199
0
      ereport(ERROR,
4200
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
4201
0
           errmsg("could not identify a hash function for type %s",
4202
0
              format_type_be(element_type))));
4203
4204
    /*
4205
     * The type cache doesn't believe that record is hashable (see
4206
     * cache_record_field_properties()), but since we're here, we're
4207
     * committed to hashing, so we can assume it does.  Worst case, if any
4208
     * components of the record don't support hashing, we will fail at
4209
     * execution.
4210
     */
4211
0
    if (element_type == RECORDOID)
4212
0
    {
4213
0
      MemoryContext oldcontext;
4214
0
      TypeCacheEntry *record_typentry;
4215
4216
0
      oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
4217
4218
      /*
4219
       * Make fake type cache entry structure.  Note that we can't just
4220
       * modify typentry, since that points directly into the type
4221
       * cache.
4222
       */
4223
0
      record_typentry = palloc0_object(TypeCacheEntry);
4224
0
      record_typentry->type_id = element_type;
4225
4226
      /* fill in what we need below */
4227
0
      record_typentry->typlen = typentry->typlen;
4228
0
      record_typentry->typbyval = typentry->typbyval;
4229
0
      record_typentry->typalign = typentry->typalign;
4230
0
      fmgr_info(F_HASH_RECORD, &record_typentry->hash_proc_finfo);
4231
4232
0
      MemoryContextSwitchTo(oldcontext);
4233
4234
0
      typentry = record_typentry;
4235
0
    }
4236
4237
0
    fcinfo->flinfo->fn_extra = typentry;
4238
0
  }
4239
4240
0
  typlen = typentry->typlen;
4241
0
  typbyval = typentry->typbyval;
4242
0
  typalign = typentry->typalign;
4243
4244
  /*
4245
   * apply the hash function to each array element.
4246
   */
4247
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_proc_finfo, 1,
4248
0
               PG_GET_COLLATION(), NULL, NULL);
4249
4250
  /* Loop over source data */
4251
0
  nitems = ArrayGetNItems(ndims, dims);
4252
0
  array_iter_setup(&iter, array, typlen, typbyval, typalign);
4253
4254
0
  for (i = 0; i < nitems; i++)
4255
0
  {
4256
0
    Datum   elt;
4257
0
    bool    isnull;
4258
0
    uint32    elthash;
4259
4260
    /* Get element, checking for NULL */
4261
0
    elt = array_iter_next(&iter, &isnull, i);
4262
4263
0
    if (isnull)
4264
0
    {
4265
      /* Treat nulls as having hashvalue 0 */
4266
0
      elthash = 0;
4267
0
    }
4268
0
    else
4269
0
    {
4270
      /* Apply the hash function */
4271
0
      locfcinfo->args[0].value = elt;
4272
0
      locfcinfo->args[0].isnull = false;
4273
0
      elthash = DatumGetUInt32(FunctionCallInvoke(locfcinfo));
4274
      /* We don't expect hash functions to return null */
4275
0
      Assert(!locfcinfo->isnull);
4276
0
    }
4277
4278
    /*
4279
     * Combine hash values of successive elements by multiplying the
4280
     * current value by 31 and adding on the new element's hash value.
4281
     *
4282
     * The result is a sum in which each element's hash value is
4283
     * multiplied by a different power of 31. This is modulo 2^32
4284
     * arithmetic, and the powers of 31 modulo 2^32 form a cyclic group of
4285
     * order 2^27. So for arrays of up to 2^27 elements, each element's
4286
     * hash value is multiplied by a different (odd) number, resulting in
4287
     * a good mixing of all the elements' hash values.
4288
     */
4289
0
    result = (result << 5) - result + elthash;
4290
0
  }
4291
4292
  /* Avoid leaking memory when handed toasted input. */
4293
0
  AARR_FREE_IF_COPY(array, 0);
4294
4295
0
  PG_RETURN_UINT32(result);
4296
0
}
4297
4298
/*
4299
 * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
4300
 * Otherwise, similar to hash_array.
4301
 */
4302
Datum
4303
hash_array_extended(PG_FUNCTION_ARGS)
4304
0
{
4305
0
  LOCAL_FCINFO(locfcinfo, 2);
4306
0
  AnyArrayType *array = PG_GETARG_ANY_ARRAY_P(0);
4307
0
  uint64    seed = PG_GETARG_INT64(1);
4308
0
  int     ndims = AARR_NDIM(array);
4309
0
  int      *dims = AARR_DIMS(array);
4310
0
  Oid     element_type = AARR_ELEMTYPE(array);
4311
0
  uint64    result = 1;
4312
0
  int     nitems;
4313
0
  TypeCacheEntry *typentry;
4314
0
  int     typlen;
4315
0
  bool    typbyval;
4316
0
  char    typalign;
4317
0
  int     i;
4318
0
  array_iter  iter;
4319
4320
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4321
0
  if (typentry == NULL ||
4322
0
    typentry->type_id != element_type)
4323
0
  {
4324
0
    typentry = lookup_type_cache(element_type,
4325
0
                   TYPECACHE_HASH_EXTENDED_PROC_FINFO);
4326
0
    if (!OidIsValid(typentry->hash_extended_proc_finfo.fn_oid))
4327
0
      ereport(ERROR,
4328
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
4329
0
           errmsg("could not identify an extended hash function for type %s",
4330
0
              format_type_be(element_type))));
4331
0
    fcinfo->flinfo->fn_extra = typentry;
4332
0
  }
4333
0
  typlen = typentry->typlen;
4334
0
  typbyval = typentry->typbyval;
4335
0
  typalign = typentry->typalign;
4336
4337
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->hash_extended_proc_finfo, 2,
4338
0
               PG_GET_COLLATION(), NULL, NULL);
4339
4340
  /* Loop over source data */
4341
0
  nitems = ArrayGetNItems(ndims, dims);
4342
0
  array_iter_setup(&iter, array, typlen, typbyval, typalign);
4343
4344
0
  for (i = 0; i < nitems; i++)
4345
0
  {
4346
0
    Datum   elt;
4347
0
    bool    isnull;
4348
0
    uint64    elthash;
4349
4350
    /* Get element, checking for NULL */
4351
0
    elt = array_iter_next(&iter, &isnull, i);
4352
4353
0
    if (isnull)
4354
0
    {
4355
0
      elthash = 0;
4356
0
    }
4357
0
    else
4358
0
    {
4359
      /* Apply the hash function */
4360
0
      locfcinfo->args[0].value = elt;
4361
0
      locfcinfo->args[0].isnull = false;
4362
0
      locfcinfo->args[1].value = Int64GetDatum(seed);
4363
0
      locfcinfo->args[1].isnull = false;
4364
0
      elthash = DatumGetUInt64(FunctionCallInvoke(locfcinfo));
4365
      /* We don't expect hash functions to return null */
4366
0
      Assert(!locfcinfo->isnull);
4367
0
    }
4368
4369
0
    result = (result << 5) - result + elthash;
4370
0
  }
4371
4372
0
  AARR_FREE_IF_COPY(array, 0);
4373
4374
0
  PG_RETURN_UINT64(result);
4375
0
}
4376
4377
4378
/*-----------------------------------------------------------------------------
4379
 * array overlap/containment comparisons
4380
 *    These use the same methods of comparing array elements as array_eq.
4381
 *    We consider only the elements of the arrays, ignoring dimensionality.
4382
 *----------------------------------------------------------------------------
4383
 */
4384
4385
/*
4386
 * array_contain_compare :
4387
 *      compares two arrays for overlap/containment
4388
 *
4389
 * When matchall is true, return true if all members of array1 are in array2.
4390
 * When matchall is false, return true if any members of array1 are in array2.
4391
 */
4392
static bool
4393
array_contain_compare(AnyArrayType *array1, AnyArrayType *array2, Oid collation,
4394
            bool matchall, void **fn_extra)
4395
0
{
4396
0
  LOCAL_FCINFO(locfcinfo, 2);
4397
0
  bool    result = matchall;
4398
0
  Oid     element_type = AARR_ELEMTYPE(array1);
4399
0
  TypeCacheEntry *typentry;
4400
0
  int     nelems1;
4401
0
  Datum    *values2;
4402
0
  bool     *nulls2;
4403
0
  int     nelems2;
4404
0
  int     typlen;
4405
0
  bool    typbyval;
4406
0
  char    typalign;
4407
0
  int     i;
4408
0
  int     j;
4409
0
  array_iter  it1;
4410
4411
0
  if (element_type != AARR_ELEMTYPE(array2))
4412
0
    ereport(ERROR,
4413
0
        (errcode(ERRCODE_DATATYPE_MISMATCH),
4414
0
         errmsg("cannot compare arrays of different element types")));
4415
4416
  /*
4417
   * We arrange to look up the equality function only once per series of
4418
   * calls, assuming the element type doesn't change underneath us.  The
4419
   * typcache is used so that we have no memory leakage when being used as
4420
   * an index support function.
4421
   */
4422
0
  typentry = (TypeCacheEntry *) *fn_extra;
4423
0
  if (typentry == NULL ||
4424
0
    typentry->type_id != element_type)
4425
0
  {
4426
0
    typentry = lookup_type_cache(element_type,
4427
0
                   TYPECACHE_EQ_OPR_FINFO);
4428
0
    if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
4429
0
      ereport(ERROR,
4430
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
4431
0
           errmsg("could not identify an equality operator for type %s",
4432
0
              format_type_be(element_type))));
4433
0
    *fn_extra = typentry;
4434
0
  }
4435
0
  typlen = typentry->typlen;
4436
0
  typbyval = typentry->typbyval;
4437
0
  typalign = typentry->typalign;
4438
4439
  /*
4440
   * Since we probably will need to scan array2 multiple times, it's
4441
   * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
4442
   * however, since we very likely won't need to look at all of it.
4443
   */
4444
0
  if (VARATT_IS_EXPANDED_HEADER(array2))
4445
0
  {
4446
    /* This should be safe even if input is read-only */
4447
0
    deconstruct_expanded_array(&(array2->xpn));
4448
0
    values2 = array2->xpn.dvalues;
4449
0
    nulls2 = array2->xpn.dnulls;
4450
0
    nelems2 = array2->xpn.nelems;
4451
0
  }
4452
0
  else
4453
0
    deconstruct_array((ArrayType *) array2,
4454
0
              element_type, typlen, typbyval, typalign,
4455
0
              &values2, &nulls2, &nelems2);
4456
4457
  /*
4458
   * Apply the comparison operator to each pair of array elements.
4459
   */
4460
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
4461
0
               collation, NULL, NULL);
4462
4463
  /* Loop over source data */
4464
0
  nelems1 = ArrayGetNItems(AARR_NDIM(array1), AARR_DIMS(array1));
4465
0
  array_iter_setup(&it1, array1, typlen, typbyval, typalign);
4466
4467
0
  for (i = 0; i < nelems1; i++)
4468
0
  {
4469
0
    Datum   elt1;
4470
0
    bool    isnull1;
4471
4472
    /* Get element, checking for NULL */
4473
0
    elt1 = array_iter_next(&it1, &isnull1, i);
4474
4475
    /*
4476
     * We assume that the comparison operator is strict, so a NULL can't
4477
     * match anything.  XXX this diverges from the "NULL=NULL" behavior of
4478
     * array_eq, should we act like that?
4479
     */
4480
0
    if (isnull1)
4481
0
    {
4482
0
      if (matchall)
4483
0
      {
4484
0
        result = false;
4485
0
        break;
4486
0
      }
4487
0
      continue;
4488
0
    }
4489
4490
0
    for (j = 0; j < nelems2; j++)
4491
0
    {
4492
0
      Datum   elt2 = values2[j];
4493
0
      bool    isnull2 = nulls2 ? nulls2[j] : false;
4494
0
      bool    oprresult;
4495
4496
0
      if (isnull2)
4497
0
        continue;   /* can't match */
4498
4499
      /*
4500
       * Apply the operator to the element pair; treat NULL as false
4501
       */
4502
0
      locfcinfo->args[0].value = elt1;
4503
0
      locfcinfo->args[0].isnull = false;
4504
0
      locfcinfo->args[1].value = elt2;
4505
0
      locfcinfo->args[1].isnull = false;
4506
0
      locfcinfo->isnull = false;
4507
0
      oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
4508
0
      if (!locfcinfo->isnull && oprresult)
4509
0
        break;
4510
0
    }
4511
4512
0
    if (j < nelems2)
4513
0
    {
4514
      /* found a match for elt1 */
4515
0
      if (!matchall)
4516
0
      {
4517
0
        result = true;
4518
0
        break;
4519
0
      }
4520
0
    }
4521
0
    else
4522
0
    {
4523
      /* no match for elt1 */
4524
0
      if (matchall)
4525
0
      {
4526
0
        result = false;
4527
0
        break;
4528
0
      }
4529
0
    }
4530
0
  }
4531
4532
0
  return result;
4533
0
}
4534
4535
Datum
4536
arrayoverlap(PG_FUNCTION_ARGS)
4537
0
{
4538
0
  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4539
0
  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4540
0
  Oid     collation = PG_GET_COLLATION();
4541
0
  bool    result;
4542
4543
0
  result = array_contain_compare(array1, array2, collation, false,
4544
0
                   &fcinfo->flinfo->fn_extra);
4545
4546
  /* Avoid leaking memory when handed toasted input. */
4547
0
  AARR_FREE_IF_COPY(array1, 0);
4548
0
  AARR_FREE_IF_COPY(array2, 1);
4549
4550
0
  PG_RETURN_BOOL(result);
4551
0
}
4552
4553
Datum
4554
arraycontains(PG_FUNCTION_ARGS)
4555
0
{
4556
0
  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4557
0
  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4558
0
  Oid     collation = PG_GET_COLLATION();
4559
0
  bool    result;
4560
4561
0
  result = array_contain_compare(array2, array1, collation, true,
4562
0
                   &fcinfo->flinfo->fn_extra);
4563
4564
  /* Avoid leaking memory when handed toasted input. */
4565
0
  AARR_FREE_IF_COPY(array1, 0);
4566
0
  AARR_FREE_IF_COPY(array2, 1);
4567
4568
0
  PG_RETURN_BOOL(result);
4569
0
}
4570
4571
Datum
4572
arraycontained(PG_FUNCTION_ARGS)
4573
0
{
4574
0
  AnyArrayType *array1 = PG_GETARG_ANY_ARRAY_P(0);
4575
0
  AnyArrayType *array2 = PG_GETARG_ANY_ARRAY_P(1);
4576
0
  Oid     collation = PG_GET_COLLATION();
4577
0
  bool    result;
4578
4579
0
  result = array_contain_compare(array1, array2, collation, true,
4580
0
                   &fcinfo->flinfo->fn_extra);
4581
4582
  /* Avoid leaking memory when handed toasted input. */
4583
0
  AARR_FREE_IF_COPY(array1, 0);
4584
0
  AARR_FREE_IF_COPY(array2, 1);
4585
4586
0
  PG_RETURN_BOOL(result);
4587
0
}
4588
4589
4590
/*-----------------------------------------------------------------------------
4591
 * Array iteration functions
4592
 *    These functions are used to iterate efficiently through arrays
4593
 *-----------------------------------------------------------------------------
4594
 */
4595
4596
/*
4597
 * array_create_iterator --- set up to iterate through an array
4598
 *
4599
 * If slice_ndim is zero, we will iterate element-by-element; the returned
4600
 * datums are of the array's element type.
4601
 *
4602
 * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
4603
 * returned datums are of the same array type as 'arr', but of size
4604
 * equal to the rightmost N dimensions of 'arr'.
4605
 *
4606
 * The passed-in array must remain valid for the lifetime of the iterator.
4607
 */
4608
ArrayIterator
4609
array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
4610
0
{
4611
0
  ArrayIterator iterator = palloc0_object(ArrayIteratorData);
4612
4613
  /*
4614
   * Sanity-check inputs --- caller should have got this right already
4615
   */
4616
0
  Assert(arr);
4617
0
  if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
4618
0
    elog(ERROR, "invalid arguments to array_create_iterator");
4619
4620
  /*
4621
   * Remember basic info about the array and its element type
4622
   */
4623
0
  iterator->arr = arr;
4624
0
  iterator->nullbitmap = ARR_NULLBITMAP(arr);
4625
0
  iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
4626
4627
0
  if (mstate != NULL)
4628
0
  {
4629
0
    Assert(mstate->element_type == ARR_ELEMTYPE(arr));
4630
4631
0
    iterator->typlen = mstate->typlen;
4632
0
    iterator->typbyval = mstate->typbyval;
4633
0
    iterator->typalign = mstate->typalign;
4634
0
  }
4635
0
  else
4636
0
    get_typlenbyvalalign(ARR_ELEMTYPE(arr),
4637
0
               &iterator->typlen,
4638
0
               &iterator->typbyval,
4639
0
               &iterator->typalign);
4640
0
  iterator->typalignby = typalign_to_alignby(iterator->typalign);
4641
4642
  /*
4643
   * Remember the slicing parameters.
4644
   */
4645
0
  iterator->slice_ndim = slice_ndim;
4646
4647
0
  if (slice_ndim > 0)
4648
0
  {
4649
    /*
4650
     * Get pointers into the array's dims and lbound arrays to represent
4651
     * the dims/lbound arrays of a slice.  These are the same as the
4652
     * rightmost N dimensions of the array.
4653
     */
4654
0
    iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
4655
0
    iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
4656
4657
    /*
4658
     * Compute number of elements in a slice.
4659
     */
4660
0
    iterator->slice_len = ArrayGetNItems(slice_ndim,
4661
0
                       iterator->slice_dims);
4662
4663
    /*
4664
     * Create workspace for building sub-arrays.
4665
     */
4666
0
    iterator->slice_values = (Datum *)
4667
0
      palloc(iterator->slice_len * sizeof(Datum));
4668
0
    iterator->slice_nulls = (bool *)
4669
0
      palloc(iterator->slice_len * sizeof(bool));
4670
0
  }
4671
4672
  /*
4673
   * Initialize our data pointer and linear element number.  These will
4674
   * advance through the array during array_iterate().
4675
   */
4676
0
  iterator->data_ptr = ARR_DATA_PTR(arr);
4677
0
  iterator->current_item = 0;
4678
4679
0
  return iterator;
4680
0
}
4681
4682
/*
4683
 * Iterate through the array referenced by 'iterator'.
4684
 *
4685
 * As long as there is another element (or slice), return it into
4686
 * *value / *isnull, and return true.  Return false when no more data.
4687
 */
4688
bool
4689
array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
4690
0
{
4691
  /* Done if we have reached the end of the array */
4692
0
  if (iterator->current_item >= iterator->nitems)
4693
0
    return false;
4694
4695
0
  if (iterator->slice_ndim == 0)
4696
0
  {
4697
    /*
4698
     * Scalar case: return one element.
4699
     */
4700
0
    if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
4701
0
    {
4702
0
      *isnull = true;
4703
0
      *value = (Datum) 0;
4704
0
    }
4705
0
    else
4706
0
    {
4707
      /* non-NULL, so fetch the individual Datum to return */
4708
0
      char     *p = iterator->data_ptr;
4709
4710
0
      *isnull = false;
4711
0
      *value = fetch_att(p, iterator->typbyval, iterator->typlen);
4712
4713
      /* Move our data pointer forward to the next element */
4714
0
      p = att_addlength_pointer(p, iterator->typlen, p);
4715
0
      p = (char *) att_nominal_alignby(p, iterator->typalignby);
4716
0
      iterator->data_ptr = p;
4717
0
    }
4718
0
  }
4719
0
  else
4720
0
  {
4721
    /*
4722
     * Slice case: build and return an array of the requested size.
4723
     */
4724
0
    ArrayType  *result;
4725
0
    Datum    *values = iterator->slice_values;
4726
0
    bool     *nulls = iterator->slice_nulls;
4727
0
    char     *p = iterator->data_ptr;
4728
0
    int     i;
4729
4730
0
    for (i = 0; i < iterator->slice_len; i++)
4731
0
    {
4732
0
      if (array_get_isnull(iterator->nullbitmap,
4733
0
                 iterator->current_item++))
4734
0
      {
4735
0
        nulls[i] = true;
4736
0
        values[i] = (Datum) 0;
4737
0
      }
4738
0
      else
4739
0
      {
4740
0
        nulls[i] = false;
4741
0
        values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
4742
4743
        /* Move our data pointer forward to the next element */
4744
0
        p = att_addlength_pointer(p, iterator->typlen, p);
4745
0
        p = (char *) att_nominal_alignby(p, iterator->typalignby);
4746
0
      }
4747
0
    }
4748
4749
0
    iterator->data_ptr = p;
4750
4751
0
    result = construct_md_array(values,
4752
0
                  nulls,
4753
0
                  iterator->slice_ndim,
4754
0
                  iterator->slice_dims,
4755
0
                  iterator->slice_lbound,
4756
0
                  ARR_ELEMTYPE(iterator->arr),
4757
0
                  iterator->typlen,
4758
0
                  iterator->typbyval,
4759
0
                  iterator->typalign);
4760
4761
0
    *isnull = false;
4762
0
    *value = PointerGetDatum(result);
4763
0
  }
4764
4765
0
  return true;
4766
0
}
4767
4768
/*
4769
 * Release an ArrayIterator data structure
4770
 */
4771
void
4772
array_free_iterator(ArrayIterator iterator)
4773
0
{
4774
0
  if (iterator->slice_ndim > 0)
4775
0
  {
4776
0
    pfree(iterator->slice_values);
4777
0
    pfree(iterator->slice_nulls);
4778
0
  }
4779
0
  pfree(iterator);
4780
0
}
4781
4782
4783
/***************************************************************************/
4784
/******************|      Support  Routines       |*****************/
4785
/***************************************************************************/
4786
4787
/*
4788
 * Check whether a specific array element is NULL
4789
 *
4790
 * nullbitmap: pointer to array's null bitmap (NULL if none)
4791
 * offset: 0-based linear element number of array element
4792
 */
4793
static bool
4794
array_get_isnull(const uint8 *nullbitmap, int offset)
4795
0
{
4796
0
  if (nullbitmap == NULL)
4797
0
    return false;     /* assume not null */
4798
0
  if (nullbitmap[offset / 8] & (1 << (offset % 8)))
4799
0
    return false;     /* not null */
4800
0
  return true;
4801
0
}
4802
4803
/*
4804
 * Set a specific array element's null-bitmap entry
4805
 *
4806
 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
4807
 * offset: 0-based linear element number of array element
4808
 * isNull: null status to set
4809
 */
4810
static void
4811
array_set_isnull(uint8 *nullbitmap, int offset, bool isNull)
4812
0
{
4813
0
  int     bitmask;
4814
4815
0
  nullbitmap += offset / 8;
4816
0
  bitmask = 1 << (offset % 8);
4817
0
  if (isNull)
4818
0
    *nullbitmap &= ~bitmask;
4819
0
  else
4820
0
    *nullbitmap |= bitmask;
4821
0
}
4822
4823
/*
4824
 * Fetch array element at pointer, converted correctly to a Datum
4825
 *
4826
 * Caller must have handled case of NULL element
4827
 */
4828
static Datum
4829
ArrayCast(char *value, bool byval, int len)
4830
0
{
4831
0
  return fetch_att(value, byval, len);
4832
0
}
4833
4834
/*
4835
 * Copy datum to *dest and return total space used (including align padding)
4836
 *
4837
 * Caller must have handled case of NULL element
4838
 */
4839
static int
4840
ArrayCastAndSet(Datum src,
4841
        int typlen,
4842
        bool typbyval,
4843
        uint8 typalignby,
4844
        char *dest)
4845
0
{
4846
0
  int     inc;
4847
4848
0
  if (typlen > 0)
4849
0
  {
4850
0
    if (typbyval)
4851
0
      store_att_byval(dest, src, typlen);
4852
0
    else
4853
0
      memmove(dest, DatumGetPointer(src), typlen);
4854
0
    inc = att_nominal_alignby(typlen, typalignby);
4855
0
  }
4856
0
  else
4857
0
  {
4858
0
    Assert(!typbyval);
4859
0
    inc = att_addlength_datum(0, typlen, src);
4860
0
    memmove(dest, DatumGetPointer(src), inc);
4861
0
    inc = att_nominal_alignby(inc, typalignby);
4862
0
  }
4863
4864
0
  return inc;
4865
0
}
4866
4867
/*
4868
 * Advance ptr over nitems array elements
4869
 *
4870
 * ptr: starting location in array
4871
 * offset: 0-based linear element number of first element (the one at *ptr)
4872
 * nullbitmap: start of array's null bitmap, or NULL if none
4873
 * nitems: number of array elements to advance over (>= 0)
4874
 * typlen, typbyval, typalign: storage parameters of array element datatype
4875
 *
4876
 * It is caller's responsibility to ensure that nitems is within range
4877
 */
4878
static char *
4879
array_seek(char *ptr, int offset, uint8 *nullbitmap, int nitems,
4880
       int typlen, bool typbyval, char typalign)
4881
0
{
4882
0
  uint8   typalignby = typalign_to_alignby(typalign);
4883
0
  int     bitmask;
4884
0
  int     i;
4885
4886
  /* easy if fixed-size elements and no NULLs */
4887
0
  if (typlen > 0 && !nullbitmap)
4888
0
    return ptr + nitems * ((Size) att_nominal_alignby(typlen, typalignby));
4889
4890
  /* seems worth having separate loops for NULL and no-NULLs cases */
4891
0
  if (nullbitmap)
4892
0
  {
4893
0
    nullbitmap += offset / 8;
4894
0
    bitmask = 1 << (offset % 8);
4895
4896
0
    for (i = 0; i < nitems; i++)
4897
0
    {
4898
0
      if (*nullbitmap & bitmask)
4899
0
      {
4900
0
        ptr = att_addlength_pointer(ptr, typlen, ptr);
4901
0
        ptr = (char *) att_nominal_alignby(ptr, typalignby);
4902
0
      }
4903
0
      bitmask <<= 1;
4904
0
      if (bitmask == 0x100)
4905
0
      {
4906
0
        nullbitmap++;
4907
0
        bitmask = 1;
4908
0
      }
4909
0
    }
4910
0
  }
4911
0
  else
4912
0
  {
4913
0
    for (i = 0; i < nitems; i++)
4914
0
    {
4915
0
      ptr = att_addlength_pointer(ptr, typlen, ptr);
4916
0
      ptr = (char *) att_nominal_alignby(ptr, typalignby);
4917
0
    }
4918
0
  }
4919
0
  return ptr;
4920
0
}
4921
4922
/*
4923
 * Compute total size of the nitems array elements starting at *ptr
4924
 *
4925
 * Parameters same as for array_seek
4926
 */
4927
static int
4928
array_nelems_size(char *ptr, int offset, uint8 *nullbitmap, int nitems,
4929
          int typlen, bool typbyval, char typalign)
4930
0
{
4931
0
  return array_seek(ptr, offset, nullbitmap, nitems,
4932
0
            typlen, typbyval, typalign) - ptr;
4933
0
}
4934
4935
/*
4936
 * Copy nitems array elements from srcptr to destptr
4937
 *
4938
 * destptr: starting destination location (must be enough room!)
4939
 * nitems: number of array elements to copy (>= 0)
4940
 * srcptr: starting location in source array
4941
 * offset: 0-based linear element number of first element (the one at *srcptr)
4942
 * nullbitmap: start of source array's null bitmap, or NULL if none
4943
 * typlen, typbyval, typalign: storage parameters of array element datatype
4944
 *
4945
 * Returns number of bytes copied
4946
 *
4947
 * NB: this does not take care of setting up the destination's null bitmap!
4948
 */
4949
static int
4950
array_copy(char *destptr, int nitems,
4951
       char *srcptr, int offset, uint8 *nullbitmap,
4952
       int typlen, bool typbyval, char typalign)
4953
0
{
4954
0
  int     numbytes;
4955
4956
0
  numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
4957
0
                 typlen, typbyval, typalign);
4958
0
  memcpy(destptr, srcptr, numbytes);
4959
0
  return numbytes;
4960
0
}
4961
4962
/*
4963
 * Copy nitems null-bitmap bits from source to destination
4964
 *
4965
 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
4966
 * destoffset: 0-based linear element number of first dest element
4967
 * srcbitmap: start of source array's null bitmap, or NULL if none
4968
 * srcoffset: 0-based linear element number of first source element
4969
 * nitems: number of bits to copy (>= 0)
4970
 *
4971
 * If srcbitmap is NULL then we assume the source is all-non-NULL and
4972
 * fill 1's into the destination bitmap.  Note that only the specified
4973
 * bits in the destination map are changed, not any before or after.
4974
 *
4975
 * Note: this could certainly be optimized using standard bitblt methods.
4976
 * However, it's not clear that the typical Postgres array has enough elements
4977
 * to make it worth worrying too much.  For the moment, KISS.
4978
 */
4979
void
4980
array_bitmap_copy(uint8 *destbitmap, int destoffset,
4981
          const uint8 *srcbitmap, int srcoffset,
4982
          int nitems)
4983
0
{
4984
0
  int     destbitmask,
4985
0
        destbitval,
4986
0
        srcbitmask,
4987
0
        srcbitval;
4988
4989
0
  Assert(destbitmap);
4990
0
  if (nitems <= 0)
4991
0
    return;         /* don't risk fetch off end of memory */
4992
0
  destbitmap += destoffset / 8;
4993
0
  destbitmask = 1 << (destoffset % 8);
4994
0
  destbitval = *destbitmap;
4995
0
  if (srcbitmap)
4996
0
  {
4997
0
    srcbitmap += srcoffset / 8;
4998
0
    srcbitmask = 1 << (srcoffset % 8);
4999
0
    srcbitval = *srcbitmap;
5000
0
    while (nitems-- > 0)
5001
0
    {
5002
0
      if (srcbitval & srcbitmask)
5003
0
        destbitval |= destbitmask;
5004
0
      else
5005
0
        destbitval &= ~destbitmask;
5006
0
      destbitmask <<= 1;
5007
0
      if (destbitmask == 0x100)
5008
0
      {
5009
0
        *destbitmap++ = destbitval;
5010
0
        destbitmask = 1;
5011
0
        if (nitems > 0)
5012
0
          destbitval = *destbitmap;
5013
0
      }
5014
0
      srcbitmask <<= 1;
5015
0
      if (srcbitmask == 0x100)
5016
0
      {
5017
0
        srcbitmap++;
5018
0
        srcbitmask = 1;
5019
0
        if (nitems > 0)
5020
0
          srcbitval = *srcbitmap;
5021
0
      }
5022
0
    }
5023
0
    if (destbitmask != 1)
5024
0
      *destbitmap = destbitval;
5025
0
  }
5026
0
  else
5027
0
  {
5028
0
    while (nitems-- > 0)
5029
0
    {
5030
0
      destbitval |= destbitmask;
5031
0
      destbitmask <<= 1;
5032
0
      if (destbitmask == 0x100)
5033
0
      {
5034
0
        *destbitmap++ = destbitval;
5035
0
        destbitmask = 1;
5036
0
        if (nitems > 0)
5037
0
          destbitval = *destbitmap;
5038
0
      }
5039
0
    }
5040
0
    if (destbitmask != 1)
5041
0
      *destbitmap = destbitval;
5042
0
  }
5043
0
}
5044
5045
/*
5046
 * Compute space needed for a slice of an array
5047
 *
5048
 * We assume the caller has verified that the slice coordinates are valid.
5049
 */
5050
static int
5051
array_slice_size(char *arraydataptr, uint8 *arraynullsptr,
5052
         int ndim, int *dim, int *lb,
5053
         int *st, int *endp,
5054
         int typlen, bool typbyval, char typalign)
5055
0
{
5056
0
  int     src_offset,
5057
0
        span[MAXDIM],
5058
0
        prod[MAXDIM],
5059
0
        dist[MAXDIM],
5060
0
        indx[MAXDIM];
5061
0
  char     *ptr;
5062
0
  int     i,
5063
0
        j,
5064
0
        inc;
5065
0
  int     count = 0;
5066
0
  uint8   typalignby = typalign_to_alignby(typalign);
5067
5068
0
  mda_get_range(ndim, span, st, endp);
5069
5070
  /* Pretty easy for fixed element length without nulls ... */
5071
0
  if (typlen > 0 && !arraynullsptr)
5072
0
    return ArrayGetNItems(ndim, span) * att_nominal_alignby(typlen, typalignby);
5073
5074
  /* Else gotta do it the hard way */
5075
0
  src_offset = ArrayGetOffset(ndim, dim, lb, st);
5076
0
  ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5077
0
           typlen, typbyval, typalign);
5078
0
  mda_get_prod(ndim, dim, prod);
5079
0
  mda_get_offset_values(ndim, dist, prod, span);
5080
0
  for (i = 0; i < ndim; i++)
5081
0
    indx[i] = 0;
5082
0
  j = ndim - 1;
5083
0
  do
5084
0
  {
5085
0
    if (dist[j])
5086
0
    {
5087
0
      ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
5088
0
               typlen, typbyval, typalign);
5089
0
      src_offset += dist[j];
5090
0
    }
5091
0
    if (!array_get_isnull(arraynullsptr, src_offset))
5092
0
    {
5093
0
      inc = att_addlength_pointer(0, typlen, ptr);
5094
0
      inc = att_nominal_alignby(inc, typalignby);
5095
0
      ptr += inc;
5096
0
      count += inc;
5097
0
    }
5098
0
    src_offset++;
5099
0
  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5100
0
  return count;
5101
0
}
5102
5103
/*
5104
 * Extract a slice of an array into consecutive elements in the destination
5105
 * array.
5106
 *
5107
 * We assume the caller has verified that the slice coordinates are valid,
5108
 * allocated enough storage for the result, and initialized the header
5109
 * of the new array.
5110
 */
5111
static void
5112
array_extract_slice(ArrayType *newarray,
5113
          int ndim,
5114
          int *dim,
5115
          int *lb,
5116
          char *arraydataptr,
5117
          uint8 *arraynullsptr,
5118
          int *st,
5119
          int *endp,
5120
          int typlen,
5121
          bool typbyval,
5122
          char typalign)
5123
0
{
5124
0
  char     *destdataptr = ARR_DATA_PTR(newarray);
5125
0
  uint8    *destnullsptr = ARR_NULLBITMAP(newarray);
5126
0
  char     *srcdataptr;
5127
0
  int     src_offset,
5128
0
        dest_offset,
5129
0
        prod[MAXDIM],
5130
0
        span[MAXDIM],
5131
0
        dist[MAXDIM],
5132
0
        indx[MAXDIM];
5133
0
  int     i,
5134
0
        j,
5135
0
        inc;
5136
5137
0
  src_offset = ArrayGetOffset(ndim, dim, lb, st);
5138
0
  srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
5139
0
              typlen, typbyval, typalign);
5140
0
  mda_get_prod(ndim, dim, prod);
5141
0
  mda_get_range(ndim, span, st, endp);
5142
0
  mda_get_offset_values(ndim, dist, prod, span);
5143
0
  for (i = 0; i < ndim; i++)
5144
0
    indx[i] = 0;
5145
0
  dest_offset = 0;
5146
0
  j = ndim - 1;
5147
0
  do
5148
0
  {
5149
0
    if (dist[j])
5150
0
    {
5151
      /* skip unwanted elements */
5152
0
      srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
5153
0
                  dist[j],
5154
0
                  typlen, typbyval, typalign);
5155
0
      src_offset += dist[j];
5156
0
    }
5157
0
    inc = array_copy(destdataptr, 1,
5158
0
             srcdataptr, src_offset, arraynullsptr,
5159
0
             typlen, typbyval, typalign);
5160
0
    if (destnullsptr)
5161
0
      array_bitmap_copy(destnullsptr, dest_offset,
5162
0
                arraynullsptr, src_offset,
5163
0
                1);
5164
0
    destdataptr += inc;
5165
0
    srcdataptr += inc;
5166
0
    src_offset++;
5167
0
    dest_offset++;
5168
0
  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5169
0
}
5170
5171
/*
5172
 * Insert a slice into an array.
5173
 *
5174
 * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
5175
 * those same dimensions is to be constructed.  destArray must already
5176
 * have been allocated and its header initialized.
5177
 *
5178
 * st[]/endp[] identify the slice to be replaced.  Elements within the slice
5179
 * volume are taken from consecutive elements of the srcArray; elements
5180
 * outside it are copied from origArray.
5181
 *
5182
 * We assume the caller has verified that the slice coordinates are valid.
5183
 */
5184
static void
5185
array_insert_slice(ArrayType *destArray,
5186
           ArrayType *origArray,
5187
           ArrayType *srcArray,
5188
           int ndim,
5189
           int *dim,
5190
           int *lb,
5191
           int *st,
5192
           int *endp,
5193
           int typlen,
5194
           bool typbyval,
5195
           char typalign)
5196
0
{
5197
0
  char     *destPtr = ARR_DATA_PTR(destArray);
5198
0
  char     *origPtr = ARR_DATA_PTR(origArray);
5199
0
  char     *srcPtr = ARR_DATA_PTR(srcArray);
5200
0
  uint8    *destBitmap = ARR_NULLBITMAP(destArray);
5201
0
  uint8    *origBitmap = ARR_NULLBITMAP(origArray);
5202
0
  uint8    *srcBitmap = ARR_NULLBITMAP(srcArray);
5203
0
  int     orignitems = ArrayGetNItems(ARR_NDIM(origArray),
5204
0
                      ARR_DIMS(origArray));
5205
0
  int     dest_offset,
5206
0
        orig_offset,
5207
0
        src_offset,
5208
0
        prod[MAXDIM],
5209
0
        span[MAXDIM],
5210
0
        dist[MAXDIM],
5211
0
        indx[MAXDIM];
5212
0
  int     i,
5213
0
        j,
5214
0
        inc;
5215
5216
0
  dest_offset = ArrayGetOffset(ndim, dim, lb, st);
5217
  /* copy items before the slice start */
5218
0
  inc = array_copy(destPtr, dest_offset,
5219
0
           origPtr, 0, origBitmap,
5220
0
           typlen, typbyval, typalign);
5221
0
  destPtr += inc;
5222
0
  origPtr += inc;
5223
0
  if (destBitmap)
5224
0
    array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
5225
0
  orig_offset = dest_offset;
5226
0
  mda_get_prod(ndim, dim, prod);
5227
0
  mda_get_range(ndim, span, st, endp);
5228
0
  mda_get_offset_values(ndim, dist, prod, span);
5229
0
  for (i = 0; i < ndim; i++)
5230
0
    indx[i] = 0;
5231
0
  src_offset = 0;
5232
0
  j = ndim - 1;
5233
0
  do
5234
0
  {
5235
    /* Copy/advance over elements between here and next part of slice */
5236
0
    if (dist[j])
5237
0
    {
5238
0
      inc = array_copy(destPtr, dist[j],
5239
0
               origPtr, orig_offset, origBitmap,
5240
0
               typlen, typbyval, typalign);
5241
0
      destPtr += inc;
5242
0
      origPtr += inc;
5243
0
      if (destBitmap)
5244
0
        array_bitmap_copy(destBitmap, dest_offset,
5245
0
                  origBitmap, orig_offset,
5246
0
                  dist[j]);
5247
0
      dest_offset += dist[j];
5248
0
      orig_offset += dist[j];
5249
0
    }
5250
    /* Copy new element at this slice position */
5251
0
    inc = array_copy(destPtr, 1,
5252
0
             srcPtr, src_offset, srcBitmap,
5253
0
             typlen, typbyval, typalign);
5254
0
    if (destBitmap)
5255
0
      array_bitmap_copy(destBitmap, dest_offset,
5256
0
                srcBitmap, src_offset,
5257
0
                1);
5258
0
    destPtr += inc;
5259
0
    srcPtr += inc;
5260
0
    dest_offset++;
5261
0
    src_offset++;
5262
    /* Advance over old element at this slice position */
5263
0
    origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
5264
0
               typlen, typbyval, typalign);
5265
0
    orig_offset++;
5266
0
  } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
5267
5268
  /* don't miss any data at the end */
5269
0
  array_copy(destPtr, orignitems - orig_offset,
5270
0
         origPtr, orig_offset, origBitmap,
5271
0
         typlen, typbyval, typalign);
5272
0
  if (destBitmap)
5273
0
    array_bitmap_copy(destBitmap, dest_offset,
5274
0
              origBitmap, orig_offset,
5275
0
              orignitems - orig_offset);
5276
0
}
5277
5278
/*
5279
 * initArrayResult - initialize an empty ArrayBuildState
5280
 *
5281
 *  element_type is the array element type (must be a valid array element type)
5282
 *  rcontext is where to keep working state
5283
 *  subcontext is a flag determining whether to use a separate memory context
5284
 *
5285
 * Note: there are two common schemes for using accumArrayResult().
5286
 * In the older scheme, you start with a NULL ArrayBuildState pointer, and
5287
 * call accumArrayResult once per element.  In this scheme you end up with
5288
 * a NULL pointer if there were no elements, which you need to special-case.
5289
 * In the newer scheme, call initArrayResult and then call accumArrayResult
5290
 * once per element.  In this scheme you always end with a non-NULL pointer
5291
 * that you can pass to makeArrayResult; you get an empty array if there
5292
 * were no elements.  This is preferred if an empty array is what you want.
5293
 *
5294
 * It's possible to choose whether to create a separate memory context for the
5295
 * array build state, or whether to allocate it directly within rcontext.
5296
 *
5297
 * When there are many concurrent small states (e.g. array_agg() using hash
5298
 * aggregation of many small groups), using a separate memory context for each
5299
 * one may result in severe memory bloat. In such cases, use the same memory
5300
 * context to initialize all such array build states, and pass
5301
 * subcontext=false.
5302
 *
5303
 * In cases when the array build states have different lifetimes, using a
5304
 * single memory context is impractical. Instead, pass subcontext=true so that
5305
 * the array build states can be freed individually.
5306
 */
5307
ArrayBuildState *
5308
initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
5309
0
{
5310
  /*
5311
   * When using a subcontext, we can afford to start with a somewhat larger
5312
   * initial array size.  Without subcontexts, we'd better hope that most of
5313
   * the states stay small ...
5314
   */
5315
0
  return initArrayResultWithSize(element_type, rcontext, subcontext,
5316
0
                   subcontext ? 64 : 8);
5317
0
}
5318
5319
/*
5320
 * initArrayResultWithSize
5321
 *    As initArrayResult, but allow the initial size of the allocated arrays
5322
 *    to be specified.
5323
 */
5324
ArrayBuildState *
5325
initArrayResultWithSize(Oid element_type, MemoryContext rcontext,
5326
            bool subcontext, int initsize)
5327
0
{
5328
0
  ArrayBuildState *astate;
5329
0
  MemoryContext arr_context = rcontext;
5330
5331
  /* Make a temporary context to hold all the junk */
5332
0
  if (subcontext)
5333
0
    arr_context = AllocSetContextCreate(rcontext,
5334
0
                      "accumArrayResult",
5335
0
                      ALLOCSET_DEFAULT_SIZES);
5336
5337
0
  astate = (ArrayBuildState *)
5338
0
    MemoryContextAlloc(arr_context, sizeof(ArrayBuildState));
5339
0
  astate->mcontext = arr_context;
5340
0
  astate->private_cxt = subcontext;
5341
0
  astate->alen = initsize;
5342
0
  astate->dvalues = (Datum *)
5343
0
    MemoryContextAlloc(arr_context, astate->alen * sizeof(Datum));
5344
0
  astate->dnulls = (bool *)
5345
0
    MemoryContextAlloc(arr_context, astate->alen * sizeof(bool));
5346
0
  astate->nelems = 0;
5347
0
  astate->element_type = element_type;
5348
0
  get_typlenbyvalalign(element_type,
5349
0
             &astate->typlen,
5350
0
             &astate->typbyval,
5351
0
             &astate->typalign);
5352
5353
0
  return astate;
5354
0
}
5355
5356
/*
5357
 * accumArrayResult - accumulate one (more) Datum for an array result
5358
 *
5359
 *  astate is working state (can be NULL on first call)
5360
 *  dvalue/disnull represent the new Datum to append to the array
5361
 *  element_type is the Datum's type (must be a valid array element type)
5362
 *  rcontext is where to keep working state
5363
 */
5364
ArrayBuildState *
5365
accumArrayResult(ArrayBuildState *astate,
5366
         Datum dvalue, bool disnull,
5367
         Oid element_type,
5368
         MemoryContext rcontext)
5369
0
{
5370
0
  MemoryContext oldcontext;
5371
5372
0
  if (astate == NULL)
5373
0
  {
5374
    /* First time through --- initialize */
5375
0
    astate = initArrayResult(element_type, rcontext, true);
5376
0
  }
5377
0
  else
5378
0
  {
5379
0
    Assert(astate->element_type == element_type);
5380
0
  }
5381
5382
0
  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5383
5384
  /* enlarge dvalues[]/dnulls[] if needed */
5385
0
  if (astate->nelems >= astate->alen)
5386
0
  {
5387
0
    astate->alen *= 2;
5388
    /* give an array-related error if we go past MaxAllocSize */
5389
0
    if (!AllocSizeIsValid(astate->alen * sizeof(Datum)))
5390
0
      ereport(ERROR,
5391
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5392
0
           errmsg("array size exceeds the maximum allowed (%zu)",
5393
0
              MaxAllocSize)));
5394
0
    astate->dvalues = (Datum *)
5395
0
      repalloc(astate->dvalues, astate->alen * sizeof(Datum));
5396
0
    astate->dnulls = (bool *)
5397
0
      repalloc(astate->dnulls, astate->alen * sizeof(bool));
5398
0
  }
5399
5400
  /*
5401
   * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
5402
   * it's varlena.  (You might think that detoasting is not needed here
5403
   * because construct_md_array can detoast the array elements later.
5404
   * However, we must not let construct_md_array modify the ArrayBuildState
5405
   * because that would mean array_agg_finalfn damages its input, which is
5406
   * verboten.  Also, this way frequently saves one copying step.)
5407
   */
5408
0
  if (!disnull && !astate->typbyval)
5409
0
  {
5410
0
    if (astate->typlen == -1)
5411
0
      dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
5412
0
    else
5413
0
      dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
5414
0
  }
5415
5416
0
  astate->dvalues[astate->nelems] = dvalue;
5417
0
  astate->dnulls[astate->nelems] = disnull;
5418
0
  astate->nelems++;
5419
5420
0
  MemoryContextSwitchTo(oldcontext);
5421
5422
0
  return astate;
5423
0
}
5424
5425
/*
5426
 * makeArrayResult - produce 1-D final result of accumArrayResult
5427
 *
5428
 * Note: only releases astate if it was initialized within a separate memory
5429
 * context (i.e. using subcontext=true when calling initArrayResult).
5430
 *
5431
 *  astate is working state (must not be NULL)
5432
 *  rcontext is where to construct result
5433
 */
5434
Datum
5435
makeArrayResult(ArrayBuildState *astate,
5436
        MemoryContext rcontext)
5437
0
{
5438
0
  int     ndims;
5439
0
  int     dims[1];
5440
0
  int     lbs[1];
5441
5442
  /* If no elements were presented, we want to create an empty array */
5443
0
  ndims = (astate->nelems > 0) ? 1 : 0;
5444
0
  dims[0] = astate->nelems;
5445
0
  lbs[0] = 1;
5446
5447
0
  return makeMdArrayResult(astate, ndims, dims, lbs, rcontext,
5448
0
               astate->private_cxt);
5449
0
}
5450
5451
/*
5452
 * makeMdArrayResult - produce multi-D final result of accumArrayResult
5453
 *
5454
 * beware: no check that specified dimensions match the number of values
5455
 * accumulated.
5456
 *
5457
 * Note: if the astate was not initialized within a separate memory context
5458
 * (that is, initArrayResult was called with subcontext=false), then using
5459
 * release=true is illegal. Instead, release astate along with the rest of its
5460
 * context when appropriate.
5461
 *
5462
 *  astate is working state (must not be NULL)
5463
 *  rcontext is where to construct result
5464
 *  release is true if okay to release working state
5465
 */
5466
Datum
5467
makeMdArrayResult(ArrayBuildState *astate,
5468
          int ndims,
5469
          int *dims,
5470
          int *lbs,
5471
          MemoryContext rcontext,
5472
          bool release)
5473
0
{
5474
0
  ArrayType  *result;
5475
0
  MemoryContext oldcontext;
5476
5477
  /* Build the final array result in rcontext */
5478
0
  oldcontext = MemoryContextSwitchTo(rcontext);
5479
5480
0
  result = construct_md_array(astate->dvalues,
5481
0
                astate->dnulls,
5482
0
                ndims,
5483
0
                dims,
5484
0
                lbs,
5485
0
                astate->element_type,
5486
0
                astate->typlen,
5487
0
                astate->typbyval,
5488
0
                astate->typalign);
5489
5490
0
  MemoryContextSwitchTo(oldcontext);
5491
5492
  /* Clean up all the junk */
5493
0
  if (release)
5494
0
  {
5495
0
    Assert(astate->private_cxt);
5496
0
    MemoryContextDelete(astate->mcontext);
5497
0
  }
5498
5499
0
  return PointerGetDatum(result);
5500
0
}
5501
5502
/*
5503
 * The following three functions provide essentially the same API as
5504
 * initArrayResult/accumArrayResult/makeArrayResult, but instead of accepting
5505
 * inputs that are array elements, they accept inputs that are arrays and
5506
 * produce an output array having N+1 dimensions.  The inputs must all have
5507
 * identical dimensionality as well as element type.
5508
 */
5509
5510
/*
5511
 * initArrayResultArr - initialize an empty ArrayBuildStateArr
5512
 *
5513
 *  array_type is the array type (must be a valid varlena array type)
5514
 *  element_type is the type of the array's elements (lookup if InvalidOid)
5515
 *  rcontext is where to keep working state
5516
 *  subcontext is a flag determining whether to use a separate memory context
5517
 */
5518
ArrayBuildStateArr *
5519
initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext,
5520
           bool subcontext)
5521
0
{
5522
0
  ArrayBuildStateArr *astate;
5523
0
  MemoryContext arr_context = rcontext; /* by default use the parent ctx */
5524
5525
  /* Lookup element type, unless element_type already provided */
5526
0
  if (!OidIsValid(element_type))
5527
0
  {
5528
0
    element_type = get_element_type(array_type);
5529
5530
0
    if (!OidIsValid(element_type))
5531
0
      ereport(ERROR,
5532
0
          (errcode(ERRCODE_DATATYPE_MISMATCH),
5533
0
           errmsg("data type %s is not an array type",
5534
0
              format_type_be(array_type))));
5535
0
  }
5536
5537
  /* Make a temporary context to hold all the junk */
5538
0
  if (subcontext)
5539
0
    arr_context = AllocSetContextCreate(rcontext,
5540
0
                      "accumArrayResultArr",
5541
0
                      ALLOCSET_DEFAULT_SIZES);
5542
5543
  /* Note we initialize all fields to zero */
5544
0
  astate = (ArrayBuildStateArr *)
5545
0
    MemoryContextAllocZero(arr_context, sizeof(ArrayBuildStateArr));
5546
0
  astate->mcontext = arr_context;
5547
0
  astate->private_cxt = subcontext;
5548
5549
  /* Save relevant datatype information */
5550
0
  astate->array_type = array_type;
5551
0
  astate->element_type = element_type;
5552
5553
0
  return astate;
5554
0
}
5555
5556
/*
5557
 * accumArrayResultArr - accumulate one (more) sub-array for an array result
5558
 *
5559
 *  astate is working state (can be NULL on first call)
5560
 *  dvalue/disnull represent the new sub-array to append to the array
5561
 *  array_type is the array type (must be a valid varlena array type)
5562
 *  rcontext is where to keep working state
5563
 */
5564
ArrayBuildStateArr *
5565
accumArrayResultArr(ArrayBuildStateArr *astate,
5566
          Datum dvalue, bool disnull,
5567
          Oid array_type,
5568
          MemoryContext rcontext)
5569
0
{
5570
0
  ArrayType  *arg;
5571
0
  MemoryContext oldcontext;
5572
0
  int      *dims,
5573
0
         *lbs,
5574
0
        ndims,
5575
0
        nitems,
5576
0
        ndatabytes;
5577
0
  char     *data;
5578
0
  int     i;
5579
0
  int     newnitems;
5580
5581
  /*
5582
   * We disallow accumulating null subarrays.  Another plausible definition
5583
   * is to ignore them, but callers that want that can just skip calling
5584
   * this function.
5585
   */
5586
0
  if (disnull)
5587
0
    ereport(ERROR,
5588
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5589
0
         errmsg("cannot accumulate null arrays")));
5590
5591
  /* Detoast input array in caller's context */
5592
0
  arg = DatumGetArrayTypeP(dvalue);
5593
5594
0
  if (astate == NULL)
5595
0
    astate = initArrayResultArr(array_type, InvalidOid, rcontext, true);
5596
0
  else
5597
0
    Assert(astate->array_type == array_type);
5598
5599
0
  oldcontext = MemoryContextSwitchTo(astate->mcontext);
5600
5601
  /* Collect this input's dimensions */
5602
0
  ndims = ARR_NDIM(arg);
5603
0
  dims = ARR_DIMS(arg);
5604
0
  lbs = ARR_LBOUND(arg);
5605
0
  data = ARR_DATA_PTR(arg);
5606
0
  nitems = ArrayGetNItems(ndims, dims);
5607
0
  ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
5608
5609
  /* Check that the array doesn't grow too large */
5610
0
  newnitems = astate->nitems + nitems;
5611
0
  if (newnitems > MaxArraySize)
5612
0
    ereport(ERROR,
5613
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5614
0
         errmsg("array size exceeds the maximum allowed (%zu)",
5615
0
            MaxArraySize)));
5616
5617
0
  if (astate->ndims == 0)
5618
0
  {
5619
    /* First input; check/save the dimensionality info */
5620
5621
    /* Should we allow empty inputs and just produce an empty output? */
5622
0
    if (ndims == 0)
5623
0
      ereport(ERROR,
5624
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5625
0
           errmsg("cannot accumulate empty arrays")));
5626
0
    if (ndims + 1 > MAXDIM)
5627
0
      ereport(ERROR,
5628
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
5629
0
           errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
5630
0
              ndims + 1, MAXDIM)));
5631
5632
    /*
5633
     * The output array will have n+1 dimensions, with the ones after the
5634
     * first matching the input's dimensions.
5635
     */
5636
0
    astate->ndims = ndims + 1;
5637
0
    astate->dims[0] = 0;
5638
0
    memcpy(&astate->dims[1], dims, ndims * sizeof(int));
5639
0
    astate->lbs[0] = 1;
5640
0
    memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
5641
5642
    /* Allocate at least enough data space for this item */
5643
0
    astate->abytes = pg_nextpower2_32(Max(1024, ndatabytes + 1));
5644
0
    astate->data = (char *) palloc(astate->abytes);
5645
0
  }
5646
0
  else
5647
0
  {
5648
    /* Second or later input: must match first input's dimensionality */
5649
0
    if (astate->ndims != ndims + 1)
5650
0
      ereport(ERROR,
5651
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5652
0
           errmsg("cannot accumulate arrays of different dimensionality")));
5653
0
    for (i = 0; i < ndims; i++)
5654
0
    {
5655
0
      if (astate->dims[i + 1] != dims[i] || astate->lbs[i + 1] != lbs[i])
5656
0
        ereport(ERROR,
5657
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5658
0
             errmsg("cannot accumulate arrays of different dimensionality")));
5659
0
    }
5660
5661
    /* Enlarge data space if needed */
5662
0
    if (astate->nbytes + ndatabytes > astate->abytes)
5663
0
    {
5664
0
      astate->abytes = Max(astate->abytes * 2,
5665
0
                 astate->nbytes + ndatabytes);
5666
0
      astate->data = (char *) repalloc(astate->data, astate->abytes);
5667
0
    }
5668
0
  }
5669
5670
  /*
5671
   * Copy the data portion of the sub-array.  Note we assume that the
5672
   * advertised data length of the sub-array is properly aligned.  We do not
5673
   * have to worry about detoasting elements since whatever's in the
5674
   * sub-array should be OK already.
5675
   */
5676
0
  memcpy(astate->data + astate->nbytes, data, ndatabytes);
5677
0
  astate->nbytes += ndatabytes;
5678
5679
  /* Deal with null bitmap if needed */
5680
0
  if (astate->nullbitmap || ARR_HASNULL(arg))
5681
0
  {
5682
0
    if (astate->nullbitmap == NULL)
5683
0
    {
5684
      /*
5685
       * First input with nulls; we must retrospectively handle any
5686
       * previous inputs by marking all their items non-null.
5687
       */
5688
0
      astate->aitems = pg_nextpower2_32(Max(256, newnitems + 1));
5689
0
      astate->nullbitmap = (uint8 *) palloc((astate->aitems + 7) / 8);
5690
0
      array_bitmap_copy(astate->nullbitmap, 0,
5691
0
                NULL, 0,
5692
0
                astate->nitems);
5693
0
    }
5694
0
    else if (newnitems > astate->aitems)
5695
0
    {
5696
0
      astate->aitems = Max(astate->aitems * 2, newnitems);
5697
0
      astate->nullbitmap = (uint8 *)
5698
0
        repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
5699
0
    }
5700
0
    array_bitmap_copy(astate->nullbitmap, astate->nitems,
5701
0
              ARR_NULLBITMAP(arg), 0,
5702
0
              nitems);
5703
0
  }
5704
5705
0
  astate->nitems = newnitems;
5706
0
  astate->dims[0] += 1;
5707
5708
0
  MemoryContextSwitchTo(oldcontext);
5709
5710
  /* Release detoasted copy if any */
5711
0
  if (arg != DatumGetPointer(dvalue))
5712
0
    pfree(arg);
5713
5714
0
  return astate;
5715
0
}
5716
5717
/*
5718
 * makeArrayResultArr - produce N+1-D final result of accumArrayResultArr
5719
 *
5720
 *  astate is working state (must not be NULL)
5721
 *  rcontext is where to construct result
5722
 *  release is true if okay to release working state
5723
 */
5724
Datum
5725
makeArrayResultArr(ArrayBuildStateArr *astate,
5726
           MemoryContext rcontext,
5727
           bool release)
5728
0
{
5729
0
  ArrayType  *result;
5730
0
  MemoryContext oldcontext;
5731
5732
  /* Build the final array result in rcontext */
5733
0
  oldcontext = MemoryContextSwitchTo(rcontext);
5734
5735
0
  if (astate->ndims == 0)
5736
0
  {
5737
    /* No inputs, return empty array */
5738
0
    result = construct_empty_array(astate->element_type);
5739
0
  }
5740
0
  else
5741
0
  {
5742
0
    int     dataoffset,
5743
0
          nbytes;
5744
5745
    /* Check for overflow of the array dimensions */
5746
0
    (void) ArrayGetNItems(astate->ndims, astate->dims);
5747
0
    ArrayCheckBounds(astate->ndims, astate->dims, astate->lbs);
5748
5749
    /* Compute required space */
5750
0
    nbytes = astate->nbytes;
5751
0
    if (astate->nullbitmap != NULL)
5752
0
    {
5753
0
      dataoffset = ARR_OVERHEAD_WITHNULLS(astate->ndims, astate->nitems);
5754
0
      nbytes += dataoffset;
5755
0
    }
5756
0
    else
5757
0
    {
5758
0
      dataoffset = 0;
5759
0
      nbytes += ARR_OVERHEAD_NONULLS(astate->ndims);
5760
0
    }
5761
5762
0
    result = (ArrayType *) palloc0(nbytes);
5763
0
    SET_VARSIZE(result, nbytes);
5764
0
    result->ndim = astate->ndims;
5765
0
    result->dataoffset = dataoffset;
5766
0
    result->elemtype = astate->element_type;
5767
5768
0
    memcpy(ARR_DIMS(result), astate->dims, astate->ndims * sizeof(int));
5769
0
    memcpy(ARR_LBOUND(result), astate->lbs, astate->ndims * sizeof(int));
5770
0
    memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
5771
5772
0
    if (astate->nullbitmap != NULL)
5773
0
      array_bitmap_copy(ARR_NULLBITMAP(result), 0,
5774
0
                astate->nullbitmap, 0,
5775
0
                astate->nitems);
5776
0
  }
5777
5778
0
  MemoryContextSwitchTo(oldcontext);
5779
5780
  /* Clean up all the junk */
5781
0
  if (release)
5782
0
  {
5783
0
    Assert(astate->private_cxt);
5784
0
    MemoryContextDelete(astate->mcontext);
5785
0
  }
5786
5787
0
  return PointerGetDatum(result);
5788
0
}
5789
5790
/*
5791
 * The following three functions provide essentially the same API as
5792
 * initArrayResult/accumArrayResult/makeArrayResult, but can accept either
5793
 * scalar or array inputs, invoking the appropriate set of functions above.
5794
 */
5795
5796
/*
5797
 * initArrayResultAny - initialize an empty ArrayBuildStateAny
5798
 *
5799
 *  input_type is the input datatype (either element or array type)
5800
 *  rcontext is where to keep working state
5801
 *  subcontext is a flag determining whether to use a separate memory context
5802
 */
5803
ArrayBuildStateAny *
5804
initArrayResultAny(Oid input_type, MemoryContext rcontext, bool subcontext)
5805
0
{
5806
0
  ArrayBuildStateAny *astate;
5807
5808
  /*
5809
   * int2vector and oidvector will satisfy both get_element_type and
5810
   * get_array_type.  We prefer to treat them as scalars, to be consistent
5811
   * with get_promoted_array_type.  Hence, check get_array_type not
5812
   * get_element_type.
5813
   */
5814
0
  if (!OidIsValid(get_array_type(input_type)))
5815
0
  {
5816
    /* Array case */
5817
0
    ArrayBuildStateArr *arraystate;
5818
5819
0
    arraystate = initArrayResultArr(input_type, InvalidOid, rcontext, subcontext);
5820
0
    astate = (ArrayBuildStateAny *)
5821
0
      MemoryContextAlloc(arraystate->mcontext,
5822
0
                 sizeof(ArrayBuildStateAny));
5823
0
    astate->scalarstate = NULL;
5824
0
    astate->arraystate = arraystate;
5825
0
  }
5826
0
  else
5827
0
  {
5828
    /* Scalar case */
5829
0
    ArrayBuildState *scalarstate;
5830
5831
0
    scalarstate = initArrayResult(input_type, rcontext, subcontext);
5832
0
    astate = (ArrayBuildStateAny *)
5833
0
      MemoryContextAlloc(scalarstate->mcontext,
5834
0
                 sizeof(ArrayBuildStateAny));
5835
0
    astate->scalarstate = scalarstate;
5836
0
    astate->arraystate = NULL;
5837
0
  }
5838
5839
0
  return astate;
5840
0
}
5841
5842
/*
5843
 * accumArrayResultAny - accumulate one (more) input for an array result
5844
 *
5845
 *  astate is working state (can be NULL on first call)
5846
 *  dvalue/disnull represent the new input to append to the array
5847
 *  input_type is the input datatype (either element or array type)
5848
 *  rcontext is where to keep working state
5849
 */
5850
ArrayBuildStateAny *
5851
accumArrayResultAny(ArrayBuildStateAny *astate,
5852
          Datum dvalue, bool disnull,
5853
          Oid input_type,
5854
          MemoryContext rcontext)
5855
0
{
5856
0
  if (astate == NULL)
5857
0
    astate = initArrayResultAny(input_type, rcontext, true);
5858
5859
0
  if (astate->scalarstate)
5860
0
    (void) accumArrayResult(astate->scalarstate,
5861
0
                dvalue, disnull,
5862
0
                input_type, rcontext);
5863
0
  else
5864
0
    (void) accumArrayResultArr(astate->arraystate,
5865
0
                   dvalue, disnull,
5866
0
                   input_type, rcontext);
5867
5868
0
  return astate;
5869
0
}
5870
5871
/*
5872
 * makeArrayResultAny - produce final result of accumArrayResultAny
5873
 *
5874
 *  astate is working state (must not be NULL)
5875
 *  rcontext is where to construct result
5876
 *  release is true if okay to release working state
5877
 */
5878
Datum
5879
makeArrayResultAny(ArrayBuildStateAny *astate,
5880
           MemoryContext rcontext, bool release)
5881
0
{
5882
0
  Datum   result;
5883
5884
0
  if (astate->scalarstate)
5885
0
  {
5886
    /* Must use makeMdArrayResult to support "release" parameter */
5887
0
    int     ndims;
5888
0
    int     dims[1];
5889
0
    int     lbs[1];
5890
5891
    /* If no elements were presented, we want to create an empty array */
5892
0
    ndims = (astate->scalarstate->nelems > 0) ? 1 : 0;
5893
0
    dims[0] = astate->scalarstate->nelems;
5894
0
    lbs[0] = 1;
5895
5896
0
    result = makeMdArrayResult(astate->scalarstate, ndims, dims, lbs,
5897
0
                   rcontext, release);
5898
0
  }
5899
0
  else
5900
0
  {
5901
0
    result = makeArrayResultArr(astate->arraystate,
5902
0
                  rcontext, release);
5903
0
  }
5904
0
  return result;
5905
0
}
5906
5907
5908
Datum
5909
array_larger(PG_FUNCTION_ARGS)
5910
0
{
5911
0
  if (array_cmp(fcinfo) > 0)
5912
0
    PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5913
0
  else
5914
0
    PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5915
0
}
5916
5917
Datum
5918
array_smaller(PG_FUNCTION_ARGS)
5919
0
{
5920
0
  if (array_cmp(fcinfo) < 0)
5921
0
    PG_RETURN_DATUM(PG_GETARG_DATUM(0));
5922
0
  else
5923
0
    PG_RETURN_DATUM(PG_GETARG_DATUM(1));
5924
0
}
5925
5926
5927
typedef struct generate_subscripts_fctx
5928
{
5929
  int32   lower;
5930
  int32   upper;
5931
  bool    reverse;
5932
} generate_subscripts_fctx;
5933
5934
/*
5935
 * generate_subscripts(array anyarray, dim int [, reverse bool])
5936
 *    Returns all subscripts of the array for any dimension
5937
 */
5938
Datum
5939
generate_subscripts(PG_FUNCTION_ARGS)
5940
0
{
5941
0
  FuncCallContext *funcctx;
5942
0
  MemoryContext oldcontext;
5943
0
  generate_subscripts_fctx *fctx;
5944
5945
  /* stuff done only on the first call of the function */
5946
0
  if (SRF_IS_FIRSTCALL())
5947
0
  {
5948
0
    AnyArrayType *v = PG_GETARG_ANY_ARRAY_P(0);
5949
0
    int     reqdim = PG_GETARG_INT32(1);
5950
0
    int      *lb,
5951
0
           *dimv;
5952
5953
    /* create a function context for cross-call persistence */
5954
0
    funcctx = SRF_FIRSTCALL_INIT();
5955
5956
    /* Sanity check: does it look like an array at all? */
5957
0
    if (AARR_NDIM(v) <= 0 || AARR_NDIM(v) > MAXDIM)
5958
0
      SRF_RETURN_DONE(funcctx);
5959
5960
    /* Sanity check: was the requested dim valid */
5961
0
    if (reqdim <= 0 || reqdim > AARR_NDIM(v))
5962
0
      SRF_RETURN_DONE(funcctx);
5963
5964
    /*
5965
     * switch to memory context appropriate for multiple function calls
5966
     */
5967
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
5968
0
    fctx = palloc_object(generate_subscripts_fctx);
5969
5970
0
    lb = AARR_LBOUND(v);
5971
0
    dimv = AARR_DIMS(v);
5972
5973
0
    fctx->lower = lb[reqdim - 1];
5974
0
    fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
5975
0
    fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
5976
5977
0
    funcctx->user_fctx = fctx;
5978
5979
0
    MemoryContextSwitchTo(oldcontext);
5980
0
  }
5981
5982
0
  funcctx = SRF_PERCALL_SETUP();
5983
5984
0
  fctx = funcctx->user_fctx;
5985
5986
0
  if (fctx->lower <= fctx->upper)
5987
0
  {
5988
0
    if (!fctx->reverse)
5989
0
      SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
5990
0
    else
5991
0
      SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
5992
0
  }
5993
0
  else
5994
    /* done when there are no more elements left */
5995
0
    SRF_RETURN_DONE(funcctx);
5996
0
}
5997
5998
/*
5999
 * generate_subscripts_nodir
6000
 *    Implements the 2-argument version of generate_subscripts
6001
 */
6002
Datum
6003
generate_subscripts_nodir(PG_FUNCTION_ARGS)
6004
0
{
6005
  /* just call the other one -- it can handle both cases */
6006
0
  return generate_subscripts(fcinfo);
6007
0
}
6008
6009
/*
6010
 * array_fill_with_lower_bounds
6011
 *    Create and fill array with defined lower bounds.
6012
 */
6013
Datum
6014
array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
6015
0
{
6016
0
  ArrayType  *dims;
6017
0
  ArrayType  *lbs;
6018
0
  ArrayType  *result;
6019
0
  Oid     elmtype;
6020
0
  Datum   value;
6021
0
  bool    isnull;
6022
6023
0
  if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
6024
0
    ereport(ERROR,
6025
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6026
0
         errmsg("dimension array or low bound array cannot be null")));
6027
6028
0
  dims = PG_GETARG_ARRAYTYPE_P(1);
6029
0
  lbs = PG_GETARG_ARRAYTYPE_P(2);
6030
6031
0
  if (!PG_ARGISNULL(0))
6032
0
  {
6033
0
    value = PG_GETARG_DATUM(0);
6034
0
    isnull = false;
6035
0
  }
6036
0
  else
6037
0
  {
6038
0
    value = 0;
6039
0
    isnull = true;
6040
0
  }
6041
6042
0
  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6043
0
  if (!OidIsValid(elmtype))
6044
0
    elog(ERROR, "could not determine data type of input");
6045
6046
0
  result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
6047
0
  PG_RETURN_ARRAYTYPE_P(result);
6048
0
}
6049
6050
/*
6051
 * array_fill
6052
 *    Create and fill array with default lower bounds.
6053
 */
6054
Datum
6055
array_fill(PG_FUNCTION_ARGS)
6056
0
{
6057
0
  ArrayType  *dims;
6058
0
  ArrayType  *result;
6059
0
  Oid     elmtype;
6060
0
  Datum   value;
6061
0
  bool    isnull;
6062
6063
0
  if (PG_ARGISNULL(1))
6064
0
    ereport(ERROR,
6065
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6066
0
         errmsg("dimension array or low bound array cannot be null")));
6067
6068
0
  dims = PG_GETARG_ARRAYTYPE_P(1);
6069
6070
0
  if (!PG_ARGISNULL(0))
6071
0
  {
6072
0
    value = PG_GETARG_DATUM(0);
6073
0
    isnull = false;
6074
0
  }
6075
0
  else
6076
0
  {
6077
0
    value = 0;
6078
0
    isnull = true;
6079
0
  }
6080
6081
0
  elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
6082
0
  if (!OidIsValid(elmtype))
6083
0
    elog(ERROR, "could not determine data type of input");
6084
6085
0
  result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
6086
0
  PG_RETURN_ARRAYTYPE_P(result);
6087
0
}
6088
6089
static ArrayType *
6090
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
6091
            Oid elmtype, int dataoffset)
6092
0
{
6093
0
  ArrayType  *result;
6094
6095
0
  result = (ArrayType *) palloc0(nbytes);
6096
0
  SET_VARSIZE(result, nbytes);
6097
0
  result->ndim = ndims;
6098
0
  result->dataoffset = dataoffset;
6099
0
  result->elemtype = elmtype;
6100
0
  memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
6101
0
  memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
6102
6103
0
  return result;
6104
0
}
6105
6106
static ArrayType *
6107
array_fill_internal(ArrayType *dims, ArrayType *lbs,
6108
          Datum value, bool isnull, Oid elmtype,
6109
          FunctionCallInfo fcinfo)
6110
0
{
6111
0
  ArrayType  *result;
6112
0
  int      *dimv;
6113
0
  int      *lbsv;
6114
0
  int     ndims;
6115
0
  int     nitems;
6116
0
  int     deflbs[MAXDIM];
6117
0
  int16   elmlen;
6118
0
  bool    elmbyval;
6119
0
  char    elmalign;
6120
0
  uint8   elmalignby;
6121
0
  ArrayMetaState *my_extra;
6122
6123
  /*
6124
   * Params checks
6125
   */
6126
0
  if (ARR_NDIM(dims) > 1)
6127
0
    ereport(ERROR,
6128
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6129
0
         errmsg("wrong number of array subscripts"),
6130
0
         errdetail("Dimension array must be one dimensional.")));
6131
6132
0
  if (array_contains_nulls(dims))
6133
0
    ereport(ERROR,
6134
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6135
0
         errmsg("dimension values cannot be null")));
6136
6137
0
  dimv = (int *) ARR_DATA_PTR(dims);
6138
0
  ndims = (ARR_NDIM(dims) > 0) ? ARR_DIMS(dims)[0] : 0;
6139
6140
0
  if (ndims < 0)       /* we do allow zero-dimension arrays */
6141
0
    ereport(ERROR,
6142
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
6143
0
         errmsg("invalid number of dimensions: %d", ndims)));
6144
0
  if (ndims > MAXDIM)
6145
0
    ereport(ERROR,
6146
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6147
0
         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
6148
0
            ndims, MAXDIM)));
6149
6150
0
  if (lbs != NULL)
6151
0
  {
6152
0
    if (ARR_NDIM(lbs) > 1)
6153
0
      ereport(ERROR,
6154
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6155
0
           errmsg("wrong number of array subscripts"),
6156
0
           errdetail("Dimension array must be one dimensional.")));
6157
6158
0
    if (array_contains_nulls(lbs))
6159
0
      ereport(ERROR,
6160
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6161
0
           errmsg("dimension values cannot be null")));
6162
6163
0
    if (ndims != ((ARR_NDIM(lbs) > 0) ? ARR_DIMS(lbs)[0] : 0))
6164
0
      ereport(ERROR,
6165
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6166
0
           errmsg("wrong number of array subscripts"),
6167
0
           errdetail("Low bound array has different size than dimensions array.")));
6168
6169
0
    lbsv = (int *) ARR_DATA_PTR(lbs);
6170
0
  }
6171
0
  else
6172
0
  {
6173
0
    int     i;
6174
6175
0
    for (i = 0; i < MAXDIM; i++)
6176
0
      deflbs[i] = 1;
6177
6178
0
    lbsv = deflbs;
6179
0
  }
6180
6181
  /* This checks for overflow of the array dimensions */
6182
0
  nitems = ArrayGetNItems(ndims, dimv);
6183
0
  ArrayCheckBounds(ndims, dimv, lbsv);
6184
6185
  /* fast track for empty array */
6186
0
  if (nitems <= 0)
6187
0
    return construct_empty_array(elmtype);
6188
6189
  /*
6190
   * We arrange to look up info about element type only once per series of
6191
   * calls, assuming the element type doesn't change underneath us.
6192
   */
6193
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6194
0
  if (my_extra == NULL)
6195
0
  {
6196
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
6197
0
                            sizeof(ArrayMetaState));
6198
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
6199
0
    my_extra->element_type = InvalidOid;
6200
0
  }
6201
6202
0
  if (my_extra->element_type != elmtype)
6203
0
  {
6204
    /* Get info about element type */
6205
0
    get_typlenbyvalalign(elmtype,
6206
0
               &my_extra->typlen,
6207
0
               &my_extra->typbyval,
6208
0
               &my_extra->typalign);
6209
0
    my_extra->element_type = elmtype;
6210
0
  }
6211
6212
0
  elmlen = my_extra->typlen;
6213
0
  elmbyval = my_extra->typbyval;
6214
0
  elmalign = my_extra->typalign;
6215
0
  elmalignby = typalign_to_alignby(elmalign);
6216
6217
  /* compute required space */
6218
0
  if (!isnull)
6219
0
  {
6220
0
    int     i;
6221
0
    char     *p;
6222
0
    int     nbytes;
6223
0
    int     totbytes;
6224
6225
    /* make sure data is not toasted */
6226
0
    if (elmlen == -1)
6227
0
      value = PointerGetDatum(PG_DETOAST_DATUM(value));
6228
6229
0
    nbytes = att_addlength_datum(0, elmlen, value);
6230
0
    nbytes = att_nominal_alignby(nbytes, elmalignby);
6231
0
    Assert(nbytes > 0);
6232
6233
0
    totbytes = nbytes * nitems;
6234
6235
    /* check for overflow of multiplication or total request */
6236
0
    if (totbytes / nbytes != nitems ||
6237
0
      !AllocSizeIsValid(totbytes))
6238
0
      ereport(ERROR,
6239
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6240
0
           errmsg("array size exceeds the maximum allowed (%zu)",
6241
0
              MaxAllocSize)));
6242
6243
    /*
6244
     * This addition can't overflow, but it might cause us to go past
6245
     * MaxAllocSize.  We leave it to palloc to complain in that case.
6246
     */
6247
0
    totbytes += ARR_OVERHEAD_NONULLS(ndims);
6248
6249
0
    result = create_array_envelope(ndims, dimv, lbsv, totbytes,
6250
0
                     elmtype, 0);
6251
6252
0
    p = ARR_DATA_PTR(result);
6253
0
    for (i = 0; i < nitems; i++)
6254
0
      p += ArrayCastAndSet(value, elmlen, elmbyval, elmalignby, p);
6255
0
  }
6256
0
  else
6257
0
  {
6258
0
    int     nbytes;
6259
0
    int     dataoffset;
6260
6261
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
6262
0
    nbytes = dataoffset;
6263
6264
0
    result = create_array_envelope(ndims, dimv, lbsv, nbytes,
6265
0
                     elmtype, dataoffset);
6266
6267
    /* create_array_envelope already zeroed the bitmap, so we're done */
6268
0
  }
6269
6270
0
  return result;
6271
0
}
6272
6273
6274
/*
6275
 * UNNEST
6276
 */
6277
Datum
6278
array_unnest(PG_FUNCTION_ARGS)
6279
0
{
6280
0
  typedef struct
6281
0
  {
6282
0
    array_iter  iter;
6283
0
    int     nextelem;
6284
0
    int     numelems;
6285
0
  } array_unnest_fctx;
6286
6287
0
  FuncCallContext *funcctx;
6288
0
  array_unnest_fctx *fctx;
6289
0
  MemoryContext oldcontext;
6290
6291
  /* stuff done only on the first call of the function */
6292
0
  if (SRF_IS_FIRSTCALL())
6293
0
  {
6294
0
    AnyArrayType *arr;
6295
0
    int16   elmlen;
6296
0
    bool    elmbyval;
6297
0
    char    elmalign;
6298
6299
    /* create a function context for cross-call persistence */
6300
0
    funcctx = SRF_FIRSTCALL_INIT();
6301
6302
    /*
6303
     * switch to memory context appropriate for multiple function calls
6304
     */
6305
0
    oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
6306
6307
    /*
6308
     * Get the array value and detoast if needed.  We can't do this
6309
     * earlier because if we have to detoast, we want the detoasted copy
6310
     * to be in multi_call_memory_ctx, so it will go away when we're done
6311
     * and not before.  (If no detoast happens, we assume the originally
6312
     * passed array will stick around till then.)
6313
     */
6314
0
    arr = PG_GETARG_ANY_ARRAY_P(0);
6315
6316
    /* allocate memory for user context */
6317
0
    fctx = palloc_object(array_unnest_fctx);
6318
6319
    /* get element-type data */
6320
0
    if (VARATT_IS_EXPANDED_HEADER(arr))
6321
0
    {
6322
      /* we can just grab the type data from expanded array */
6323
0
      elmlen = arr->xpn.typlen;
6324
0
      elmbyval = arr->xpn.typbyval;
6325
0
      elmalign = arr->xpn.typalign;
6326
0
    }
6327
0
    else
6328
0
      get_typlenbyvalalign(AARR_ELEMTYPE(arr),
6329
0
                 &elmlen,
6330
0
                 &elmbyval,
6331
0
                 &elmalign);
6332
6333
    /* initialize state */
6334
0
    array_iter_setup(&fctx->iter, arr, elmlen, elmbyval, elmalign);
6335
0
    fctx->nextelem = 0;
6336
0
    fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr));
6337
6338
0
    funcctx->user_fctx = fctx;
6339
0
    MemoryContextSwitchTo(oldcontext);
6340
0
  }
6341
6342
  /* stuff done on every call of the function */
6343
0
  funcctx = SRF_PERCALL_SETUP();
6344
0
  fctx = funcctx->user_fctx;
6345
6346
0
  if (fctx->nextelem < fctx->numelems)
6347
0
  {
6348
0
    int     offset = fctx->nextelem++;
6349
0
    Datum   elem;
6350
6351
0
    elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset);
6352
6353
0
    SRF_RETURN_NEXT(funcctx, elem);
6354
0
  }
6355
0
  else
6356
0
  {
6357
    /* do when there is no more left */
6358
0
    SRF_RETURN_DONE(funcctx);
6359
0
  }
6360
0
}
6361
6362
/*
6363
 * Planner support function for array_unnest(anyarray)
6364
 *
6365
 * Note: this is now also used for information_schema._pg_expandarray(),
6366
 * which is simply a wrapper around array_unnest().
6367
 */
6368
Datum
6369
array_unnest_support(PG_FUNCTION_ARGS)
6370
0
{
6371
0
  Node     *rawreq = (Node *) PG_GETARG_POINTER(0);
6372
0
  Node     *ret = NULL;
6373
6374
0
  if (IsA(rawreq, SupportRequestRows))
6375
0
  {
6376
    /* Try to estimate the number of rows returned */
6377
0
    SupportRequestRows *req = (SupportRequestRows *) rawreq;
6378
6379
0
    if (is_funcclause(req->node)) /* be paranoid */
6380
0
    {
6381
0
      List     *args = ((FuncExpr *) req->node)->args;
6382
0
      Node     *arg1;
6383
6384
      /* We can use estimated argument values here */
6385
0
      arg1 = estimate_expression_value(req->root, linitial(args));
6386
6387
0
      req->rows = estimate_array_length(req->root, arg1);
6388
0
      ret = (Node *) req;
6389
0
    }
6390
0
  }
6391
6392
0
  PG_RETURN_POINTER(ret);
6393
0
}
6394
6395
6396
/*
6397
 * array_replace/array_remove support
6398
 *
6399
 * Find all array entries matching (not distinct from) search/search_isnull,
6400
 * and delete them if remove is true, else replace them with
6401
 * replace/replace_isnull.  Comparisons are done using the specified
6402
 * collation.  fcinfo is passed only for caching purposes.
6403
 */
6404
static ArrayType *
6405
array_replace_internal(ArrayType *array,
6406
             Datum search, bool search_isnull,
6407
             Datum replace, bool replace_isnull,
6408
             bool remove, Oid collation,
6409
             FunctionCallInfo fcinfo)
6410
0
{
6411
0
  LOCAL_FCINFO(locfcinfo, 2);
6412
0
  ArrayType  *result;
6413
0
  Oid     element_type;
6414
0
  Datum    *values;
6415
0
  bool     *nulls;
6416
0
  int      *dim;
6417
0
  int     ndim;
6418
0
  int     nitems,
6419
0
        nresult;
6420
0
  int     i;
6421
0
  int32   nbytes = 0;
6422
0
  int32   dataoffset;
6423
0
  bool    hasnulls;
6424
0
  int     typlen;
6425
0
  bool    typbyval;
6426
0
  char    typalign;
6427
0
  uint8   typalignby;
6428
0
  char     *arraydataptr;
6429
0
  uint8    *bitmap;
6430
0
  int     bitmask;
6431
0
  bool    changed = false;
6432
0
  TypeCacheEntry *typentry;
6433
6434
0
  element_type = ARR_ELEMTYPE(array);
6435
0
  ndim = ARR_NDIM(array);
6436
0
  dim = ARR_DIMS(array);
6437
0
  nitems = ArrayGetNItems(ndim, dim);
6438
6439
  /* Return input array unmodified if it is empty */
6440
0
  if (nitems <= 0)
6441
0
    return array;
6442
6443
  /*
6444
   * We can't remove elements from multi-dimensional arrays, since the
6445
   * result might not be rectangular.
6446
   */
6447
0
  if (remove && ndim > 1)
6448
0
    ereport(ERROR,
6449
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6450
0
         errmsg("removing elements from multidimensional arrays is not supported")));
6451
6452
  /*
6453
   * We arrange to look up the equality function only once per series of
6454
   * calls, assuming the element type doesn't change underneath us.
6455
   */
6456
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6457
0
  if (typentry == NULL ||
6458
0
    typentry->type_id != element_type)
6459
0
  {
6460
0
    typentry = lookup_type_cache(element_type,
6461
0
                   TYPECACHE_EQ_OPR_FINFO);
6462
0
    if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
6463
0
      ereport(ERROR,
6464
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
6465
0
           errmsg("could not identify an equality operator for type %s",
6466
0
              format_type_be(element_type))));
6467
0
    fcinfo->flinfo->fn_extra = typentry;
6468
0
  }
6469
0
  typlen = typentry->typlen;
6470
0
  typbyval = typentry->typbyval;
6471
0
  typalign = typentry->typalign;
6472
0
  typalignby = typalign_to_alignby(typalign);
6473
6474
  /*
6475
   * Detoast values if they are toasted.  The replacement value must be
6476
   * detoasted for insertion into the result array, while detoasting the
6477
   * search value only once saves cycles.
6478
   */
6479
0
  if (typlen == -1)
6480
0
  {
6481
0
    if (!search_isnull)
6482
0
      search = PointerGetDatum(PG_DETOAST_DATUM(search));
6483
0
    if (!replace_isnull)
6484
0
      replace = PointerGetDatum(PG_DETOAST_DATUM(replace));
6485
0
  }
6486
6487
  /* Prepare to apply the comparison operator */
6488
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->eq_opr_finfo, 2,
6489
0
               collation, NULL, NULL);
6490
6491
  /* Allocate temporary arrays for new values */
6492
0
  values = (Datum *) palloc(nitems * sizeof(Datum));
6493
0
  nulls = (bool *) palloc(nitems * sizeof(bool));
6494
6495
  /* Loop over source data */
6496
0
  arraydataptr = ARR_DATA_PTR(array);
6497
0
  bitmap = ARR_NULLBITMAP(array);
6498
0
  bitmask = 1;
6499
0
  hasnulls = false;
6500
0
  nresult = 0;
6501
6502
0
  for (i = 0; i < nitems; i++)
6503
0
  {
6504
0
    Datum   elt;
6505
0
    bool    isNull;
6506
0
    bool    oprresult;
6507
0
    bool    skip = false;
6508
6509
    /* Get source element, checking for NULL */
6510
0
    if (bitmap && (*bitmap & bitmask) == 0)
6511
0
    {
6512
0
      isNull = true;
6513
      /* If searching for NULL, we have a match */
6514
0
      if (search_isnull)
6515
0
      {
6516
0
        if (remove)
6517
0
        {
6518
0
          skip = true;
6519
0
          changed = true;
6520
0
        }
6521
0
        else if (!replace_isnull)
6522
0
        {
6523
0
          values[nresult] = replace;
6524
0
          isNull = false;
6525
0
          changed = true;
6526
0
        }
6527
0
      }
6528
0
    }
6529
0
    else
6530
0
    {
6531
0
      isNull = false;
6532
0
      elt = fetch_att(arraydataptr, typbyval, typlen);
6533
0
      arraydataptr = att_addlength_datum(arraydataptr, typlen, elt);
6534
0
      arraydataptr = (char *) att_nominal_alignby(arraydataptr, typalignby);
6535
6536
0
      if (search_isnull)
6537
0
      {
6538
        /* no match possible, keep element */
6539
0
        values[nresult] = elt;
6540
0
      }
6541
0
      else
6542
0
      {
6543
        /*
6544
         * Apply the operator to the element pair; treat NULL as false
6545
         */
6546
0
        locfcinfo->args[0].value = elt;
6547
0
        locfcinfo->args[0].isnull = false;
6548
0
        locfcinfo->args[1].value = search;
6549
0
        locfcinfo->args[1].isnull = false;
6550
0
        locfcinfo->isnull = false;
6551
0
        oprresult = DatumGetBool(FunctionCallInvoke(locfcinfo));
6552
0
        if (locfcinfo->isnull || !oprresult)
6553
0
        {
6554
          /* no match, keep element */
6555
0
          values[nresult] = elt;
6556
0
        }
6557
0
        else
6558
0
        {
6559
          /* match, so replace or delete */
6560
0
          changed = true;
6561
0
          if (remove)
6562
0
            skip = true;
6563
0
          else
6564
0
          {
6565
0
            values[nresult] = replace;
6566
0
            isNull = replace_isnull;
6567
0
          }
6568
0
        }
6569
0
      }
6570
0
    }
6571
6572
0
    if (!skip)
6573
0
    {
6574
0
      nulls[nresult] = isNull;
6575
0
      if (isNull)
6576
0
        hasnulls = true;
6577
0
      else
6578
0
      {
6579
        /* Update total result size */
6580
0
        nbytes = att_addlength_datum(nbytes, typlen, values[nresult]);
6581
0
        nbytes = att_nominal_alignby(nbytes, typalignby);
6582
        /* check for overflow of total request */
6583
0
        if (!AllocSizeIsValid(nbytes))
6584
0
          ereport(ERROR,
6585
0
              (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6586
0
               errmsg("array size exceeds the maximum allowed (%zu)",
6587
0
                  MaxAllocSize)));
6588
0
      }
6589
0
      nresult++;
6590
0
    }
6591
6592
    /* advance bitmap pointer if any */
6593
0
    if (bitmap)
6594
0
    {
6595
0
      bitmask <<= 1;
6596
0
      if (bitmask == 0x100)
6597
0
      {
6598
0
        bitmap++;
6599
0
        bitmask = 1;
6600
0
      }
6601
0
    }
6602
0
  }
6603
6604
  /*
6605
   * If not changed just return the original array
6606
   */
6607
0
  if (!changed)
6608
0
  {
6609
0
    pfree(values);
6610
0
    pfree(nulls);
6611
0
    return array;
6612
0
  }
6613
6614
  /* If all elements were removed return an empty array */
6615
0
  if (nresult == 0)
6616
0
  {
6617
0
    pfree(values);
6618
0
    pfree(nulls);
6619
0
    return construct_empty_array(element_type);
6620
0
  }
6621
6622
  /* Allocate and initialize the result array */
6623
0
  if (hasnulls)
6624
0
  {
6625
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nresult);
6626
0
    nbytes += dataoffset;
6627
0
  }
6628
0
  else
6629
0
  {
6630
0
    dataoffset = 0;     /* marker for no null bitmap */
6631
0
    nbytes += ARR_OVERHEAD_NONULLS(ndim);
6632
0
  }
6633
0
  result = (ArrayType *) palloc0(nbytes);
6634
0
  SET_VARSIZE(result, nbytes);
6635
0
  result->ndim = ndim;
6636
0
  result->dataoffset = dataoffset;
6637
0
  result->elemtype = element_type;
6638
0
  memcpy(ARR_DIMS(result), ARR_DIMS(array), ndim * sizeof(int));
6639
0
  memcpy(ARR_LBOUND(result), ARR_LBOUND(array), ndim * sizeof(int));
6640
6641
0
  if (remove)
6642
0
  {
6643
    /* Adjust the result length */
6644
0
    ARR_DIMS(result)[0] = nresult;
6645
0
  }
6646
6647
  /* Insert data into result array */
6648
0
  CopyArrayEls(result,
6649
0
         values, nulls, nresult,
6650
0
         typlen, typbyval, typalign,
6651
0
         false);
6652
6653
0
  pfree(values);
6654
0
  pfree(nulls);
6655
6656
0
  return result;
6657
0
}
6658
6659
/*
6660
 * Remove any occurrences of an element from an array
6661
 *
6662
 * If used on a multi-dimensional array this will raise an error.
6663
 */
6664
Datum
6665
array_remove(PG_FUNCTION_ARGS)
6666
0
{
6667
0
  ArrayType  *array;
6668
0
  Datum   search = PG_GETARG_DATUM(1);
6669
0
  bool    search_isnull = PG_ARGISNULL(1);
6670
6671
0
  if (PG_ARGISNULL(0))
6672
0
    PG_RETURN_NULL();
6673
0
  array = PG_GETARG_ARRAYTYPE_P(0);
6674
6675
0
  array = array_replace_internal(array,
6676
0
                   search, search_isnull,
6677
0
                   (Datum) 0, true,
6678
0
                   true, PG_GET_COLLATION(),
6679
0
                   fcinfo);
6680
0
  PG_RETURN_ARRAYTYPE_P(array);
6681
0
}
6682
6683
/*
6684
 * Replace any occurrences of an element in an array
6685
 */
6686
Datum
6687
array_replace(PG_FUNCTION_ARGS)
6688
0
{
6689
0
  ArrayType  *array;
6690
0
  Datum   search = PG_GETARG_DATUM(1);
6691
0
  bool    search_isnull = PG_ARGISNULL(1);
6692
0
  Datum   replace = PG_GETARG_DATUM(2);
6693
0
  bool    replace_isnull = PG_ARGISNULL(2);
6694
6695
0
  if (PG_ARGISNULL(0))
6696
0
    PG_RETURN_NULL();
6697
0
  array = PG_GETARG_ARRAYTYPE_P(0);
6698
6699
0
  array = array_replace_internal(array,
6700
0
                   search, search_isnull,
6701
0
                   replace, replace_isnull,
6702
0
                   false, PG_GET_COLLATION(),
6703
0
                   fcinfo);
6704
0
  PG_RETURN_ARRAYTYPE_P(array);
6705
0
}
6706
6707
/*
6708
 * Implements width_bucket(anyelement, anyarray).
6709
 *
6710
 * 'thresholds' is an array containing lower bound values for each bucket;
6711
 * these must be sorted from smallest to largest, or bogus results will be
6712
 * produced.  If N thresholds are supplied, the output is from 0 to N:
6713
 * 0 is for inputs < first threshold, N is for inputs >= last threshold.
6714
 */
6715
Datum
6716
width_bucket_array(PG_FUNCTION_ARGS)
6717
0
{
6718
0
  Datum   operand = PG_GETARG_DATUM(0);
6719
0
  ArrayType  *thresholds = PG_GETARG_ARRAYTYPE_P(1);
6720
0
  Oid     collation = PG_GET_COLLATION();
6721
0
  Oid     element_type = ARR_ELEMTYPE(thresholds);
6722
0
  int     result;
6723
6724
  /* Check input */
6725
0
  if (ARR_NDIM(thresholds) > 1)
6726
0
    ereport(ERROR,
6727
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
6728
0
         errmsg("thresholds must be one-dimensional array")));
6729
6730
0
  if (array_contains_nulls(thresholds))
6731
0
    ereport(ERROR,
6732
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
6733
0
         errmsg("thresholds array must not contain NULLs")));
6734
6735
  /* We have a dedicated implementation for float8 data */
6736
0
  if (element_type == FLOAT8OID)
6737
0
    result = width_bucket_array_float8(operand, thresholds);
6738
0
  else
6739
0
  {
6740
0
    TypeCacheEntry *typentry;
6741
6742
    /* Cache information about the input type */
6743
0
    typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
6744
0
    if (typentry == NULL ||
6745
0
      typentry->type_id != element_type)
6746
0
    {
6747
0
      typentry = lookup_type_cache(element_type,
6748
0
                     TYPECACHE_CMP_PROC_FINFO);
6749
0
      if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
6750
0
        ereport(ERROR,
6751
0
            (errcode(ERRCODE_UNDEFINED_FUNCTION),
6752
0
             errmsg("could not identify a comparison function for type %s",
6753
0
                format_type_be(element_type))));
6754
0
      fcinfo->flinfo->fn_extra = typentry;
6755
0
    }
6756
6757
    /*
6758
     * We have separate implementation paths for fixed- and variable-width
6759
     * types, since indexing the array is a lot cheaper in the first case.
6760
     */
6761
0
    if (typentry->typlen > 0)
6762
0
      result = width_bucket_array_fixed(operand, thresholds,
6763
0
                        collation, typentry);
6764
0
    else
6765
0
      result = width_bucket_array_variable(operand, thresholds,
6766
0
                         collation, typentry);
6767
0
  }
6768
6769
  /* Avoid leaking memory when handed toasted input. */
6770
0
  PG_FREE_IF_COPY(thresholds, 1);
6771
6772
0
  PG_RETURN_INT32(result);
6773
0
}
6774
6775
/*
6776
 * width_bucket_array for float8 data.
6777
 */
6778
static int
6779
width_bucket_array_float8(Datum operand, ArrayType *thresholds)
6780
0
{
6781
0
  float8    op = DatumGetFloat8(operand);
6782
0
  float8     *thresholds_data;
6783
0
  int     left;
6784
0
  int     right;
6785
6786
  /*
6787
   * Since we know the array contains no NULLs, we can just index it
6788
   * directly.
6789
   */
6790
0
  thresholds_data = (float8 *) ARR_DATA_PTR(thresholds);
6791
6792
0
  left = 0;
6793
0
  right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6794
6795
  /*
6796
   * If the probe value is a NaN, it's greater than or equal to all possible
6797
   * threshold values (including other NaNs), so we need not search.  Note
6798
   * that this would give the same result as searching even if the array
6799
   * contains multiple NaNs (as long as they're correctly sorted), since the
6800
   * loop logic will find the rightmost of multiple equal threshold values.
6801
   */
6802
0
  if (isnan(op))
6803
0
    return right;
6804
6805
  /* Find the bucket */
6806
0
  while (left < right)
6807
0
  {
6808
0
    int     mid = (left + right) / 2;
6809
6810
0
    if (isnan(thresholds_data[mid]) || op < thresholds_data[mid])
6811
0
      right = mid;
6812
0
    else
6813
0
      left = mid + 1;
6814
0
  }
6815
6816
0
  return left;
6817
0
}
6818
6819
/*
6820
 * width_bucket_array for generic fixed-width data types.
6821
 */
6822
static int
6823
width_bucket_array_fixed(Datum operand,
6824
             ArrayType *thresholds,
6825
             Oid collation,
6826
             TypeCacheEntry *typentry)
6827
0
{
6828
0
  LOCAL_FCINFO(locfcinfo, 2);
6829
0
  char     *thresholds_data;
6830
0
  int     typlen = typentry->typlen;
6831
0
  bool    typbyval = typentry->typbyval;
6832
0
  int     left;
6833
0
  int     right;
6834
6835
  /*
6836
   * Since we know the array contains no NULLs, we can just index it
6837
   * directly.
6838
   */
6839
0
  thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6840
6841
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6842
0
               collation, NULL, NULL);
6843
6844
  /* Find the bucket */
6845
0
  left = 0;
6846
0
  right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6847
0
  while (left < right)
6848
0
  {
6849
0
    int     mid = (left + right) / 2;
6850
0
    char     *ptr;
6851
0
    int32   cmpresult;
6852
6853
0
    ptr = thresholds_data + mid * typlen;
6854
6855
0
    locfcinfo->args[0].value = operand;
6856
0
    locfcinfo->args[0].isnull = false;
6857
0
    locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6858
0
    locfcinfo->args[1].isnull = false;
6859
6860
0
    cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6861
6862
    /* We don't expect comparison support functions to return null */
6863
0
    Assert(!locfcinfo->isnull);
6864
6865
0
    if (cmpresult < 0)
6866
0
      right = mid;
6867
0
    else
6868
0
      left = mid + 1;
6869
0
  }
6870
6871
0
  return left;
6872
0
}
6873
6874
/*
6875
 * width_bucket_array for generic variable-width data types.
6876
 */
6877
static int
6878
width_bucket_array_variable(Datum operand,
6879
              ArrayType *thresholds,
6880
              Oid collation,
6881
              TypeCacheEntry *typentry)
6882
0
{
6883
0
  LOCAL_FCINFO(locfcinfo, 2);
6884
0
  char     *thresholds_data;
6885
0
  int     typlen = typentry->typlen;
6886
0
  bool    typbyval = typentry->typbyval;
6887
0
  char    typalign = typentry->typalign;
6888
0
  uint8   typalignby = typalign_to_alignby(typalign);
6889
0
  int     left;
6890
0
  int     right;
6891
6892
0
  thresholds_data = (char *) ARR_DATA_PTR(thresholds);
6893
6894
0
  InitFunctionCallInfoData(*locfcinfo, &typentry->cmp_proc_finfo, 2,
6895
0
               collation, NULL, NULL);
6896
6897
  /* Find the bucket */
6898
0
  left = 0;
6899
0
  right = ArrayGetNItems(ARR_NDIM(thresholds), ARR_DIMS(thresholds));
6900
0
  while (left < right)
6901
0
  {
6902
0
    int     mid = (left + right) / 2;
6903
0
    char     *ptr;
6904
0
    int     i;
6905
0
    int32   cmpresult;
6906
6907
    /* Locate mid'th array element by advancing from left element */
6908
0
    ptr = thresholds_data;
6909
0
    for (i = left; i < mid; i++)
6910
0
    {
6911
0
      ptr = att_addlength_pointer(ptr, typlen, ptr);
6912
0
      ptr = (char *) att_nominal_alignby(ptr, typalignby);
6913
0
    }
6914
6915
0
    locfcinfo->args[0].value = operand;
6916
0
    locfcinfo->args[0].isnull = false;
6917
0
    locfcinfo->args[1].value = fetch_att(ptr, typbyval, typlen);
6918
0
    locfcinfo->args[1].isnull = false;
6919
6920
0
    cmpresult = DatumGetInt32(FunctionCallInvoke(locfcinfo));
6921
6922
    /* We don't expect comparison support functions to return null */
6923
0
    Assert(!locfcinfo->isnull);
6924
6925
0
    if (cmpresult < 0)
6926
0
      right = mid;
6927
0
    else
6928
0
    {
6929
0
      left = mid + 1;
6930
6931
      /*
6932
       * Move the thresholds pointer to match new "left" index, so we
6933
       * don't have to seek over those elements again.  This trick
6934
       * ensures we do only O(N) array indexing work, not O(N^2).
6935
       */
6936
0
      ptr = att_addlength_pointer(ptr, typlen, ptr);
6937
0
      thresholds_data = (char *) att_nominal_alignby(ptr, typalignby);
6938
0
    }
6939
0
  }
6940
6941
0
  return left;
6942
0
}
6943
6944
/*
6945
 * Trim the last N elements from an array by building an appropriate slice.
6946
 * Only the first dimension is trimmed.
6947
 */
6948
Datum
6949
trim_array(PG_FUNCTION_ARGS)
6950
0
{
6951
0
  ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
6952
0
  int     n = PG_GETARG_INT32(1);
6953
0
  int     array_length = (ARR_NDIM(v) > 0) ? ARR_DIMS(v)[0] : 0;
6954
0
  int16   elmlen;
6955
0
  bool    elmbyval;
6956
0
  char    elmalign;
6957
0
  int     lower[MAXDIM];
6958
0
  int     upper[MAXDIM];
6959
0
  bool    lowerProvided[MAXDIM];
6960
0
  bool    upperProvided[MAXDIM];
6961
0
  Datum   result;
6962
6963
  /* Per spec, throw an error if out of bounds */
6964
0
  if (n < 0 || n > array_length)
6965
0
    ereport(ERROR,
6966
0
        (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
6967
0
         errmsg("number of elements to trim must be between 0 and %d",
6968
0
            array_length)));
6969
6970
  /* Set all the bounds as unprovided except the first upper bound */
6971
0
  memset(lowerProvided, false, sizeof(lowerProvided));
6972
0
  memset(upperProvided, false, sizeof(upperProvided));
6973
0
  if (ARR_NDIM(v) > 0)
6974
0
  {
6975
0
    upper[0] = ARR_LBOUND(v)[0] + array_length - n - 1;
6976
0
    upperProvided[0] = true;
6977
0
  }
6978
6979
  /* Fetch the needed information about the element type */
6980
0
  get_typlenbyvalalign(ARR_ELEMTYPE(v), &elmlen, &elmbyval, &elmalign);
6981
6982
  /* Get the slice */
6983
0
  result = array_get_slice(PointerGetDatum(v), 1,
6984
0
               upper, lower, upperProvided, lowerProvided,
6985
0
               -1, elmlen, elmbyval, elmalign);
6986
6987
0
  PG_RETURN_DATUM(result);
6988
0
}