~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/utils/adt/arrayfuncs.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * arrayfuncs.c
 
4
 *        Support functions for arrays.
 
5
 *
 
6
 * Portions Copyright (c) 1996-2011, 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
 
 
19
#include "funcapi.h"
 
20
#include "libpq/pqformat.h"
 
21
#include "parser/parse_coerce.h"
 
22
#include "utils/array.h"
 
23
#include "utils/builtins.h"
 
24
#include "utils/datum.h"
 
25
#include "utils/lsyscache.h"
 
26
#include "utils/memutils.h"
 
27
#include "utils/typcache.h"
 
28
 
 
29
 
 
30
/*
 
31
 * GUC parameter
 
32
 */
 
33
bool            Array_nulls = true;
 
34
 
 
35
/*
 
36
 * Local definitions
 
37
 */
 
38
#define ASSGN    "="
 
39
 
 
40
typedef enum
 
41
{
 
42
        ARRAY_NO_LEVEL,
 
43
        ARRAY_LEVEL_STARTED,
 
44
        ARRAY_ELEM_STARTED,
 
45
        ARRAY_ELEM_COMPLETED,
 
46
        ARRAY_QUOTED_ELEM_STARTED,
 
47
        ARRAY_QUOTED_ELEM_COMPLETED,
 
48
        ARRAY_ELEM_DELIMITED,
 
49
        ARRAY_LEVEL_COMPLETED,
 
50
        ARRAY_LEVEL_DELIMITED
 
51
} ArrayParseState;
 
52
 
 
53
/* Working state for array_iterate() */
 
54
typedef struct ArrayIteratorData
 
55
{
 
56
        /* basic info about the array, set up during array_create_iterator() */
 
57
        ArrayType  *arr;                        /* array we're iterating through */
 
58
        bits8      *nullbitmap;         /* its null bitmap, if any */
 
59
        int                     nitems;                 /* total number of elements in array */
 
60
        int16           typlen;                 /* element type's length */
 
61
        bool            typbyval;               /* element type's byval property */
 
62
        char            typalign;               /* element type's align property */
 
63
 
 
64
        /* information about the requested slice size */
 
65
        int                     slice_ndim;             /* slice dimension, or 0 if not slicing */
 
66
        int                     slice_len;              /* number of elements per slice */
 
67
        int                *slice_dims;         /* slice dims array */
 
68
        int                *slice_lbound;       /* slice lbound array */
 
69
        Datum      *slice_values;       /* workspace of length slice_len */
 
70
        bool       *slice_nulls;        /* workspace of length slice_len */
 
71
 
 
72
        /* current position information, updated on each iteration */
 
73
        char       *data_ptr;           /* our current position in the array */
 
74
        int                     current_item;   /* the item # we're at in the array */
 
75
}       ArrayIteratorData;
 
76
 
 
77
static bool array_isspace(char ch);
 
78
static int      ArrayCount(const char *str, int *dim, char typdelim);
 
79
static void ReadArrayStr(char *arrayStr, const char *origStr,
 
80
                         int nitems, int ndim, int *dim,
 
81
                         FmgrInfo *inputproc, Oid typioparam, int32 typmod,
 
82
                         char typdelim,
 
83
                         int typlen, bool typbyval, char typalign,
 
84
                         Datum *values, bool *nulls,
 
85
                         bool *hasnulls, int32 *nbytes);
 
86
static void ReadArrayBinary(StringInfo buf, int nitems,
 
87
                                FmgrInfo *receiveproc, Oid typioparam, int32 typmod,
 
88
                                int typlen, bool typbyval, char typalign,
 
89
                                Datum *values, bool *nulls,
 
90
                                bool *hasnulls, int32 *nbytes);
 
91
static void CopyArrayEls(ArrayType *array,
 
92
                         Datum *values, bool *nulls, int nitems,
 
93
                         int typlen, bool typbyval, char typalign,
 
94
                         bool freedata);
 
95
static bool array_get_isnull(const bits8 *nullbitmap, int offset);
 
96
static void array_set_isnull(bits8 *nullbitmap, int offset, bool isNull);
 
97
static Datum ArrayCast(char *value, bool byval, int len);
 
98
static int ArrayCastAndSet(Datum src,
 
99
                                int typlen, bool typbyval, char typalign,
 
100
                                char *dest);
 
101
static char *array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 
102
                   int typlen, bool typbyval, char typalign);
 
103
static int array_nelems_size(char *ptr, int offset, bits8 *nullbitmap,
 
104
                                  int nitems, int typlen, bool typbyval, char typalign);
 
105
static int array_copy(char *destptr, int nitems,
 
106
                   char *srcptr, int offset, bits8 *nullbitmap,
 
107
                   int typlen, bool typbyval, char typalign);
 
108
static int array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 
109
                                 int ndim, int *dim, int *lb,
 
110
                                 int *st, int *endp,
 
111
                                 int typlen, bool typbyval, char typalign);
 
112
static void array_extract_slice(ArrayType *newarray,
 
113
                                        int ndim, int *dim, int *lb,
 
114
                                        char *arraydataptr, bits8 *arraynullsptr,
 
115
                                        int *st, int *endp,
 
116
                                        int typlen, bool typbyval, char typalign);
 
117
static void array_insert_slice(ArrayType *destArray, ArrayType *origArray,
 
118
                                   ArrayType *srcArray,
 
119
                                   int ndim, int *dim, int *lb,
 
120
                                   int *st, int *endp,
 
121
                                   int typlen, bool typbyval, char typalign);
 
122
static int      array_cmp(FunctionCallInfo fcinfo);
 
123
static ArrayType *create_array_envelope(int ndims, int *dimv, int *lbv, int nbytes,
 
124
                                          Oid elmtype, int dataoffset);
 
125
static ArrayType *array_fill_internal(ArrayType *dims, ArrayType *lbs,
 
126
                                        Datum value, bool isnull, Oid elmtype,
 
127
                                        FunctionCallInfo fcinfo);
 
128
 
 
129
 
 
130
/*
 
131
 * array_in :
 
132
 *                converts an array from the external format in "string" to
 
133
 *                its internal format.
 
134
 *
 
135
 * return value :
 
136
 *                the internal representation of the input array
 
137
 */
 
138
Datum
 
139
array_in(PG_FUNCTION_ARGS)
 
140
{
 
141
        char       *string = PG_GETARG_CSTRING(0);      /* external form */
 
142
        Oid                     element_type = PG_GETARG_OID(1);                /* type of an array
 
143
                                                                                                                 * element */
 
144
        int32           typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
 
145
        int                     typlen;
 
146
        bool            typbyval;
 
147
        char            typalign;
 
148
        char            typdelim;
 
149
        Oid                     typioparam;
 
150
        char       *string_save,
 
151
                           *p;
 
152
        int                     i,
 
153
                                nitems;
 
154
        Datum      *dataPtr;
 
155
        bool       *nullsPtr;
 
156
        bool            hasnulls;
 
157
        int32           nbytes;
 
158
        int32           dataoffset;
 
159
        ArrayType  *retval;
 
160
        int                     ndim,
 
161
                                dim[MAXDIM],
 
162
                                lBound[MAXDIM];
 
163
        ArrayMetaState *my_extra;
 
164
 
 
165
        /*
 
166
         * We arrange to look up info about element type, including its input
 
167
         * conversion proc, only once per series of calls, assuming the element
 
168
         * type doesn't change underneath us.
 
169
         */
 
170
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
171
        if (my_extra == NULL)
 
172
        {
 
173
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
174
                                                                                                          sizeof(ArrayMetaState));
 
175
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
176
                my_extra->element_type = ~element_type;
 
177
        }
 
178
 
 
179
        if (my_extra->element_type != element_type)
 
180
        {
 
181
                /*
 
182
                 * Get info about element type, including its input conversion proc
 
183
                 */
 
184
                get_type_io_data(element_type, IOFunc_input,
 
185
                                                 &my_extra->typlen, &my_extra->typbyval,
 
186
                                                 &my_extra->typalign, &my_extra->typdelim,
 
187
                                                 &my_extra->typioparam, &my_extra->typiofunc);
 
188
                fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 
189
                                          fcinfo->flinfo->fn_mcxt);
 
190
                my_extra->element_type = element_type;
 
191
        }
 
192
        typlen = my_extra->typlen;
 
193
        typbyval = my_extra->typbyval;
 
194
        typalign = my_extra->typalign;
 
195
        typdelim = my_extra->typdelim;
 
196
        typioparam = my_extra->typioparam;
 
197
 
 
198
        /* Make a modifiable copy of the input */
 
199
        string_save = pstrdup(string);
 
200
 
 
201
        /*
 
202
         * If the input string starts with dimension info, read and use that.
 
203
         * Otherwise, we require the input to be in curly-brace style, and we
 
204
         * prescan the input to determine dimensions.
 
205
         *
 
206
         * Dimension info takes the form of one or more [n] or [m:n] items. The
 
207
         * outer loop iterates once per dimension item.
 
208
         */
 
209
        p = string_save;
 
210
        ndim = 0;
 
211
        for (;;)
 
212
        {
 
213
                char       *q;
 
214
                int                     ub;
 
215
 
 
216
                /*
 
217
                 * Note: we currently allow whitespace between, but not within,
 
218
                 * dimension items.
 
219
                 */
 
220
                while (array_isspace(*p))
 
221
                        p++;
 
222
                if (*p != '[')
 
223
                        break;                          /* no more dimension items */
 
224
                p++;
 
225
                if (ndim >= MAXDIM)
 
226
                        ereport(ERROR,
 
227
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
228
                                         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 
229
                                                        ndim + 1, MAXDIM)));
 
230
 
 
231
                for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
 
232
                if (q == p)                             /* no digits? */
 
233
                        ereport(ERROR,
 
234
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
235
                                         errmsg("missing dimension value")));
 
236
 
 
237
                if (*q == ':')
 
238
                {
 
239
                        /* [m:n] format */
 
240
                        *q = '\0';
 
241
                        lBound[ndim] = atoi(p);
 
242
                        p = q + 1;
 
243
                        for (q = p; isdigit((unsigned char) *q) || (*q == '-') || (*q == '+'); q++);
 
244
                        if (q == p)                     /* no digits? */
 
245
                                ereport(ERROR,
 
246
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
247
                                                 errmsg("missing dimension value")));
 
248
                }
 
249
                else
 
250
                {
 
251
                        /* [n] format */
 
252
                        lBound[ndim] = 1;
 
253
                }
 
254
                if (*q != ']')
 
255
                        ereport(ERROR,
 
256
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
257
                                         errmsg("missing \"]\" in array dimensions")));
 
258
 
 
259
                *q = '\0';
 
260
                ub = atoi(p);
 
261
                p = q + 1;
 
262
                if (ub < lBound[ndim])
 
263
                        ereport(ERROR,
 
264
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
265
                                         errmsg("upper bound cannot be less than lower bound")));
 
266
 
 
267
                dim[ndim] = ub - lBound[ndim] + 1;
 
268
                ndim++;
 
269
        }
 
270
 
 
271
        if (ndim == 0)
 
272
        {
 
273
                /* No array dimensions, so intuit dimensions from brace structure */
 
274
                if (*p != '{')
 
275
                        ereport(ERROR,
 
276
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
277
                                         errmsg("array value must start with \"{\" or dimension information")));
 
278
                ndim = ArrayCount(p, dim, typdelim);
 
279
                for (i = 0; i < ndim; i++)
 
280
                        lBound[i] = 1;
 
281
        }
 
282
        else
 
283
        {
 
284
                int                     ndim_braces,
 
285
                                        dim_braces[MAXDIM];
 
286
 
 
287
                /* If array dimensions are given, expect '=' operator */
 
288
                if (strncmp(p, ASSGN, strlen(ASSGN)) != 0)
 
289
                        ereport(ERROR,
 
290
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
291
                                         errmsg("missing assignment operator")));
 
292
                p += strlen(ASSGN);
 
293
                while (array_isspace(*p))
 
294
                        p++;
 
295
 
 
296
                /*
 
297
                 * intuit dimensions from brace structure -- it better match what we
 
298
                 * were given
 
299
                 */
 
300
                if (*p != '{')
 
301
                        ereport(ERROR,
 
302
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
303
                                         errmsg("array value must start with \"{\" or dimension information")));
 
304
                ndim_braces = ArrayCount(p, dim_braces, typdelim);
 
305
                if (ndim_braces != ndim)
 
306
                        ereport(ERROR,
 
307
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
308
                                errmsg("array dimensions incompatible with array literal")));
 
309
                for (i = 0; i < ndim; ++i)
 
310
                {
 
311
                        if (dim[i] != dim_braces[i])
 
312
                                ereport(ERROR,
 
313
                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
314
                                errmsg("array dimensions incompatible with array literal")));
 
315
                }
 
316
        }
 
317
 
 
318
#ifdef ARRAYDEBUG
 
319
        printf("array_in- ndim %d (", ndim);
 
320
        for (i = 0; i < ndim; i++)
 
321
        {
 
322
                printf(" %d", dim[i]);
 
323
        };
 
324
        printf(") for %s\n", string);
 
325
#endif
 
326
 
 
327
        /* This checks for overflow of the array dimensions */
 
328
        nitems = ArrayGetNItems(ndim, dim);
 
329
        /* Empty array? */
 
330
        if (nitems == 0)
 
331
                PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
 
332
 
 
333
        dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
 
334
        nullsPtr = (bool *) palloc(nitems * sizeof(bool));
 
335
        ReadArrayStr(p, string,
 
336
                                 nitems, ndim, dim,
 
337
                                 &my_extra->proc, typioparam, typmod,
 
338
                                 typdelim,
 
339
                                 typlen, typbyval, typalign,
 
340
                                 dataPtr, nullsPtr,
 
341
                                 &hasnulls, &nbytes);
 
342
        if (hasnulls)
 
343
        {
 
344
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
 
345
                nbytes += dataoffset;
 
346
        }
 
347
        else
 
348
        {
 
349
                dataoffset = 0;                 /* marker for no null bitmap */
 
350
                nbytes += ARR_OVERHEAD_NONULLS(ndim);
 
351
        }
 
352
        retval = (ArrayType *) palloc0(nbytes);
 
353
        SET_VARSIZE(retval, nbytes);
 
354
        retval->ndim = ndim;
 
355
        retval->dataoffset = dataoffset;
 
356
 
 
357
        /*
 
358
         * This comes from the array's pg_type.typelem (which points to the base
 
359
         * data type's pg_type.oid) and stores system oids in user tables. This
 
360
         * oid must be preserved by binary upgrades.
 
361
         */
 
362
        retval->elemtype = element_type;
 
363
        memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
 
364
        memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
 
365
 
 
366
        CopyArrayEls(retval,
 
367
                                 dataPtr, nullsPtr, nitems,
 
368
                                 typlen, typbyval, typalign,
 
369
                                 true);
 
370
 
 
371
        pfree(dataPtr);
 
372
        pfree(nullsPtr);
 
373
        pfree(string_save);
 
374
 
 
375
        PG_RETURN_ARRAYTYPE_P(retval);
 
376
}
 
377
 
 
378
/*
 
379
 * array_isspace() --- a non-locale-dependent isspace()
 
380
 *
 
381
 * We used to use isspace() for parsing array values, but that has
 
382
 * undesirable results: an array value might be silently interpreted
 
383
 * differently depending on the locale setting.  Now we just hard-wire
 
384
 * the traditional ASCII definition of isspace().
 
385
 */
 
386
static bool
 
387
array_isspace(char ch)
 
388
{
 
389
        if (ch == ' ' ||
 
390
                ch == '\t' ||
 
391
                ch == '\n' ||
 
392
                ch == '\r' ||
 
393
                ch == '\v' ||
 
394
                ch == '\f')
 
395
                return true;
 
396
        return false;
 
397
}
 
398
 
 
399
/*
 
400
 * ArrayCount
 
401
 *       Determines the dimensions for an array string.
 
402
 *
 
403
 * Returns number of dimensions as function result.  The axis lengths are
 
404
 * returned in dim[], which must be of size MAXDIM.
 
405
 */
 
406
static int
 
407
ArrayCount(const char *str, int *dim, char typdelim)
 
408
{
 
409
        int                     nest_level = 0,
 
410
                                i;
 
411
        int                     ndim = 1,
 
412
                                temp[MAXDIM],
 
413
                                nelems[MAXDIM],
 
414
                                nelems_last[MAXDIM];
 
415
        bool            in_quotes = false;
 
416
        bool            eoArray = false;
 
417
        bool            empty_array = true;
 
418
        const char *ptr;
 
419
        ArrayParseState parse_state = ARRAY_NO_LEVEL;
 
420
 
 
421
        for (i = 0; i < MAXDIM; ++i)
 
422
        {
 
423
                temp[i] = dim[i] = 0;
 
424
                nelems_last[i] = nelems[i] = 1;
 
425
        }
 
426
 
 
427
        ptr = str;
 
428
        while (!eoArray)
 
429
        {
 
430
                bool            itemdone = false;
 
431
 
 
432
                while (!itemdone)
 
433
                {
 
434
                        if (parse_state == ARRAY_ELEM_STARTED ||
 
435
                                parse_state == ARRAY_QUOTED_ELEM_STARTED)
 
436
                                empty_array = false;
 
437
 
 
438
                        switch (*ptr)
 
439
                        {
 
440
                                case '\0':
 
441
                                        /* Signal a premature end of the string */
 
442
                                        ereport(ERROR,
 
443
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
444
                                                         errmsg("malformed array literal: \"%s\"", str)));
 
445
                                        break;
 
446
                                case '\\':
 
447
 
 
448
                                        /*
 
449
                                         * An escape must be after a level start, after an element
 
450
                                         * start, or after an element delimiter. In any case we
 
451
                                         * now must be past an element start.
 
452
                                         */
 
453
                                        if (parse_state != ARRAY_LEVEL_STARTED &&
 
454
                                                parse_state != ARRAY_ELEM_STARTED &&
 
455
                                                parse_state != ARRAY_QUOTED_ELEM_STARTED &&
 
456
                                                parse_state != ARRAY_ELEM_DELIMITED)
 
457
                                                ereport(ERROR,
 
458
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
459
                                                        errmsg("malformed array literal: \"%s\"", str)));
 
460
                                        if (parse_state != ARRAY_QUOTED_ELEM_STARTED)
 
461
                                                parse_state = ARRAY_ELEM_STARTED;
 
462
                                        /* skip the escaped character */
 
463
                                        if (*(ptr + 1))
 
464
                                                ptr++;
 
465
                                        else
 
466
                                                ereport(ERROR,
 
467
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
468
                                                        errmsg("malformed array literal: \"%s\"", str)));
 
469
                                        break;
 
470
                                case '\"':
 
471
 
 
472
                                        /*
 
473
                                         * A quote must be after a level start, after a quoted
 
474
                                         * element start, or after an element delimiter. In any
 
475
                                         * case we now must be past an element start.
 
476
                                         */
 
477
                                        if (parse_state != ARRAY_LEVEL_STARTED &&
 
478
                                                parse_state != ARRAY_QUOTED_ELEM_STARTED &&
 
479
                                                parse_state != ARRAY_ELEM_DELIMITED)
 
480
                                                ereport(ERROR,
 
481
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
482
                                                        errmsg("malformed array literal: \"%s\"", str)));
 
483
                                        in_quotes = !in_quotes;
 
484
                                        if (in_quotes)
 
485
                                                parse_state = ARRAY_QUOTED_ELEM_STARTED;
 
486
                                        else
 
487
                                                parse_state = ARRAY_QUOTED_ELEM_COMPLETED;
 
488
                                        break;
 
489
                                case '{':
 
490
                                        if (!in_quotes)
 
491
                                        {
 
492
                                                /*
 
493
                                                 * A left brace can occur if no nesting has occurred
 
494
                                                 * yet, after a level start, or after a level
 
495
                                                 * delimiter.
 
496
                                                 */
 
497
                                                if (parse_state != ARRAY_NO_LEVEL &&
 
498
                                                        parse_state != ARRAY_LEVEL_STARTED &&
 
499
                                                        parse_state != ARRAY_LEVEL_DELIMITED)
 
500
                                                        ereport(ERROR,
 
501
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
502
                                                        errmsg("malformed array literal: \"%s\"", str)));
 
503
                                                parse_state = ARRAY_LEVEL_STARTED;
 
504
                                                if (nest_level >= MAXDIM)
 
505
                                                        ereport(ERROR,
 
506
                                                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
507
                                                                         errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 
508
                                                                                        nest_level + 1, MAXDIM)));
 
509
                                                temp[nest_level] = 0;
 
510
                                                nest_level++;
 
511
                                                if (ndim < nest_level)
 
512
                                                        ndim = nest_level;
 
513
                                        }
 
514
                                        break;
 
515
                                case '}':
 
516
                                        if (!in_quotes)
 
517
                                        {
 
518
                                                /*
 
519
                                                 * A right brace can occur after an element start, an
 
520
                                                 * element completion, a quoted element completion, or
 
521
                                                 * a level completion.
 
522
                                                 */
 
523
                                                if (parse_state != ARRAY_ELEM_STARTED &&
 
524
                                                        parse_state != ARRAY_ELEM_COMPLETED &&
 
525
                                                        parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
 
526
                                                        parse_state != ARRAY_LEVEL_COMPLETED &&
 
527
                                                        !(nest_level == 1 && parse_state == ARRAY_LEVEL_STARTED))
 
528
                                                        ereport(ERROR,
 
529
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
530
                                                        errmsg("malformed array literal: \"%s\"", str)));
 
531
                                                parse_state = ARRAY_LEVEL_COMPLETED;
 
532
                                                if (nest_level == 0)
 
533
                                                        ereport(ERROR,
 
534
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
535
                                                        errmsg("malformed array literal: \"%s\"", str)));
 
536
                                                nest_level--;
 
537
 
 
538
                                                if ((nelems_last[nest_level] != 1) &&
 
539
                                                        (nelems[nest_level] != nelems_last[nest_level]))
 
540
                                                        ereport(ERROR,
 
541
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
542
                                                                errmsg("multidimensional arrays must have "
 
543
                                                                           "array expressions with matching "
 
544
                                                                           "dimensions")));
 
545
                                                nelems_last[nest_level] = nelems[nest_level];
 
546
                                                nelems[nest_level] = 1;
 
547
                                                if (nest_level == 0)
 
548
                                                        eoArray = itemdone = true;
 
549
                                                else
 
550
                                                {
 
551
                                                        /*
 
552
                                                         * We don't set itemdone here; see comments in
 
553
                                                         * ReadArrayStr
 
554
                                                         */
 
555
                                                        temp[nest_level - 1]++;
 
556
                                                }
 
557
                                        }
 
558
                                        break;
 
559
                                default:
 
560
                                        if (!in_quotes)
 
561
                                        {
 
562
                                                if (*ptr == typdelim)
 
563
                                                {
 
564
                                                        /*
 
565
                                                         * Delimiters can occur after an element start, an
 
566
                                                         * element completion, a quoted element
 
567
                                                         * completion, or a level completion.
 
568
                                                         */
 
569
                                                        if (parse_state != ARRAY_ELEM_STARTED &&
 
570
                                                                parse_state != ARRAY_ELEM_COMPLETED &&
 
571
                                                                parse_state != ARRAY_QUOTED_ELEM_COMPLETED &&
 
572
                                                                parse_state != ARRAY_LEVEL_COMPLETED)
 
573
                                                                ereport(ERROR,
 
574
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
575
                                                                 errmsg("malformed array literal: \"%s\"", str)));
 
576
                                                        if (parse_state == ARRAY_LEVEL_COMPLETED)
 
577
                                                                parse_state = ARRAY_LEVEL_DELIMITED;
 
578
                                                        else
 
579
                                                                parse_state = ARRAY_ELEM_DELIMITED;
 
580
                                                        itemdone = true;
 
581
                                                        nelems[nest_level - 1]++;
 
582
                                                }
 
583
                                                else if (!array_isspace(*ptr))
 
584
                                                {
 
585
                                                        /*
 
586
                                                         * Other non-space characters must be after a
 
587
                                                         * level start, after an element start, or after
 
588
                                                         * an element delimiter. In any case we now must
 
589
                                                         * be past an element start.
 
590
                                                         */
 
591
                                                        if (parse_state != ARRAY_LEVEL_STARTED &&
 
592
                                                                parse_state != ARRAY_ELEM_STARTED &&
 
593
                                                                parse_state != ARRAY_ELEM_DELIMITED)
 
594
                                                                ereport(ERROR,
 
595
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
596
                                                                 errmsg("malformed array literal: \"%s\"", str)));
 
597
                                                        parse_state = ARRAY_ELEM_STARTED;
 
598
                                                }
 
599
                                        }
 
600
                                        break;
 
601
                        }
 
602
                        if (!itemdone)
 
603
                                ptr++;
 
604
                }
 
605
                temp[ndim - 1]++;
 
606
                ptr++;
 
607
        }
 
608
 
 
609
        /* only whitespace is allowed after the closing brace */
 
610
        while (*ptr)
 
611
        {
 
612
                if (!array_isspace(*ptr++))
 
613
                        ereport(ERROR,
 
614
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
615
                                         errmsg("malformed array literal: \"%s\"", str)));
 
616
        }
 
617
 
 
618
        /* special case for an empty array */
 
619
        if (empty_array)
 
620
                return 0;
 
621
 
 
622
        for (i = 0; i < ndim; ++i)
 
623
                dim[i] = temp[i];
 
624
 
 
625
        return ndim;
 
626
}
 
627
 
 
628
/*
 
629
 * ReadArrayStr :
 
630
 *       parses the array string pointed to by "arrayStr" and converts the values
 
631
 *       to internal format.  Unspecified elements are initialized to nulls.
 
632
 *       The array dimensions must already have been determined.
 
633
 *
 
634
 * Inputs:
 
635
 *      arrayStr: the string to parse.
 
636
 *                        CAUTION: the contents of "arrayStr" will be modified!
 
637
 *      origStr: the unmodified input string, used only in error messages.
 
638
 *      nitems: total number of array elements, as already determined.
 
639
 *      ndim: number of array dimensions
 
640
 *      dim[]: array axis lengths
 
641
 *      inputproc: type-specific input procedure for element datatype.
 
642
 *      typioparam, typmod: auxiliary values to pass to inputproc.
 
643
 *      typdelim: the value delimiter (type-specific).
 
644
 *      typlen, typbyval, typalign: storage parameters of element datatype.
 
645
 *
 
646
 * Outputs:
 
647
 *      values[]: filled with converted data values.
 
648
 *      nulls[]: filled with is-null markers.
 
649
 *      *hasnulls: set TRUE iff there are any null elements.
 
650
 *      *nbytes: set to total size of data area needed (including alignment
 
651
 *              padding but not including array header overhead).
 
652
 *
 
653
 * Note that values[] and nulls[] are allocated by the caller, and must have
 
654
 * nitems elements.
 
655
 */
 
656
static void
 
657
ReadArrayStr(char *arrayStr,
 
658
                         const char *origStr,
 
659
                         int nitems,
 
660
                         int ndim,
 
661
                         int *dim,
 
662
                         FmgrInfo *inputproc,
 
663
                         Oid typioparam,
 
664
                         int32 typmod,
 
665
                         char typdelim,
 
666
                         int typlen,
 
667
                         bool typbyval,
 
668
                         char typalign,
 
669
                         Datum *values,
 
670
                         bool *nulls,
 
671
                         bool *hasnulls,
 
672
                         int32 *nbytes)
 
673
{
 
674
        int                     i,
 
675
                                nest_level = 0;
 
676
        char       *srcptr;
 
677
        bool            in_quotes = false;
 
678
        bool            eoArray = false;
 
679
        bool            hasnull;
 
680
        int32           totbytes;
 
681
        int                     indx[MAXDIM],
 
682
                                prod[MAXDIM];
 
683
 
 
684
        mda_get_prod(ndim, dim, prod);
 
685
        MemSet(indx, 0, sizeof(indx));
 
686
 
 
687
        /* Initialize is-null markers to true */
 
688
        memset(nulls, true, nitems * sizeof(bool));
 
689
 
 
690
        /*
 
691
         * We have to remove " and \ characters to create a clean item value to
 
692
         * pass to the datatype input routine.  We overwrite each item value
 
693
         * in-place within arrayStr to do this.  srcptr is the current scan point,
 
694
         * and dstptr is where we are copying to.
 
695
         *
 
696
         * We also want to suppress leading and trailing unquoted whitespace. We
 
697
         * use the leadingspace flag to suppress leading space.  Trailing space is
 
698
         * tracked by using dstendptr to point to the last significant output
 
699
         * character.
 
700
         *
 
701
         * The error checking in this routine is mostly pro-forma, since we expect
 
702
         * that ArrayCount() already validated the string.
 
703
         */
 
704
        srcptr = arrayStr;
 
705
        while (!eoArray)
 
706
        {
 
707
                bool            itemdone = false;
 
708
                bool            leadingspace = true;
 
709
                bool            hasquoting = false;
 
710
                char       *itemstart;
 
711
                char       *dstptr;
 
712
                char       *dstendptr;
 
713
 
 
714
                i = -1;
 
715
                itemstart = dstptr = dstendptr = srcptr;
 
716
 
 
717
                while (!itemdone)
 
718
                {
 
719
                        switch (*srcptr)
 
720
                        {
 
721
                                case '\0':
 
722
                                        /* Signal a premature end of the string */
 
723
                                        ereport(ERROR,
 
724
                                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
725
                                                         errmsg("malformed array literal: \"%s\"",
 
726
                                                                        origStr)));
 
727
                                        break;
 
728
                                case '\\':
 
729
                                        /* Skip backslash, copy next character as-is. */
 
730
                                        srcptr++;
 
731
                                        if (*srcptr == '\0')
 
732
                                                ereport(ERROR,
 
733
                                                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
734
                                                                 errmsg("malformed array literal: \"%s\"",
 
735
                                                                                origStr)));
 
736
                                        *dstptr++ = *srcptr++;
 
737
                                        /* Treat the escaped character as non-whitespace */
 
738
                                        leadingspace = false;
 
739
                                        dstendptr = dstptr;
 
740
                                        hasquoting = true;      /* can't be a NULL marker */
 
741
                                        break;
 
742
                                case '\"':
 
743
                                        in_quotes = !in_quotes;
 
744
                                        if (in_quotes)
 
745
                                                leadingspace = false;
 
746
                                        else
 
747
                                        {
 
748
                                                /*
 
749
                                                 * Advance dstendptr when we exit in_quotes; this
 
750
                                                 * saves having to do it in all the other in_quotes
 
751
                                                 * cases.
 
752
                                                 */
 
753
                                                dstendptr = dstptr;
 
754
                                        }
 
755
                                        hasquoting = true;      /* can't be a NULL marker */
 
756
                                        srcptr++;
 
757
                                        break;
 
758
                                case '{':
 
759
                                        if (!in_quotes)
 
760
                                        {
 
761
                                                if (nest_level >= ndim)
 
762
                                                        ereport(ERROR,
 
763
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
764
                                                                errmsg("malformed array literal: \"%s\"",
 
765
                                                                           origStr)));
 
766
                                                nest_level++;
 
767
                                                indx[nest_level - 1] = 0;
 
768
                                                srcptr++;
 
769
                                        }
 
770
                                        else
 
771
                                                *dstptr++ = *srcptr++;
 
772
                                        break;
 
773
                                case '}':
 
774
                                        if (!in_quotes)
 
775
                                        {
 
776
                                                if (nest_level == 0)
 
777
                                                        ereport(ERROR,
 
778
                                                           (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
779
                                                                errmsg("malformed array literal: \"%s\"",
 
780
                                                                           origStr)));
 
781
                                                if (i == -1)
 
782
                                                        i = ArrayGetOffset0(ndim, indx, prod);
 
783
                                                indx[nest_level - 1] = 0;
 
784
                                                nest_level--;
 
785
                                                if (nest_level == 0)
 
786
                                                        eoArray = itemdone = true;
 
787
                                                else
 
788
                                                        indx[nest_level - 1]++;
 
789
                                                srcptr++;
 
790
                                        }
 
791
                                        else
 
792
                                                *dstptr++ = *srcptr++;
 
793
                                        break;
 
794
                                default:
 
795
                                        if (in_quotes)
 
796
                                                *dstptr++ = *srcptr++;
 
797
                                        else if (*srcptr == typdelim)
 
798
                                        {
 
799
                                                if (i == -1)
 
800
                                                        i = ArrayGetOffset0(ndim, indx, prod);
 
801
                                                itemdone = true;
 
802
                                                indx[ndim - 1]++;
 
803
                                                srcptr++;
 
804
                                        }
 
805
                                        else if (array_isspace(*srcptr))
 
806
                                        {
 
807
                                                /*
 
808
                                                 * If leading space, drop it immediately.  Else, copy
 
809
                                                 * but don't advance dstendptr.
 
810
                                                 */
 
811
                                                if (leadingspace)
 
812
                                                        srcptr++;
 
813
                                                else
 
814
                                                        *dstptr++ = *srcptr++;
 
815
                                        }
 
816
                                        else
 
817
                                        {
 
818
                                                *dstptr++ = *srcptr++;
 
819
                                                leadingspace = false;
 
820
                                                dstendptr = dstptr;
 
821
                                        }
 
822
                                        break;
 
823
                        }
 
824
                }
 
825
 
 
826
                Assert(dstptr < srcptr);
 
827
                *dstendptr = '\0';
 
828
 
 
829
                if (i < 0 || i >= nitems)
 
830
                        ereport(ERROR,
 
831
                                        (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
832
                                         errmsg("malformed array literal: \"%s\"",
 
833
                                                        origStr)));
 
834
 
 
835
                if (Array_nulls && !hasquoting &&
 
836
                        pg_strcasecmp(itemstart, "NULL") == 0)
 
837
                {
 
838
                        /* it's a NULL item */
 
839
                        values[i] = InputFunctionCall(inputproc, NULL,
 
840
                                                                                  typioparam, typmod);
 
841
                        nulls[i] = true;
 
842
                }
 
843
                else
 
844
                {
 
845
                        values[i] = InputFunctionCall(inputproc, itemstart,
 
846
                                                                                  typioparam, typmod);
 
847
                        nulls[i] = false;
 
848
                }
 
849
        }
 
850
 
 
851
        /*
 
852
         * Check for nulls, compute total data space needed
 
853
         */
 
854
        hasnull = false;
 
855
        totbytes = 0;
 
856
        for (i = 0; i < nitems; i++)
 
857
        {
 
858
                if (nulls[i])
 
859
                        hasnull = true;
 
860
                else
 
861
                {
 
862
                        /* let's just make sure data is not toasted */
 
863
                        if (typlen == -1)
 
864
                                values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 
865
                        totbytes = att_addlength_datum(totbytes, typlen, values[i]);
 
866
                        totbytes = att_align_nominal(totbytes, typalign);
 
867
                        /* check for overflow of total request */
 
868
                        if (!AllocSizeIsValid(totbytes))
 
869
                                ereport(ERROR,
 
870
                                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
871
                                                 errmsg("array size exceeds the maximum allowed (%d)",
 
872
                                                                (int) MaxAllocSize)));
 
873
                }
 
874
        }
 
875
        *hasnulls = hasnull;
 
876
        *nbytes = totbytes;
 
877
}
 
878
 
 
879
 
 
880
/*
 
881
 * Copy data into an array object from a temporary array of Datums.
 
882
 *
 
883
 * array: array object (with header fields already filled in)
 
884
 * values: array of Datums to be copied
 
885
 * nulls: array of is-null flags (can be NULL if no nulls)
 
886
 * nitems: number of Datums to be copied
 
887
 * typbyval, typlen, typalign: info about element datatype
 
888
 * freedata: if TRUE and element type is pass-by-ref, pfree data values
 
889
 * referenced by Datums after copying them.
 
890
 *
 
891
 * If the input data is of varlena type, the caller must have ensured that
 
892
 * the values are not toasted.  (Doing it here doesn't work since the
 
893
 * caller has already allocated space for the array...)
 
894
 */
 
895
static void
 
896
CopyArrayEls(ArrayType *array,
 
897
                         Datum *values,
 
898
                         bool *nulls,
 
899
                         int nitems,
 
900
                         int typlen,
 
901
                         bool typbyval,
 
902
                         char typalign,
 
903
                         bool freedata)
 
904
{
 
905
        char       *p = ARR_DATA_PTR(array);
 
906
        bits8      *bitmap = ARR_NULLBITMAP(array);
 
907
        int                     bitval = 0;
 
908
        int                     bitmask = 1;
 
909
        int                     i;
 
910
 
 
911
        if (typbyval)
 
912
                freedata = false;
 
913
 
 
914
        for (i = 0; i < nitems; i++)
 
915
        {
 
916
                if (nulls && nulls[i])
 
917
                {
 
918
                        if (!bitmap)            /* shouldn't happen */
 
919
                                elog(ERROR, "null array element where not supported");
 
920
                        /* bitmap bit stays 0 */
 
921
                }
 
922
                else
 
923
                {
 
924
                        bitval |= bitmask;
 
925
                        p += ArrayCastAndSet(values[i], typlen, typbyval, typalign, p);
 
926
                        if (freedata)
 
927
                                pfree(DatumGetPointer(values[i]));
 
928
                }
 
929
                if (bitmap)
 
930
                {
 
931
                        bitmask <<= 1;
 
932
                        if (bitmask == 0x100)
 
933
                        {
 
934
                                *bitmap++ = bitval;
 
935
                                bitval = 0;
 
936
                                bitmask = 1;
 
937
                        }
 
938
                }
 
939
        }
 
940
 
 
941
        if (bitmap && bitmask != 1)
 
942
                *bitmap = bitval;
 
943
}
 
944
 
 
945
/*
 
946
 * array_out :
 
947
 *                 takes the internal representation of an array and returns a string
 
948
 *                containing the array in its external format.
 
949
 */
 
950
Datum
 
951
array_out(PG_FUNCTION_ARGS)
 
952
{
 
953
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
954
        Oid                     element_type = ARR_ELEMTYPE(v);
 
955
        int                     typlen;
 
956
        bool            typbyval;
 
957
        char            typalign;
 
958
        char            typdelim;
 
959
        char       *p,
 
960
                           *tmp,
 
961
                           *retval,
 
962
                          **values,
 
963
                                dims_str[(MAXDIM * 33) + 2];
 
964
 
 
965
        /*
 
966
         * 33 per dim since we assume 15 digits per number + ':' +'[]'
 
967
         *
 
968
         * +2 allows for assignment operator + trailing null
 
969
         */
 
970
        bits8      *bitmap;
 
971
        int                     bitmask;
 
972
        bool       *needquotes,
 
973
                                needdims = false;
 
974
        int                     nitems,
 
975
                                overall_length,
 
976
                                i,
 
977
                                j,
 
978
                                k,
 
979
                                indx[MAXDIM];
 
980
        int                     ndim,
 
981
                           *dims,
 
982
                           *lb;
 
983
        ArrayMetaState *my_extra;
 
984
 
 
985
        /*
 
986
         * We arrange to look up info about element type, including its output
 
987
         * conversion proc, only once per series of calls, assuming the element
 
988
         * type doesn't change underneath us.
 
989
         */
 
990
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
991
        if (my_extra == NULL)
 
992
        {
 
993
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
994
                                                                                                          sizeof(ArrayMetaState));
 
995
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
996
                my_extra->element_type = ~element_type;
 
997
        }
 
998
 
 
999
        if (my_extra->element_type != element_type)
 
1000
        {
 
1001
                /*
 
1002
                 * Get info about element type, including its output conversion proc
 
1003
                 */
 
1004
                get_type_io_data(element_type, IOFunc_output,
 
1005
                                                 &my_extra->typlen, &my_extra->typbyval,
 
1006
                                                 &my_extra->typalign, &my_extra->typdelim,
 
1007
                                                 &my_extra->typioparam, &my_extra->typiofunc);
 
1008
                fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 
1009
                                          fcinfo->flinfo->fn_mcxt);
 
1010
                my_extra->element_type = element_type;
 
1011
        }
 
1012
        typlen = my_extra->typlen;
 
1013
        typbyval = my_extra->typbyval;
 
1014
        typalign = my_extra->typalign;
 
1015
        typdelim = my_extra->typdelim;
 
1016
 
 
1017
        ndim = ARR_NDIM(v);
 
1018
        dims = ARR_DIMS(v);
 
1019
        lb = ARR_LBOUND(v);
 
1020
        nitems = ArrayGetNItems(ndim, dims);
 
1021
 
 
1022
        if (nitems == 0)
 
1023
        {
 
1024
                retval = pstrdup("{}");
 
1025
                PG_RETURN_CSTRING(retval);
 
1026
        }
 
1027
 
 
1028
        /*
 
1029
         * we will need to add explicit dimensions if any dimension has a lower
 
1030
         * bound other than one
 
1031
         */
 
1032
        for (i = 0; i < ndim; i++)
 
1033
        {
 
1034
                if (lb[i] != 1)
 
1035
                {
 
1036
                        needdims = true;
 
1037
                        break;
 
1038
                }
 
1039
        }
 
1040
 
 
1041
        /*
 
1042
         * Convert all values to string form, count total space needed (including
 
1043
         * any overhead such as escaping backslashes), and detect whether each
 
1044
         * item needs double quotes.
 
1045
         */
 
1046
        values = (char **) palloc(nitems * sizeof(char *));
 
1047
        needquotes = (bool *) palloc(nitems * sizeof(bool));
 
1048
        overall_length = 1;                     /* don't forget to count \0 at end. */
 
1049
 
 
1050
        p = ARR_DATA_PTR(v);
 
1051
        bitmap = ARR_NULLBITMAP(v);
 
1052
        bitmask = 1;
 
1053
 
 
1054
        for (i = 0; i < nitems; i++)
 
1055
        {
 
1056
                bool            needquote;
 
1057
 
 
1058
                /* Get source element, checking for NULL */
 
1059
                if (bitmap && (*bitmap & bitmask) == 0)
 
1060
                {
 
1061
                        values[i] = pstrdup("NULL");
 
1062
                        overall_length += 4;
 
1063
                        needquote = false;
 
1064
                }
 
1065
                else
 
1066
                {
 
1067
                        Datum           itemvalue;
 
1068
 
 
1069
                        itemvalue = fetch_att(p, typbyval, typlen);
 
1070
                        values[i] = OutputFunctionCall(&my_extra->proc, itemvalue);
 
1071
                        p = att_addlength_pointer(p, typlen, p);
 
1072
                        p = (char *) att_align_nominal(p, typalign);
 
1073
 
 
1074
                        /* count data plus backslashes; detect chars needing quotes */
 
1075
                        if (values[i][0] == '\0')
 
1076
                                needquote = true;               /* force quotes for empty string */
 
1077
                        else if (pg_strcasecmp(values[i], "NULL") == 0)
 
1078
                                needquote = true;               /* force quotes for literal NULL */
 
1079
                        else
 
1080
                                needquote = false;
 
1081
 
 
1082
                        for (tmp = values[i]; *tmp != '\0'; tmp++)
 
1083
                        {
 
1084
                                char            ch = *tmp;
 
1085
 
 
1086
                                overall_length += 1;
 
1087
                                if (ch == '"' || ch == '\\')
 
1088
                                {
 
1089
                                        needquote = true;
 
1090
                                        overall_length += 1;
 
1091
                                }
 
1092
                                else if (ch == '{' || ch == '}' || ch == typdelim ||
 
1093
                                                 array_isspace(ch))
 
1094
                                        needquote = true;
 
1095
                        }
 
1096
                }
 
1097
 
 
1098
                needquotes[i] = needquote;
 
1099
 
 
1100
                /* Count the pair of double quotes, if needed */
 
1101
                if (needquote)
 
1102
                        overall_length += 2;
 
1103
                /* and the comma */
 
1104
                overall_length += 1;
 
1105
 
 
1106
                /* advance bitmap pointer if any */
 
1107
                if (bitmap)
 
1108
                {
 
1109
                        bitmask <<= 1;
 
1110
                        if (bitmask == 0x100)
 
1111
                        {
 
1112
                                bitmap++;
 
1113
                                bitmask = 1;
 
1114
                        }
 
1115
                }
 
1116
        }
 
1117
 
 
1118
        /*
 
1119
         * count total number of curly braces in output string
 
1120
         */
 
1121
        for (i = j = 0, k = 1; i < ndim; i++)
 
1122
                k *= dims[i], j += k;
 
1123
 
 
1124
        dims_str[0] = '\0';
 
1125
 
 
1126
        /* add explicit dimensions if required */
 
1127
        if (needdims)
 
1128
        {
 
1129
                char       *ptr = dims_str;
 
1130
 
 
1131
                for (i = 0; i < ndim; i++)
 
1132
                {
 
1133
                        sprintf(ptr, "[%d:%d]", lb[i], lb[i] + dims[i] - 1);
 
1134
                        ptr += strlen(ptr);
 
1135
                }
 
1136
                *ptr++ = *ASSGN;
 
1137
                *ptr = '\0';
 
1138
        }
 
1139
 
 
1140
        retval = (char *) palloc(strlen(dims_str) + overall_length + 2 * j);
 
1141
        p = retval;
 
1142
 
 
1143
#define APPENDSTR(str)  (strcpy(p, (str)), p += strlen(p))
 
1144
#define APPENDCHAR(ch)  (*p++ = (ch), *p = '\0')
 
1145
 
 
1146
        if (needdims)
 
1147
                APPENDSTR(dims_str);
 
1148
        APPENDCHAR('{');
 
1149
        for (i = 0; i < ndim; i++)
 
1150
                indx[i] = 0;
 
1151
        j = 0;
 
1152
        k = 0;
 
1153
        do
 
1154
        {
 
1155
                for (i = j; i < ndim - 1; i++)
 
1156
                        APPENDCHAR('{');
 
1157
 
 
1158
                if (needquotes[k])
 
1159
                {
 
1160
                        APPENDCHAR('"');
 
1161
                        for (tmp = values[k]; *tmp; tmp++)
 
1162
                        {
 
1163
                                char            ch = *tmp;
 
1164
 
 
1165
                                if (ch == '"' || ch == '\\')
 
1166
                                        *p++ = '\\';
 
1167
                                *p++ = ch;
 
1168
                        }
 
1169
                        *p = '\0';
 
1170
                        APPENDCHAR('"');
 
1171
                }
 
1172
                else
 
1173
                        APPENDSTR(values[k]);
 
1174
                pfree(values[k++]);
 
1175
 
 
1176
                for (i = ndim - 1; i >= 0; i--)
 
1177
                {
 
1178
                        indx[i] = (indx[i] + 1) % dims[i];
 
1179
                        if (indx[i])
 
1180
                        {
 
1181
                                APPENDCHAR(typdelim);
 
1182
                                break;
 
1183
                        }
 
1184
                        else
 
1185
                                APPENDCHAR('}');
 
1186
                }
 
1187
                j = i;
 
1188
        } while (j != -1);
 
1189
 
 
1190
#undef APPENDSTR
 
1191
#undef APPENDCHAR
 
1192
 
 
1193
        pfree(values);
 
1194
        pfree(needquotes);
 
1195
 
 
1196
        PG_RETURN_CSTRING(retval);
 
1197
}
 
1198
 
 
1199
/*
 
1200
 * array_recv :
 
1201
 *                converts an array from the external binary format to
 
1202
 *                its internal format.
 
1203
 *
 
1204
 * return value :
 
1205
 *                the internal representation of the input array
 
1206
 */
 
1207
Datum
 
1208
array_recv(PG_FUNCTION_ARGS)
 
1209
{
 
1210
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
 
1211
        Oid                     spec_element_type = PG_GETARG_OID(1);   /* type of an array
 
1212
                                                                                                                 * element */
 
1213
        int32           typmod = PG_GETARG_INT32(2);    /* typmod for array elements */
 
1214
        Oid                     element_type;
 
1215
        int                     typlen;
 
1216
        bool            typbyval;
 
1217
        char            typalign;
 
1218
        Oid                     typioparam;
 
1219
        int                     i,
 
1220
                                nitems;
 
1221
        Datum      *dataPtr;
 
1222
        bool       *nullsPtr;
 
1223
        bool            hasnulls;
 
1224
        int32           nbytes;
 
1225
        int32           dataoffset;
 
1226
        ArrayType  *retval;
 
1227
        int                     ndim,
 
1228
                                flags,
 
1229
                                dim[MAXDIM],
 
1230
                                lBound[MAXDIM];
 
1231
        ArrayMetaState *my_extra;
 
1232
 
 
1233
        /* Get the array header information */
 
1234
        ndim = pq_getmsgint(buf, 4);
 
1235
        if (ndim < 0)                           /* we do allow zero-dimension arrays */
 
1236
                ereport(ERROR,
 
1237
                                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
1238
                                 errmsg("invalid number of dimensions: %d", ndim)));
 
1239
        if (ndim > MAXDIM)
 
1240
                ereport(ERROR,
 
1241
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
1242
                                 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 
1243
                                                ndim, MAXDIM)));
 
1244
 
 
1245
        flags = pq_getmsgint(buf, 4);
 
1246
        if (flags != 0 && flags != 1)
 
1247
                ereport(ERROR,
 
1248
                                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
1249
                                 errmsg("invalid array flags")));
 
1250
 
 
1251
        element_type = pq_getmsgint(buf, sizeof(Oid));
 
1252
        if (element_type != spec_element_type)
 
1253
        {
 
1254
                /* XXX Can we allow taking the input element type in any cases? */
 
1255
                ereport(ERROR,
 
1256
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
 
1257
                                 errmsg("wrong element type")));
 
1258
        }
 
1259
 
 
1260
        for (i = 0; i < ndim; i++)
 
1261
        {
 
1262
                dim[i] = pq_getmsgint(buf, 4);
 
1263
                lBound[i] = pq_getmsgint(buf, 4);
 
1264
 
 
1265
                /*
 
1266
                 * Check overflow of upper bound. (ArrayNItems() below checks that
 
1267
                 * dim[i] >= 0)
 
1268
                 */
 
1269
                if (dim[i] != 0)
 
1270
                {
 
1271
                        int                     ub = lBound[i] + dim[i] - 1;
 
1272
 
 
1273
                        if (lBound[i] > ub)
 
1274
                                ereport(ERROR,
 
1275
                                                (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 
1276
                                                 errmsg("integer out of range")));
 
1277
                }
 
1278
        }
 
1279
 
 
1280
        /* This checks for overflow of array dimensions */
 
1281
        nitems = ArrayGetNItems(ndim, dim);
 
1282
 
 
1283
        /*
 
1284
         * We arrange to look up info about element type, including its receive
 
1285
         * conversion proc, only once per series of calls, assuming the element
 
1286
         * type doesn't change underneath us.
 
1287
         */
 
1288
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
1289
        if (my_extra == NULL)
 
1290
        {
 
1291
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
1292
                                                                                                          sizeof(ArrayMetaState));
 
1293
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
1294
                my_extra->element_type = ~element_type;
 
1295
        }
 
1296
 
 
1297
        if (my_extra->element_type != element_type)
 
1298
        {
 
1299
                /* Get info about element type, including its receive proc */
 
1300
                get_type_io_data(element_type, IOFunc_receive,
 
1301
                                                 &my_extra->typlen, &my_extra->typbyval,
 
1302
                                                 &my_extra->typalign, &my_extra->typdelim,
 
1303
                                                 &my_extra->typioparam, &my_extra->typiofunc);
 
1304
                if (!OidIsValid(my_extra->typiofunc))
 
1305
                        ereport(ERROR,
 
1306
                                        (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
1307
                                         errmsg("no binary input function available for type %s",
 
1308
                                                        format_type_be(element_type))));
 
1309
                fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 
1310
                                          fcinfo->flinfo->fn_mcxt);
 
1311
                my_extra->element_type = element_type;
 
1312
        }
 
1313
 
 
1314
        if (nitems == 0)
 
1315
        {
 
1316
                /* Return empty array ... but not till we've validated element_type */
 
1317
                PG_RETURN_ARRAYTYPE_P(construct_empty_array(element_type));
 
1318
        }
 
1319
 
 
1320
        typlen = my_extra->typlen;
 
1321
        typbyval = my_extra->typbyval;
 
1322
        typalign = my_extra->typalign;
 
1323
        typioparam = my_extra->typioparam;
 
1324
 
 
1325
        dataPtr = (Datum *) palloc(nitems * sizeof(Datum));
 
1326
        nullsPtr = (bool *) palloc(nitems * sizeof(bool));
 
1327
        ReadArrayBinary(buf, nitems,
 
1328
                                        &my_extra->proc, typioparam, typmod,
 
1329
                                        typlen, typbyval, typalign,
 
1330
                                        dataPtr, nullsPtr,
 
1331
                                        &hasnulls, &nbytes);
 
1332
        if (hasnulls)
 
1333
        {
 
1334
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
 
1335
                nbytes += dataoffset;
 
1336
        }
 
1337
        else
 
1338
        {
 
1339
                dataoffset = 0;                 /* marker for no null bitmap */
 
1340
                nbytes += ARR_OVERHEAD_NONULLS(ndim);
 
1341
        }
 
1342
        retval = (ArrayType *) palloc0(nbytes);
 
1343
        SET_VARSIZE(retval, nbytes);
 
1344
        retval->ndim = ndim;
 
1345
        retval->dataoffset = dataoffset;
 
1346
        retval->elemtype = element_type;
 
1347
        memcpy(ARR_DIMS(retval), dim, ndim * sizeof(int));
 
1348
        memcpy(ARR_LBOUND(retval), lBound, ndim * sizeof(int));
 
1349
 
 
1350
        CopyArrayEls(retval,
 
1351
                                 dataPtr, nullsPtr, nitems,
 
1352
                                 typlen, typbyval, typalign,
 
1353
                                 true);
 
1354
 
 
1355
        pfree(dataPtr);
 
1356
        pfree(nullsPtr);
 
1357
 
 
1358
        PG_RETURN_ARRAYTYPE_P(retval);
 
1359
}
 
1360
 
 
1361
/*
 
1362
 * ReadArrayBinary:
 
1363
 *       collect the data elements of an array being read in binary style.
 
1364
 *
 
1365
 * Inputs:
 
1366
 *      buf: the data buffer to read from.
 
1367
 *      nitems: total number of array elements (already read).
 
1368
 *      receiveproc: type-specific receive procedure for element datatype.
 
1369
 *      typioparam, typmod: auxiliary values to pass to receiveproc.
 
1370
 *      typlen, typbyval, typalign: storage parameters of element datatype.
 
1371
 *
 
1372
 * Outputs:
 
1373
 *      values[]: filled with converted data values.
 
1374
 *      nulls[]: filled with is-null markers.
 
1375
 *      *hasnulls: set TRUE iff there are any null elements.
 
1376
 *      *nbytes: set to total size of data area needed (including alignment
 
1377
 *              padding but not including array header overhead).
 
1378
 *
 
1379
 * Note that values[] and nulls[] are allocated by the caller, and must have
 
1380
 * nitems elements.
 
1381
 */
 
1382
static void
 
1383
ReadArrayBinary(StringInfo buf,
 
1384
                                int nitems,
 
1385
                                FmgrInfo *receiveproc,
 
1386
                                Oid typioparam,
 
1387
                                int32 typmod,
 
1388
                                int typlen,
 
1389
                                bool typbyval,
 
1390
                                char typalign,
 
1391
                                Datum *values,
 
1392
                                bool *nulls,
 
1393
                                bool *hasnulls,
 
1394
                                int32 *nbytes)
 
1395
{
 
1396
        int                     i;
 
1397
        bool            hasnull;
 
1398
        int32           totbytes;
 
1399
 
 
1400
        for (i = 0; i < nitems; i++)
 
1401
        {
 
1402
                int                     itemlen;
 
1403
                StringInfoData elem_buf;
 
1404
                char            csave;
 
1405
 
 
1406
                /* Get and check the item length */
 
1407
                itemlen = pq_getmsgint(buf, 4);
 
1408
                if (itemlen < -1 || itemlen > (buf->len - buf->cursor))
 
1409
                        ereport(ERROR,
 
1410
                                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
1411
                                         errmsg("insufficient data left in message")));
 
1412
 
 
1413
                if (itemlen == -1)
 
1414
                {
 
1415
                        /* -1 length means NULL */
 
1416
                        values[i] = ReceiveFunctionCall(receiveproc, NULL,
 
1417
                                                                                        typioparam, typmod);
 
1418
                        nulls[i] = true;
 
1419
                        continue;
 
1420
                }
 
1421
 
 
1422
                /*
 
1423
                 * Rather than copying data around, we just set up a phony StringInfo
 
1424
                 * pointing to the correct portion of the input buffer. We assume we
 
1425
                 * can scribble on the input buffer so as to maintain the convention
 
1426
                 * that StringInfos have a trailing null.
 
1427
                 */
 
1428
                elem_buf.data = &buf->data[buf->cursor];
 
1429
                elem_buf.maxlen = itemlen + 1;
 
1430
                elem_buf.len = itemlen;
 
1431
                elem_buf.cursor = 0;
 
1432
 
 
1433
                buf->cursor += itemlen;
 
1434
 
 
1435
                csave = buf->data[buf->cursor];
 
1436
                buf->data[buf->cursor] = '\0';
 
1437
 
 
1438
                /* Now call the element's receiveproc */
 
1439
                values[i] = ReceiveFunctionCall(receiveproc, &elem_buf,
 
1440
                                                                                typioparam, typmod);
 
1441
                nulls[i] = false;
 
1442
 
 
1443
                /* Trouble if it didn't eat the whole buffer */
 
1444
                if (elem_buf.cursor != itemlen)
 
1445
                        ereport(ERROR,
 
1446
                                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
1447
                                         errmsg("improper binary format in array element %d",
 
1448
                                                        i + 1)));
 
1449
 
 
1450
                buf->data[buf->cursor] = csave;
 
1451
        }
 
1452
 
 
1453
        /*
 
1454
         * Check for nulls, compute total data space needed
 
1455
         */
 
1456
        hasnull = false;
 
1457
        totbytes = 0;
 
1458
        for (i = 0; i < nitems; i++)
 
1459
        {
 
1460
                if (nulls[i])
 
1461
                        hasnull = true;
 
1462
                else
 
1463
                {
 
1464
                        /* let's just make sure data is not toasted */
 
1465
                        if (typlen == -1)
 
1466
                                values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 
1467
                        totbytes = att_addlength_datum(totbytes, typlen, values[i]);
 
1468
                        totbytes = att_align_nominal(totbytes, typalign);
 
1469
                        /* check for overflow of total request */
 
1470
                        if (!AllocSizeIsValid(totbytes))
 
1471
                                ereport(ERROR,
 
1472
                                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
1473
                                                 errmsg("array size exceeds the maximum allowed (%d)",
 
1474
                                                                (int) MaxAllocSize)));
 
1475
                }
 
1476
        }
 
1477
        *hasnulls = hasnull;
 
1478
        *nbytes = totbytes;
 
1479
}
 
1480
 
 
1481
 
 
1482
/*
 
1483
 * array_send :
 
1484
 *                takes the internal representation of an array and returns a bytea
 
1485
 *                containing the array in its external binary format.
 
1486
 */
 
1487
Datum
 
1488
array_send(PG_FUNCTION_ARGS)
 
1489
{
 
1490
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
1491
        Oid                     element_type = ARR_ELEMTYPE(v);
 
1492
        int                     typlen;
 
1493
        bool            typbyval;
 
1494
        char            typalign;
 
1495
        char       *p;
 
1496
        bits8      *bitmap;
 
1497
        int                     bitmask;
 
1498
        int                     nitems,
 
1499
                                i;
 
1500
        int                     ndim,
 
1501
                           *dim;
 
1502
        StringInfoData buf;
 
1503
        ArrayMetaState *my_extra;
 
1504
 
 
1505
        /*
 
1506
         * We arrange to look up info about element type, including its send
 
1507
         * conversion proc, only once per series of calls, assuming the element
 
1508
         * type doesn't change underneath us.
 
1509
         */
 
1510
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
1511
        if (my_extra == NULL)
 
1512
        {
 
1513
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
1514
                                                                                                          sizeof(ArrayMetaState));
 
1515
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
1516
                my_extra->element_type = ~element_type;
 
1517
        }
 
1518
 
 
1519
        if (my_extra->element_type != element_type)
 
1520
        {
 
1521
                /* Get info about element type, including its send proc */
 
1522
                get_type_io_data(element_type, IOFunc_send,
 
1523
                                                 &my_extra->typlen, &my_extra->typbyval,
 
1524
                                                 &my_extra->typalign, &my_extra->typdelim,
 
1525
                                                 &my_extra->typioparam, &my_extra->typiofunc);
 
1526
                if (!OidIsValid(my_extra->typiofunc))
 
1527
                        ereport(ERROR,
 
1528
                                        (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
1529
                                         errmsg("no binary output function available for type %s",
 
1530
                                                        format_type_be(element_type))));
 
1531
                fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc,
 
1532
                                          fcinfo->flinfo->fn_mcxt);
 
1533
                my_extra->element_type = element_type;
 
1534
        }
 
1535
        typlen = my_extra->typlen;
 
1536
        typbyval = my_extra->typbyval;
 
1537
        typalign = my_extra->typalign;
 
1538
 
 
1539
        ndim = ARR_NDIM(v);
 
1540
        dim = ARR_DIMS(v);
 
1541
        nitems = ArrayGetNItems(ndim, dim);
 
1542
 
 
1543
        pq_begintypsend(&buf);
 
1544
 
 
1545
        /* Send the array header information */
 
1546
        pq_sendint(&buf, ndim, 4);
 
1547
        pq_sendint(&buf, ARR_HASNULL(v) ? 1 : 0, 4);
 
1548
        pq_sendint(&buf, element_type, sizeof(Oid));
 
1549
        for (i = 0; i < ndim; i++)
 
1550
        {
 
1551
                pq_sendint(&buf, ARR_DIMS(v)[i], 4);
 
1552
                pq_sendint(&buf, ARR_LBOUND(v)[i], 4);
 
1553
        }
 
1554
 
 
1555
        /* Send the array elements using the element's own sendproc */
 
1556
        p = ARR_DATA_PTR(v);
 
1557
        bitmap = ARR_NULLBITMAP(v);
 
1558
        bitmask = 1;
 
1559
 
 
1560
        for (i = 0; i < nitems; i++)
 
1561
        {
 
1562
                /* Get source element, checking for NULL */
 
1563
                if (bitmap && (*bitmap & bitmask) == 0)
 
1564
                {
 
1565
                        /* -1 length means a NULL */
 
1566
                        pq_sendint(&buf, -1, 4);
 
1567
                }
 
1568
                else
 
1569
                {
 
1570
                        Datum           itemvalue;
 
1571
                        bytea      *outputbytes;
 
1572
 
 
1573
                        itemvalue = fetch_att(p, typbyval, typlen);
 
1574
                        outputbytes = SendFunctionCall(&my_extra->proc, itemvalue);
 
1575
                        pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
 
1576
                        pq_sendbytes(&buf, VARDATA(outputbytes),
 
1577
                                                 VARSIZE(outputbytes) - VARHDRSZ);
 
1578
                        pfree(outputbytes);
 
1579
 
 
1580
                        p = att_addlength_pointer(p, typlen, p);
 
1581
                        p = (char *) att_align_nominal(p, typalign);
 
1582
                }
 
1583
 
 
1584
                /* advance bitmap pointer if any */
 
1585
                if (bitmap)
 
1586
                {
 
1587
                        bitmask <<= 1;
 
1588
                        if (bitmask == 0x100)
 
1589
                        {
 
1590
                                bitmap++;
 
1591
                                bitmask = 1;
 
1592
                        }
 
1593
                }
 
1594
        }
 
1595
 
 
1596
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 
1597
}
 
1598
 
 
1599
/*
 
1600
 * array_ndims :
 
1601
 *                returns the number of dimensions of the array pointed to by "v"
 
1602
 */
 
1603
Datum
 
1604
array_ndims(PG_FUNCTION_ARGS)
 
1605
{
 
1606
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
1607
 
 
1608
        /* Sanity check: does it look like an array at all? */
 
1609
        if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
 
1610
                PG_RETURN_NULL();
 
1611
 
 
1612
        PG_RETURN_INT32(ARR_NDIM(v));
 
1613
}
 
1614
 
 
1615
/*
 
1616
 * array_dims :
 
1617
 *                returns the dimensions of the array pointed to by "v", as a "text"
 
1618
 */
 
1619
Datum
 
1620
array_dims(PG_FUNCTION_ARGS)
 
1621
{
 
1622
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
1623
        char       *p;
 
1624
        int                     i;
 
1625
        int                *dimv,
 
1626
                           *lb;
 
1627
 
 
1628
        /*
 
1629
         * 33 since we assume 15 digits per number + ':' +'[]'
 
1630
         *
 
1631
         * +1 for trailing null
 
1632
         */
 
1633
        char            buf[MAXDIM * 33 + 1];
 
1634
 
 
1635
        /* Sanity check: does it look like an array at all? */
 
1636
        if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
 
1637
                PG_RETURN_NULL();
 
1638
 
 
1639
        dimv = ARR_DIMS(v);
 
1640
        lb = ARR_LBOUND(v);
 
1641
 
 
1642
        p = buf;
 
1643
        for (i = 0; i < ARR_NDIM(v); i++)
 
1644
        {
 
1645
                sprintf(p, "[%d:%d]", lb[i], dimv[i] + lb[i] - 1);
 
1646
                p += strlen(p);
 
1647
        }
 
1648
 
 
1649
        PG_RETURN_TEXT_P(cstring_to_text(buf));
 
1650
}
 
1651
 
 
1652
/*
 
1653
 * array_lower :
 
1654
 *              returns the lower dimension, of the DIM requested, for
 
1655
 *              the array pointed to by "v", as an int4
 
1656
 */
 
1657
Datum
 
1658
array_lower(PG_FUNCTION_ARGS)
 
1659
{
 
1660
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
1661
        int                     reqdim = PG_GETARG_INT32(1);
 
1662
        int                *lb;
 
1663
        int                     result;
 
1664
 
 
1665
        /* Sanity check: does it look like an array at all? */
 
1666
        if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
 
1667
                PG_RETURN_NULL();
 
1668
 
 
1669
        /* Sanity check: was the requested dim valid */
 
1670
        if (reqdim <= 0 || reqdim > ARR_NDIM(v))
 
1671
                PG_RETURN_NULL();
 
1672
 
 
1673
        lb = ARR_LBOUND(v);
 
1674
        result = lb[reqdim - 1];
 
1675
 
 
1676
        PG_RETURN_INT32(result);
 
1677
}
 
1678
 
 
1679
/*
 
1680
 * array_upper :
 
1681
 *              returns the upper dimension, of the DIM requested, for
 
1682
 *              the array pointed to by "v", as an int4
 
1683
 */
 
1684
Datum
 
1685
array_upper(PG_FUNCTION_ARGS)
 
1686
{
 
1687
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
1688
        int                     reqdim = PG_GETARG_INT32(1);
 
1689
        int                *dimv,
 
1690
                           *lb;
 
1691
        int                     result;
 
1692
 
 
1693
        /* Sanity check: does it look like an array at all? */
 
1694
        if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
 
1695
                PG_RETURN_NULL();
 
1696
 
 
1697
        /* Sanity check: was the requested dim valid */
 
1698
        if (reqdim <= 0 || reqdim > ARR_NDIM(v))
 
1699
                PG_RETURN_NULL();
 
1700
 
 
1701
        lb = ARR_LBOUND(v);
 
1702
        dimv = ARR_DIMS(v);
 
1703
 
 
1704
        result = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
 
1705
 
 
1706
        PG_RETURN_INT32(result);
 
1707
}
 
1708
 
 
1709
/*
 
1710
 * array_length :
 
1711
 *              returns the length, of the dimension requested, for
 
1712
 *              the array pointed to by "v", as an int4
 
1713
 */
 
1714
Datum
 
1715
array_length(PG_FUNCTION_ARGS)
 
1716
{
 
1717
        ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
1718
        int                     reqdim = PG_GETARG_INT32(1);
 
1719
        int                *dimv;
 
1720
        int                     result;
 
1721
 
 
1722
        /* Sanity check: does it look like an array at all? */
 
1723
        if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
 
1724
                PG_RETURN_NULL();
 
1725
 
 
1726
        /* Sanity check: was the requested dim valid */
 
1727
        if (reqdim <= 0 || reqdim > ARR_NDIM(v))
 
1728
                PG_RETURN_NULL();
 
1729
 
 
1730
        dimv = ARR_DIMS(v);
 
1731
 
 
1732
        result = dimv[reqdim - 1];
 
1733
 
 
1734
        PG_RETURN_INT32(result);
 
1735
}
 
1736
 
 
1737
/*
 
1738
 * array_ref :
 
1739
 *        This routine takes an array pointer and a subscript array and returns
 
1740
 *        the referenced item as a Datum.  Note that for a pass-by-reference
 
1741
 *        datatype, the returned Datum is a pointer into the array object.
 
1742
 *
 
1743
 * This handles both ordinary varlena arrays and fixed-length arrays.
 
1744
 *
 
1745
 * Inputs:
 
1746
 *      array: the array object (mustn't be NULL)
 
1747
 *      nSubscripts: number of subscripts supplied
 
1748
 *      indx[]: the subscript values
 
1749
 *      arraytyplen: pg_type.typlen for the array type
 
1750
 *      elmlen: pg_type.typlen for the array's element type
 
1751
 *      elmbyval: pg_type.typbyval for the array's element type
 
1752
 *      elmalign: pg_type.typalign for the array's element type
 
1753
 *
 
1754
 * Outputs:
 
1755
 *      The return value is the element Datum.
 
1756
 *      *isNull is set to indicate whether the element is NULL.
 
1757
 */
 
1758
Datum
 
1759
array_ref(ArrayType *array,
 
1760
                  int nSubscripts,
 
1761
                  int *indx,
 
1762
                  int arraytyplen,
 
1763
                  int elmlen,
 
1764
                  bool elmbyval,
 
1765
                  char elmalign,
 
1766
                  bool *isNull)
 
1767
{
 
1768
        int                     i,
 
1769
                                ndim,
 
1770
                           *dim,
 
1771
                           *lb,
 
1772
                                offset,
 
1773
                                fixedDim[1],
 
1774
                                fixedLb[1];
 
1775
        char       *arraydataptr,
 
1776
                           *retptr;
 
1777
        bits8      *arraynullsptr;
 
1778
 
 
1779
        if (arraytyplen > 0)
 
1780
        {
 
1781
                /*
 
1782
                 * fixed-length arrays -- these are assumed to be 1-d, 0-based
 
1783
                 */
 
1784
                ndim = 1;
 
1785
                fixedDim[0] = arraytyplen / elmlen;
 
1786
                fixedLb[0] = 0;
 
1787
                dim = fixedDim;
 
1788
                lb = fixedLb;
 
1789
                arraydataptr = (char *) array;
 
1790
                arraynullsptr = NULL;
 
1791
        }
 
1792
        else
 
1793
        {
 
1794
                /* detoast input array if necessary */
 
1795
                array = DatumGetArrayTypeP(PointerGetDatum(array));
 
1796
 
 
1797
                ndim = ARR_NDIM(array);
 
1798
                dim = ARR_DIMS(array);
 
1799
                lb = ARR_LBOUND(array);
 
1800
                arraydataptr = ARR_DATA_PTR(array);
 
1801
                arraynullsptr = ARR_NULLBITMAP(array);
 
1802
        }
 
1803
 
 
1804
        /*
 
1805
         * Return NULL for invalid subscript
 
1806
         */
 
1807
        if (ndim != nSubscripts || ndim <= 0 || ndim > MAXDIM)
 
1808
        {
 
1809
                *isNull = true;
 
1810
                return (Datum) 0;
 
1811
        }
 
1812
        for (i = 0; i < ndim; i++)
 
1813
        {
 
1814
                if (indx[i] < lb[i] || indx[i] >= (dim[i] + lb[i]))
 
1815
                {
 
1816
                        *isNull = true;
 
1817
                        return (Datum) 0;
 
1818
                }
 
1819
        }
 
1820
 
 
1821
        /*
 
1822
         * Calculate the element number
 
1823
         */
 
1824
        offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
 
1825
 
 
1826
        /*
 
1827
         * Check for NULL array element
 
1828
         */
 
1829
        if (array_get_isnull(arraynullsptr, offset))
 
1830
        {
 
1831
                *isNull = true;
 
1832
                return (Datum) 0;
 
1833
        }
 
1834
 
 
1835
        /*
 
1836
         * OK, get the element
 
1837
         */
 
1838
        *isNull = false;
 
1839
        retptr = array_seek(arraydataptr, 0, arraynullsptr, offset,
 
1840
                                                elmlen, elmbyval, elmalign);
 
1841
        return ArrayCast(retptr, elmbyval, elmlen);
 
1842
}
 
1843
 
 
1844
/*
 
1845
 * array_get_slice :
 
1846
 *                 This routine takes an array and a range of indices (upperIndex and
 
1847
 *                 lowerIndx), creates a new array structure for the referred elements
 
1848
 *                 and returns a pointer to it.
 
1849
 *
 
1850
 * This handles both ordinary varlena arrays and fixed-length arrays.
 
1851
 *
 
1852
 * Inputs:
 
1853
 *      array: the array object (mustn't be NULL)
 
1854
 *      nSubscripts: number of subscripts supplied (must be same for upper/lower)
 
1855
 *      upperIndx[]: the upper subscript values
 
1856
 *      lowerIndx[]: the lower subscript values
 
1857
 *      arraytyplen: pg_type.typlen for the array type
 
1858
 *      elmlen: pg_type.typlen for the array's element type
 
1859
 *      elmbyval: pg_type.typbyval for the array's element type
 
1860
 *      elmalign: pg_type.typalign for the array's element type
 
1861
 *
 
1862
 * Outputs:
 
1863
 *      The return value is the new array Datum (it's never NULL)
 
1864
 *
 
1865
 * NOTE: we assume it is OK to scribble on the provided subscript arrays
 
1866
 * lowerIndx[] and upperIndx[].  These are generally just temporaries.
 
1867
 */
 
1868
ArrayType *
 
1869
array_get_slice(ArrayType *array,
 
1870
                                int nSubscripts,
 
1871
                                int *upperIndx,
 
1872
                                int *lowerIndx,
 
1873
                                int arraytyplen,
 
1874
                                int elmlen,
 
1875
                                bool elmbyval,
 
1876
                                char elmalign)
 
1877
{
 
1878
        ArrayType  *newarray;
 
1879
        int                     i,
 
1880
                                ndim,
 
1881
                           *dim,
 
1882
                           *lb,
 
1883
                           *newlb;
 
1884
        int                     fixedDim[1],
 
1885
                                fixedLb[1];
 
1886
        Oid                     elemtype;
 
1887
        char       *arraydataptr;
 
1888
        bits8      *arraynullsptr;
 
1889
        int32           dataoffset;
 
1890
        int                     bytes,
 
1891
                                span[MAXDIM];
 
1892
 
 
1893
        if (arraytyplen > 0)
 
1894
        {
 
1895
                /*
 
1896
                 * fixed-length arrays -- currently, cannot slice these because parser
 
1897
                 * labels output as being of the fixed-length array type! Code below
 
1898
                 * shows how we could support it if the parser were changed to label
 
1899
                 * output as a suitable varlena array type.
 
1900
                 */
 
1901
                ereport(ERROR,
 
1902
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
1903
                                 errmsg("slices of fixed-length arrays not implemented")));
 
1904
 
 
1905
                /*
 
1906
                 * fixed-length arrays -- these are assumed to be 1-d, 0-based
 
1907
                 *
 
1908
                 * XXX where would we get the correct ELEMTYPE from?
 
1909
                 */
 
1910
                ndim = 1;
 
1911
                fixedDim[0] = arraytyplen / elmlen;
 
1912
                fixedLb[0] = 0;
 
1913
                dim = fixedDim;
 
1914
                lb = fixedLb;
 
1915
                elemtype = InvalidOid;  /* XXX */
 
1916
                arraydataptr = (char *) array;
 
1917
                arraynullsptr = NULL;
 
1918
        }
 
1919
        else
 
1920
        {
 
1921
                /* detoast input array if necessary */
 
1922
                array = DatumGetArrayTypeP(PointerGetDatum(array));
 
1923
 
 
1924
                ndim = ARR_NDIM(array);
 
1925
                dim = ARR_DIMS(array);
 
1926
                lb = ARR_LBOUND(array);
 
1927
                elemtype = ARR_ELEMTYPE(array);
 
1928
                arraydataptr = ARR_DATA_PTR(array);
 
1929
                arraynullsptr = ARR_NULLBITMAP(array);
 
1930
        }
 
1931
 
 
1932
        /*
 
1933
         * Check provided subscripts.  A slice exceeding the current array limits
 
1934
         * is silently truncated to the array limits.  If we end up with an empty
 
1935
         * slice, return an empty array.
 
1936
         */
 
1937
        if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
 
1938
                return construct_empty_array(elemtype);
 
1939
 
 
1940
        for (i = 0; i < nSubscripts; i++)
 
1941
        {
 
1942
                if (lowerIndx[i] < lb[i])
 
1943
                        lowerIndx[i] = lb[i];
 
1944
                if (upperIndx[i] >= (dim[i] + lb[i]))
 
1945
                        upperIndx[i] = dim[i] + lb[i] - 1;
 
1946
                if (lowerIndx[i] > upperIndx[i])
 
1947
                        return construct_empty_array(elemtype);
 
1948
        }
 
1949
        /* fill any missing subscript positions with full array range */
 
1950
        for (; i < ndim; i++)
 
1951
        {
 
1952
                lowerIndx[i] = lb[i];
 
1953
                upperIndx[i] = dim[i] + lb[i] - 1;
 
1954
                if (lowerIndx[i] > upperIndx[i])
 
1955
                        return construct_empty_array(elemtype);
 
1956
        }
 
1957
 
 
1958
        mda_get_range(ndim, span, lowerIndx, upperIndx);
 
1959
 
 
1960
        bytes = array_slice_size(arraydataptr, arraynullsptr,
 
1961
                                                         ndim, dim, lb,
 
1962
                                                         lowerIndx, upperIndx,
 
1963
                                                         elmlen, elmbyval, elmalign);
 
1964
 
 
1965
        /*
 
1966
         * Currently, we put a null bitmap in the result if the source has one;
 
1967
         * could be smarter ...
 
1968
         */
 
1969
        if (arraynullsptr)
 
1970
        {
 
1971
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, ArrayGetNItems(ndim, span));
 
1972
                bytes += dataoffset;
 
1973
        }
 
1974
        else
 
1975
        {
 
1976
                dataoffset = 0;                 /* marker for no null bitmap */
 
1977
                bytes += ARR_OVERHEAD_NONULLS(ndim);
 
1978
        }
 
1979
 
 
1980
        newarray = (ArrayType *) palloc0(bytes);
 
1981
        SET_VARSIZE(newarray, bytes);
 
1982
        newarray->ndim = ndim;
 
1983
        newarray->dataoffset = dataoffset;
 
1984
        newarray->elemtype = elemtype;
 
1985
        memcpy(ARR_DIMS(newarray), span, ndim * sizeof(int));
 
1986
 
 
1987
        /*
 
1988
         * Lower bounds of the new array are set to 1.  Formerly (before 7.3) we
 
1989
         * copied the given lowerIndx values ... but that seems confusing.
 
1990
         */
 
1991
        newlb = ARR_LBOUND(newarray);
 
1992
        for (i = 0; i < ndim; i++)
 
1993
                newlb[i] = 1;
 
1994
 
 
1995
        array_extract_slice(newarray,
 
1996
                                                ndim, dim, lb,
 
1997
                                                arraydataptr, arraynullsptr,
 
1998
                                                lowerIndx, upperIndx,
 
1999
                                                elmlen, elmbyval, elmalign);
 
2000
 
 
2001
        return newarray;
 
2002
}
 
2003
 
 
2004
/*
 
2005
 * array_set :
 
2006
 *                This routine sets the value of an array element (specified by
 
2007
 *                a subscript array) to a new value specified by "dataValue".
 
2008
 *
 
2009
 * This handles both ordinary varlena arrays and fixed-length arrays.
 
2010
 *
 
2011
 * Inputs:
 
2012
 *      array: the initial array object (mustn't be NULL)
 
2013
 *      nSubscripts: number of subscripts supplied
 
2014
 *      indx[]: the subscript values
 
2015
 *      dataValue: the datum to be inserted at the given position
 
2016
 *      isNull: whether dataValue is NULL
 
2017
 *      arraytyplen: pg_type.typlen for the array type
 
2018
 *      elmlen: pg_type.typlen for the array's element type
 
2019
 *      elmbyval: pg_type.typbyval for the array's element type
 
2020
 *      elmalign: pg_type.typalign for the array's element type
 
2021
 *
 
2022
 * Result:
 
2023
 *                A new array is returned, just like the old except for the one
 
2024
 *                modified entry.  The original array object is not changed.
 
2025
 *
 
2026
 * For one-dimensional arrays only, we allow the array to be extended
 
2027
 * by assigning to a position outside the existing subscript range; any
 
2028
 * positions between the existing elements and the new one are set to NULLs.
 
2029
 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
 
2030
 *
 
2031
 * NOTE: For assignments, we throw an error for invalid subscripts etc,
 
2032
 * rather than returning a NULL as the fetch operations do.
 
2033
 */
 
2034
ArrayType *
 
2035
array_set(ArrayType *array,
 
2036
                  int nSubscripts,
 
2037
                  int *indx,
 
2038
                  Datum dataValue,
 
2039
                  bool isNull,
 
2040
                  int arraytyplen,
 
2041
                  int elmlen,
 
2042
                  bool elmbyval,
 
2043
                  char elmalign)
 
2044
{
 
2045
        ArrayType  *newarray;
 
2046
        int                     i,
 
2047
                                ndim,
 
2048
                                dim[MAXDIM],
 
2049
                                lb[MAXDIM],
 
2050
                                offset;
 
2051
        char       *elt_ptr;
 
2052
        bool            newhasnulls;
 
2053
        bits8      *oldnullbitmap;
 
2054
        int                     oldnitems,
 
2055
                                newnitems,
 
2056
                                olddatasize,
 
2057
                                newsize,
 
2058
                                olditemlen,
 
2059
                                newitemlen,
 
2060
                                overheadlen,
 
2061
                                oldoverheadlen,
 
2062
                                addedbefore,
 
2063
                                addedafter,
 
2064
                                lenbefore,
 
2065
                                lenafter;
 
2066
 
 
2067
        if (arraytyplen > 0)
 
2068
        {
 
2069
                /*
 
2070
                 * fixed-length arrays -- these are assumed to be 1-d, 0-based. We
 
2071
                 * cannot extend them, either.
 
2072
                 */
 
2073
                if (nSubscripts != 1)
 
2074
                        ereport(ERROR,
 
2075
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2076
                                         errmsg("wrong number of array subscripts")));
 
2077
 
 
2078
                if (indx[0] < 0 || indx[0] * elmlen >= arraytyplen)
 
2079
                        ereport(ERROR,
 
2080
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2081
                                         errmsg("array subscript out of range")));
 
2082
 
 
2083
                if (isNull)
 
2084
                        ereport(ERROR,
 
2085
                                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 
2086
                                         errmsg("cannot assign null value to an element of a fixed-length array")));
 
2087
 
 
2088
                newarray = (ArrayType *) palloc(arraytyplen);
 
2089
                memcpy(newarray, array, arraytyplen);
 
2090
                elt_ptr = (char *) newarray + indx[0] * elmlen;
 
2091
                ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign, elt_ptr);
 
2092
                return newarray;
 
2093
        }
 
2094
 
 
2095
        if (nSubscripts <= 0 || nSubscripts > MAXDIM)
 
2096
                ereport(ERROR,
 
2097
                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2098
                                 errmsg("wrong number of array subscripts")));
 
2099
 
 
2100
        /* make sure item to be inserted is not toasted */
 
2101
        if (elmlen == -1 && !isNull)
 
2102
                dataValue = PointerGetDatum(PG_DETOAST_DATUM(dataValue));
 
2103
 
 
2104
        /* detoast input array if necessary */
 
2105
        array = DatumGetArrayTypeP(PointerGetDatum(array));
 
2106
 
 
2107
        ndim = ARR_NDIM(array);
 
2108
 
 
2109
        /*
 
2110
         * if number of dims is zero, i.e. an empty array, create an array with
 
2111
         * nSubscripts dimensions, and set the lower bounds to the supplied
 
2112
         * subscripts
 
2113
         */
 
2114
        if (ndim == 0)
 
2115
        {
 
2116
                Oid                     elmtype = ARR_ELEMTYPE(array);
 
2117
 
 
2118
                for (i = 0; i < nSubscripts; i++)
 
2119
                {
 
2120
                        dim[i] = 1;
 
2121
                        lb[i] = indx[i];
 
2122
                }
 
2123
 
 
2124
                return construct_md_array(&dataValue, &isNull, nSubscripts,
 
2125
                                                                  dim, lb, elmtype,
 
2126
                                                                  elmlen, elmbyval, elmalign);
 
2127
        }
 
2128
 
 
2129
        if (ndim != nSubscripts)
 
2130
                ereport(ERROR,
 
2131
                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2132
                                 errmsg("wrong number of array subscripts")));
 
2133
 
 
2134
        /* copy dim/lb since we may modify them */
 
2135
        memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
 
2136
        memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
 
2137
 
 
2138
        newhasnulls = (ARR_HASNULL(array) || isNull);
 
2139
        addedbefore = addedafter = 0;
 
2140
 
 
2141
        /*
 
2142
         * Check subscripts
 
2143
         */
 
2144
        if (ndim == 1)
 
2145
        {
 
2146
                if (indx[0] < lb[0])
 
2147
                {
 
2148
                        addedbefore = lb[0] - indx[0];
 
2149
                        dim[0] += addedbefore;
 
2150
                        lb[0] = indx[0];
 
2151
                        if (addedbefore > 1)
 
2152
                                newhasnulls = true;             /* will insert nulls */
 
2153
                }
 
2154
                if (indx[0] >= (dim[0] + lb[0]))
 
2155
                {
 
2156
                        addedafter = indx[0] - (dim[0] + lb[0]) + 1;
 
2157
                        dim[0] += addedafter;
 
2158
                        if (addedafter > 1)
 
2159
                                newhasnulls = true;             /* will insert nulls */
 
2160
                }
 
2161
        }
 
2162
        else
 
2163
        {
 
2164
                /*
 
2165
                 * XXX currently we do not support extending multi-dimensional arrays
 
2166
                 * during assignment
 
2167
                 */
 
2168
                for (i = 0; i < ndim; i++)
 
2169
                {
 
2170
                        if (indx[i] < lb[i] ||
 
2171
                                indx[i] >= (dim[i] + lb[i]))
 
2172
                                ereport(ERROR,
 
2173
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2174
                                                 errmsg("array subscript out of range")));
 
2175
                }
 
2176
        }
 
2177
 
 
2178
        /*
 
2179
         * Compute sizes of items and areas to copy
 
2180
         */
 
2181
        newnitems = ArrayGetNItems(ndim, dim);
 
2182
        if (newhasnulls)
 
2183
                overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, newnitems);
 
2184
        else
 
2185
                overheadlen = ARR_OVERHEAD_NONULLS(ndim);
 
2186
        oldnitems = ArrayGetNItems(ndim, ARR_DIMS(array));
 
2187
        oldnullbitmap = ARR_NULLBITMAP(array);
 
2188
        oldoverheadlen = ARR_DATA_OFFSET(array);
 
2189
        olddatasize = ARR_SIZE(array) - oldoverheadlen;
 
2190
        if (addedbefore)
 
2191
        {
 
2192
                offset = 0;
 
2193
                lenbefore = 0;
 
2194
                olditemlen = 0;
 
2195
                lenafter = olddatasize;
 
2196
        }
 
2197
        else if (addedafter)
 
2198
        {
 
2199
                offset = oldnitems;
 
2200
                lenbefore = olddatasize;
 
2201
                olditemlen = 0;
 
2202
                lenafter = 0;
 
2203
        }
 
2204
        else
 
2205
        {
 
2206
                offset = ArrayGetOffset(nSubscripts, dim, lb, indx);
 
2207
                elt_ptr = array_seek(ARR_DATA_PTR(array), 0, oldnullbitmap, offset,
 
2208
                                                         elmlen, elmbyval, elmalign);
 
2209
                lenbefore = (int) (elt_ptr - ARR_DATA_PTR(array));
 
2210
                if (array_get_isnull(oldnullbitmap, offset))
 
2211
                        olditemlen = 0;
 
2212
                else
 
2213
                {
 
2214
                        olditemlen = att_addlength_pointer(0, elmlen, elt_ptr);
 
2215
                        olditemlen = att_align_nominal(olditemlen, elmalign);
 
2216
                }
 
2217
                lenafter = (int) (olddatasize - lenbefore - olditemlen);
 
2218
        }
 
2219
 
 
2220
        if (isNull)
 
2221
                newitemlen = 0;
 
2222
        else
 
2223
        {
 
2224
                newitemlen = att_addlength_datum(0, elmlen, dataValue);
 
2225
                newitemlen = att_align_nominal(newitemlen, elmalign);
 
2226
        }
 
2227
 
 
2228
        newsize = overheadlen + lenbefore + newitemlen + lenafter;
 
2229
 
 
2230
        /*
 
2231
         * OK, create the new array and fill in header/dimensions
 
2232
         */
 
2233
        newarray = (ArrayType *) palloc0(newsize);
 
2234
        SET_VARSIZE(newarray, newsize);
 
2235
        newarray->ndim = ndim;
 
2236
        newarray->dataoffset = newhasnulls ? overheadlen : 0;
 
2237
        newarray->elemtype = ARR_ELEMTYPE(array);
 
2238
        memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
 
2239
        memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
 
2240
 
 
2241
        /*
 
2242
         * Fill in data
 
2243
         */
 
2244
        memcpy((char *) newarray + overheadlen,
 
2245
                   (char *) array + oldoverheadlen,
 
2246
                   lenbefore);
 
2247
        if (!isNull)
 
2248
                ArrayCastAndSet(dataValue, elmlen, elmbyval, elmalign,
 
2249
                                                (char *) newarray + overheadlen + lenbefore);
 
2250
        memcpy((char *) newarray + overheadlen + lenbefore + newitemlen,
 
2251
                   (char *) array + oldoverheadlen + lenbefore + olditemlen,
 
2252
                   lenafter);
 
2253
 
 
2254
        /*
 
2255
         * Fill in nulls bitmap if needed
 
2256
         *
 
2257
         * Note: it's possible we just replaced the last NULL with a non-NULL, and
 
2258
         * could get rid of the bitmap.  Seems not worth testing for though.
 
2259
         */
 
2260
        if (newhasnulls)
 
2261
        {
 
2262
                bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
 
2263
 
 
2264
                /* Zero the bitmap to take care of marking inserted positions null */
 
2265
                MemSet(newnullbitmap, 0, (newnitems + 7) / 8);
 
2266
                /* Fix the inserted value */
 
2267
                if (addedafter)
 
2268
                        array_set_isnull(newnullbitmap, newnitems - 1, isNull);
 
2269
                else
 
2270
                        array_set_isnull(newnullbitmap, offset, isNull);
 
2271
                /* Fix the copied range(s) */
 
2272
                if (addedbefore)
 
2273
                        array_bitmap_copy(newnullbitmap, addedbefore,
 
2274
                                                          oldnullbitmap, 0,
 
2275
                                                          oldnitems);
 
2276
                else
 
2277
                {
 
2278
                        array_bitmap_copy(newnullbitmap, 0,
 
2279
                                                          oldnullbitmap, 0,
 
2280
                                                          offset);
 
2281
                        if (addedafter == 0)
 
2282
                                array_bitmap_copy(newnullbitmap, offset + 1,
 
2283
                                                                  oldnullbitmap, offset + 1,
 
2284
                                                                  oldnitems - offset - 1);
 
2285
                }
 
2286
        }
 
2287
 
 
2288
        return newarray;
 
2289
}
 
2290
 
 
2291
/*
 
2292
 * array_set_slice :
 
2293
 *                This routine sets the value of a range of array locations (specified
 
2294
 *                by upper and lower subscript values) to new values passed as
 
2295
 *                another array.
 
2296
 *
 
2297
 * This handles both ordinary varlena arrays and fixed-length arrays.
 
2298
 *
 
2299
 * Inputs:
 
2300
 *      array: the initial array object (mustn't be NULL)
 
2301
 *      nSubscripts: number of subscripts supplied (must be same for upper/lower)
 
2302
 *      upperIndx[]: the upper subscript values
 
2303
 *      lowerIndx[]: the lower subscript values
 
2304
 *      srcArray: the source for the inserted values
 
2305
 *      isNull: indicates whether srcArray is NULL
 
2306
 *      arraytyplen: pg_type.typlen for the array type
 
2307
 *      elmlen: pg_type.typlen for the array's element type
 
2308
 *      elmbyval: pg_type.typbyval for the array's element type
 
2309
 *      elmalign: pg_type.typalign for the array's element type
 
2310
 *
 
2311
 * Result:
 
2312
 *                A new array is returned, just like the old except for the
 
2313
 *                modified range.  The original array object is not changed.
 
2314
 *
 
2315
 * For one-dimensional arrays only, we allow the array to be extended
 
2316
 * by assigning to positions outside the existing subscript range; any
 
2317
 * positions between the existing elements and the new ones are set to NULLs.
 
2318
 * (XXX TODO: allow a corresponding behavior for multidimensional arrays)
 
2319
 *
 
2320
 * NOTE: we assume it is OK to scribble on the provided index arrays
 
2321
 * lowerIndx[] and upperIndx[].  These are generally just temporaries.
 
2322
 *
 
2323
 * NOTE: For assignments, we throw an error for silly subscripts etc,
 
2324
 * rather than returning a NULL or empty array as the fetch operations do.
 
2325
 */
 
2326
ArrayType *
 
2327
array_set_slice(ArrayType *array,
 
2328
                                int nSubscripts,
 
2329
                                int *upperIndx,
 
2330
                                int *lowerIndx,
 
2331
                                ArrayType *srcArray,
 
2332
                                bool isNull,
 
2333
                                int arraytyplen,
 
2334
                                int elmlen,
 
2335
                                bool elmbyval,
 
2336
                                char elmalign)
 
2337
{
 
2338
        ArrayType  *newarray;
 
2339
        int                     i,
 
2340
                                ndim,
 
2341
                                dim[MAXDIM],
 
2342
                                lb[MAXDIM],
 
2343
                                span[MAXDIM];
 
2344
        bool            newhasnulls;
 
2345
        int                     nitems,
 
2346
                                nsrcitems,
 
2347
                                olddatasize,
 
2348
                                newsize,
 
2349
                                olditemsize,
 
2350
                                newitemsize,
 
2351
                                overheadlen,
 
2352
                                oldoverheadlen,
 
2353
                                addedbefore,
 
2354
                                addedafter,
 
2355
                                lenbefore,
 
2356
                                lenafter,
 
2357
                                itemsbefore,
 
2358
                                itemsafter,
 
2359
                                nolditems;
 
2360
 
 
2361
        /* Currently, assignment from a NULL source array is a no-op */
 
2362
        if (isNull)
 
2363
                return array;
 
2364
 
 
2365
        if (arraytyplen > 0)
 
2366
        {
 
2367
                /*
 
2368
                 * fixed-length arrays -- not got round to doing this...
 
2369
                 */
 
2370
                ereport(ERROR,
 
2371
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
2372
                errmsg("updates on slices of fixed-length arrays not implemented")));
 
2373
        }
 
2374
 
 
2375
        /* detoast arrays if necessary */
 
2376
        array = DatumGetArrayTypeP(PointerGetDatum(array));
 
2377
        srcArray = DatumGetArrayTypeP(PointerGetDatum(srcArray));
 
2378
 
 
2379
        /* note: we assume srcArray contains no toasted elements */
 
2380
 
 
2381
        ndim = ARR_NDIM(array);
 
2382
 
 
2383
        /*
 
2384
         * if number of dims is zero, i.e. an empty array, create an array with
 
2385
         * nSubscripts dimensions, and set the upper and lower bounds to the
 
2386
         * supplied subscripts
 
2387
         */
 
2388
        if (ndim == 0)
 
2389
        {
 
2390
                Datum      *dvalues;
 
2391
                bool       *dnulls;
 
2392
                int                     nelems;
 
2393
                Oid                     elmtype = ARR_ELEMTYPE(array);
 
2394
 
 
2395
                deconstruct_array(srcArray, elmtype, elmlen, elmbyval, elmalign,
 
2396
                                                  &dvalues, &dnulls, &nelems);
 
2397
 
 
2398
                for (i = 0; i < nSubscripts; i++)
 
2399
                {
 
2400
                        dim[i] = 1 + upperIndx[i] - lowerIndx[i];
 
2401
                        lb[i] = lowerIndx[i];
 
2402
                }
 
2403
 
 
2404
                /* complain if too few source items; we ignore extras, however */
 
2405
                if (nelems < ArrayGetNItems(nSubscripts, dim))
 
2406
                        ereport(ERROR,
 
2407
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2408
                                         errmsg("source array too small")));
 
2409
 
 
2410
                return construct_md_array(dvalues, dnulls, nSubscripts,
 
2411
                                                                  dim, lb, elmtype,
 
2412
                                                                  elmlen, elmbyval, elmalign);
 
2413
        }
 
2414
 
 
2415
        if (ndim < nSubscripts || ndim <= 0 || ndim > MAXDIM)
 
2416
                ereport(ERROR,
 
2417
                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2418
                                 errmsg("wrong number of array subscripts")));
 
2419
 
 
2420
        /* copy dim/lb since we may modify them */
 
2421
        memcpy(dim, ARR_DIMS(array), ndim * sizeof(int));
 
2422
        memcpy(lb, ARR_LBOUND(array), ndim * sizeof(int));
 
2423
 
 
2424
        newhasnulls = (ARR_HASNULL(array) || ARR_HASNULL(srcArray));
 
2425
        addedbefore = addedafter = 0;
 
2426
 
 
2427
        /*
 
2428
         * Check subscripts
 
2429
         */
 
2430
        if (ndim == 1)
 
2431
        {
 
2432
                Assert(nSubscripts == 1);
 
2433
                if (lowerIndx[0] > upperIndx[0])
 
2434
                        ereport(ERROR,
 
2435
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2436
                                         errmsg("upper bound cannot be less than lower bound")));
 
2437
                if (lowerIndx[0] < lb[0])
 
2438
                {
 
2439
                        if (upperIndx[0] < lb[0] - 1)
 
2440
                                newhasnulls = true;             /* will insert nulls */
 
2441
                        addedbefore = lb[0] - lowerIndx[0];
 
2442
                        dim[0] += addedbefore;
 
2443
                        lb[0] = lowerIndx[0];
 
2444
                }
 
2445
                if (upperIndx[0] >= (dim[0] + lb[0]))
 
2446
                {
 
2447
                        if (lowerIndx[0] > (dim[0] + lb[0]))
 
2448
                                newhasnulls = true;             /* will insert nulls */
 
2449
                        addedafter = upperIndx[0] - (dim[0] + lb[0]) + 1;
 
2450
                        dim[0] += addedafter;
 
2451
                }
 
2452
        }
 
2453
        else
 
2454
        {
 
2455
                /*
 
2456
                 * XXX currently we do not support extending multi-dimensional arrays
 
2457
                 * during assignment
 
2458
                 */
 
2459
                for (i = 0; i < nSubscripts; i++)
 
2460
                {
 
2461
                        if (lowerIndx[i] > upperIndx[i])
 
2462
                                ereport(ERROR,
 
2463
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2464
                                         errmsg("upper bound cannot be less than lower bound")));
 
2465
                        if (lowerIndx[i] < lb[i] ||
 
2466
                                upperIndx[i] >= (dim[i] + lb[i]))
 
2467
                                ereport(ERROR,
 
2468
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2469
                                                 errmsg("array subscript out of range")));
 
2470
                }
 
2471
                /* fill any missing subscript positions with full array range */
 
2472
                for (; i < ndim; i++)
 
2473
                {
 
2474
                        lowerIndx[i] = lb[i];
 
2475
                        upperIndx[i] = dim[i] + lb[i] - 1;
 
2476
                        if (lowerIndx[i] > upperIndx[i])
 
2477
                                ereport(ERROR,
 
2478
                                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2479
                                         errmsg("upper bound cannot be less than lower bound")));
 
2480
                }
 
2481
        }
 
2482
 
 
2483
        /* Do this mainly to check for overflow */
 
2484
        nitems = ArrayGetNItems(ndim, dim);
 
2485
 
 
2486
        /*
 
2487
         * Make sure source array has enough entries.  Note we ignore the shape of
 
2488
         * the source array and just read entries serially.
 
2489
         */
 
2490
        mda_get_range(ndim, span, lowerIndx, upperIndx);
 
2491
        nsrcitems = ArrayGetNItems(ndim, span);
 
2492
        if (nsrcitems > ArrayGetNItems(ARR_NDIM(srcArray), ARR_DIMS(srcArray)))
 
2493
                ereport(ERROR,
 
2494
                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
2495
                                 errmsg("source array too small")));
 
2496
 
 
2497
        /*
 
2498
         * Compute space occupied by new entries, space occupied by replaced
 
2499
         * entries, and required space for new array.
 
2500
         */
 
2501
        if (newhasnulls)
 
2502
                overheadlen = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
 
2503
        else
 
2504
                overheadlen = ARR_OVERHEAD_NONULLS(ndim);
 
2505
        newitemsize = array_nelems_size(ARR_DATA_PTR(srcArray), 0,
 
2506
                                                                        ARR_NULLBITMAP(srcArray), nsrcitems,
 
2507
                                                                        elmlen, elmbyval, elmalign);
 
2508
        oldoverheadlen = ARR_DATA_OFFSET(array);
 
2509
        olddatasize = ARR_SIZE(array) - oldoverheadlen;
 
2510
        if (ndim > 1)
 
2511
        {
 
2512
                /*
 
2513
                 * here we do not need to cope with extension of the array; it would
 
2514
                 * be a lot more complicated if we had to do so...
 
2515
                 */
 
2516
                olditemsize = array_slice_size(ARR_DATA_PTR(array),
 
2517
                                                                           ARR_NULLBITMAP(array),
 
2518
                                                                           ndim, dim, lb,
 
2519
                                                                           lowerIndx, upperIndx,
 
2520
                                                                           elmlen, elmbyval, elmalign);
 
2521
                lenbefore = lenafter = 0;               /* keep compiler quiet */
 
2522
                itemsbefore = itemsafter = nolditems = 0;
 
2523
        }
 
2524
        else
 
2525
        {
 
2526
                /*
 
2527
                 * here we must allow for possibility of slice larger than orig array
 
2528
                 * and/or not adjacent to orig array subscripts
 
2529
                 */
 
2530
                int                     oldlb = ARR_LBOUND(array)[0];
 
2531
                int                     oldub = oldlb + ARR_DIMS(array)[0] - 1;
 
2532
                int                     slicelb = Max(oldlb, lowerIndx[0]);
 
2533
                int                     sliceub = Min(oldub, upperIndx[0]);
 
2534
                char       *oldarraydata = ARR_DATA_PTR(array);
 
2535
                bits8      *oldarraybitmap = ARR_NULLBITMAP(array);
 
2536
 
 
2537
                /* count/size of old array entries that will go before the slice */
 
2538
                itemsbefore = Min(slicelb, oldub + 1) - oldlb;
 
2539
                lenbefore = array_nelems_size(oldarraydata, 0, oldarraybitmap,
 
2540
                                                                          itemsbefore,
 
2541
                                                                          elmlen, elmbyval, elmalign);
 
2542
                /* count/size of old array entries that will be replaced by slice */
 
2543
                if (slicelb > sliceub)
 
2544
                {
 
2545
                        nolditems = 0;
 
2546
                        olditemsize = 0;
 
2547
                }
 
2548
                else
 
2549
                {
 
2550
                        nolditems = sliceub - slicelb + 1;
 
2551
                        olditemsize = array_nelems_size(oldarraydata + lenbefore,
 
2552
                                                                                        itemsbefore, oldarraybitmap,
 
2553
                                                                                        nolditems,
 
2554
                                                                                        elmlen, elmbyval, elmalign);
 
2555
                }
 
2556
                /* count/size of old array entries that will go after the slice */
 
2557
                itemsafter = oldub + 1 - Max(sliceub + 1, oldlb);
 
2558
                lenafter = olddatasize - lenbefore - olditemsize;
 
2559
        }
 
2560
 
 
2561
        newsize = overheadlen + olddatasize - olditemsize + newitemsize;
 
2562
 
 
2563
        newarray = (ArrayType *) palloc0(newsize);
 
2564
        SET_VARSIZE(newarray, newsize);
 
2565
        newarray->ndim = ndim;
 
2566
        newarray->dataoffset = newhasnulls ? overheadlen : 0;
 
2567
        newarray->elemtype = ARR_ELEMTYPE(array);
 
2568
        memcpy(ARR_DIMS(newarray), dim, ndim * sizeof(int));
 
2569
        memcpy(ARR_LBOUND(newarray), lb, ndim * sizeof(int));
 
2570
 
 
2571
        if (ndim > 1)
 
2572
        {
 
2573
                /*
 
2574
                 * here we do not need to cope with extension of the array; it would
 
2575
                 * be a lot more complicated if we had to do so...
 
2576
                 */
 
2577
                array_insert_slice(newarray, array, srcArray,
 
2578
                                                   ndim, dim, lb,
 
2579
                                                   lowerIndx, upperIndx,
 
2580
                                                   elmlen, elmbyval, elmalign);
 
2581
        }
 
2582
        else
 
2583
        {
 
2584
                /* fill in data */
 
2585
                memcpy((char *) newarray + overheadlen,
 
2586
                           (char *) array + oldoverheadlen,
 
2587
                           lenbefore);
 
2588
                memcpy((char *) newarray + overheadlen + lenbefore,
 
2589
                           ARR_DATA_PTR(srcArray),
 
2590
                           newitemsize);
 
2591
                memcpy((char *) newarray + overheadlen + lenbefore + newitemsize,
 
2592
                           (char *) array + oldoverheadlen + lenbefore + olditemsize,
 
2593
                           lenafter);
 
2594
                /* fill in nulls bitmap if needed */
 
2595
                if (newhasnulls)
 
2596
                {
 
2597
                        bits8      *newnullbitmap = ARR_NULLBITMAP(newarray);
 
2598
                        bits8      *oldnullbitmap = ARR_NULLBITMAP(array);
 
2599
 
 
2600
                        /* Zero the bitmap to handle marking inserted positions null */
 
2601
                        MemSet(newnullbitmap, 0, (nitems + 7) / 8);
 
2602
                        array_bitmap_copy(newnullbitmap, addedbefore,
 
2603
                                                          oldnullbitmap, 0,
 
2604
                                                          itemsbefore);
 
2605
                        array_bitmap_copy(newnullbitmap, lowerIndx[0] - lb[0],
 
2606
                                                          ARR_NULLBITMAP(srcArray), 0,
 
2607
                                                          nsrcitems);
 
2608
                        array_bitmap_copy(newnullbitmap, addedbefore + itemsbefore + nolditems,
 
2609
                                                          oldnullbitmap, itemsbefore + nolditems,
 
2610
                                                          itemsafter);
 
2611
                }
 
2612
        }
 
2613
 
 
2614
        return newarray;
 
2615
}
 
2616
 
 
2617
/*
 
2618
 * array_map()
 
2619
 *
 
2620
 * Map an array through an arbitrary function.  Return a new array with
 
2621
 * same dimensions and each source element transformed by fn().  Each
 
2622
 * source element is passed as the first argument to fn(); additional
 
2623
 * arguments to be passed to fn() can be specified by the caller.
 
2624
 * The output array can have a different element type than the input.
 
2625
 *
 
2626
 * Parameters are:
 
2627
 * * fcinfo: a function-call data structure pre-constructed by the caller
 
2628
 *       to be ready to call the desired function, with everything except the
 
2629
 *       first argument position filled in.  In particular, flinfo identifies
 
2630
 *       the function fn(), and if nargs > 1 then argument positions after the
 
2631
 *       first must be preset to the additional values to be passed.  The
 
2632
 *       first argument position initially holds the input array value.
 
2633
 * * inpType: OID of element type of input array.  This must be the same as,
 
2634
 *       or binary-compatible with, the first argument type of fn().
 
2635
 * * retType: OID of element type of output array.      This must be the same as,
 
2636
 *       or binary-compatible with, the result type of fn().
 
2637
 * * amstate: workspace for array_map.  Must be zeroed by caller before
 
2638
 *       first call, and not touched after that.
 
2639
 *
 
2640
 * It is legitimate to pass a freshly-zeroed ArrayMapState on each call,
 
2641
 * but better performance can be had if the state can be preserved across
 
2642
 * a series of calls.
 
2643
 *
 
2644
 * NB: caller must assure that input array is not NULL.  NULL elements in
 
2645
 * the array are OK however.
 
2646
 */
 
2647
Datum
 
2648
array_map(FunctionCallInfo fcinfo, Oid inpType, Oid retType,
 
2649
                  ArrayMapState *amstate)
 
2650
{
 
2651
        ArrayType  *v;
 
2652
        ArrayType  *result;
 
2653
        Datum      *values;
 
2654
        bool       *nulls;
 
2655
        Datum           elt;
 
2656
        int                *dim;
 
2657
        int                     ndim;
 
2658
        int                     nitems;
 
2659
        int                     i;
 
2660
        int32           nbytes = 0;
 
2661
        int32           dataoffset;
 
2662
        bool            hasnulls;
 
2663
        int                     inp_typlen;
 
2664
        bool            inp_typbyval;
 
2665
        char            inp_typalign;
 
2666
        int                     typlen;
 
2667
        bool            typbyval;
 
2668
        char            typalign;
 
2669
        char       *s;
 
2670
        bits8      *bitmap;
 
2671
        int                     bitmask;
 
2672
        ArrayMetaState *inp_extra;
 
2673
        ArrayMetaState *ret_extra;
 
2674
 
 
2675
        /* Get input array */
 
2676
        if (fcinfo->nargs < 1)
 
2677
                elog(ERROR, "invalid nargs: %d", fcinfo->nargs);
 
2678
        if (PG_ARGISNULL(0))
 
2679
                elog(ERROR, "null input array");
 
2680
        v = PG_GETARG_ARRAYTYPE_P(0);
 
2681
 
 
2682
        Assert(ARR_ELEMTYPE(v) == inpType);
 
2683
 
 
2684
        ndim = ARR_NDIM(v);
 
2685
        dim = ARR_DIMS(v);
 
2686
        nitems = ArrayGetNItems(ndim, dim);
 
2687
 
 
2688
        /* Check for empty array */
 
2689
        if (nitems <= 0)
 
2690
        {
 
2691
                /* Return empty array */
 
2692
                PG_RETURN_ARRAYTYPE_P(construct_empty_array(retType));
 
2693
        }
 
2694
 
 
2695
        /*
 
2696
         * We arrange to look up info about input and return element types only
 
2697
         * once per series of calls, assuming the element type doesn't change
 
2698
         * underneath us.
 
2699
         */
 
2700
        inp_extra = &amstate->inp_extra;
 
2701
        ret_extra = &amstate->ret_extra;
 
2702
 
 
2703
        if (inp_extra->element_type != inpType)
 
2704
        {
 
2705
                get_typlenbyvalalign(inpType,
 
2706
                                                         &inp_extra->typlen,
 
2707
                                                         &inp_extra->typbyval,
 
2708
                                                         &inp_extra->typalign);
 
2709
                inp_extra->element_type = inpType;
 
2710
        }
 
2711
        inp_typlen = inp_extra->typlen;
 
2712
        inp_typbyval = inp_extra->typbyval;
 
2713
        inp_typalign = inp_extra->typalign;
 
2714
 
 
2715
        if (ret_extra->element_type != retType)
 
2716
        {
 
2717
                get_typlenbyvalalign(retType,
 
2718
                                                         &ret_extra->typlen,
 
2719
                                                         &ret_extra->typbyval,
 
2720
                                                         &ret_extra->typalign);
 
2721
                ret_extra->element_type = retType;
 
2722
        }
 
2723
        typlen = ret_extra->typlen;
 
2724
        typbyval = ret_extra->typbyval;
 
2725
        typalign = ret_extra->typalign;
 
2726
 
 
2727
        /* Allocate temporary arrays for new values */
 
2728
        values = (Datum *) palloc(nitems * sizeof(Datum));
 
2729
        nulls = (bool *) palloc(nitems * sizeof(bool));
 
2730
 
 
2731
        /* Loop over source data */
 
2732
        s = ARR_DATA_PTR(v);
 
2733
        bitmap = ARR_NULLBITMAP(v);
 
2734
        bitmask = 1;
 
2735
        hasnulls = false;
 
2736
 
 
2737
        for (i = 0; i < nitems; i++)
 
2738
        {
 
2739
                bool            callit = true;
 
2740
 
 
2741
                /* Get source element, checking for NULL */
 
2742
                if (bitmap && (*bitmap & bitmask) == 0)
 
2743
                {
 
2744
                        fcinfo->argnull[0] = true;
 
2745
                }
 
2746
                else
 
2747
                {
 
2748
                        elt = fetch_att(s, inp_typbyval, inp_typlen);
 
2749
                        s = att_addlength_datum(s, inp_typlen, elt);
 
2750
                        s = (char *) att_align_nominal(s, inp_typalign);
 
2751
                        fcinfo->arg[0] = elt;
 
2752
                        fcinfo->argnull[0] = false;
 
2753
                }
 
2754
 
 
2755
                /*
 
2756
                 * Apply the given function to source elt and extra args.
 
2757
                 */
 
2758
                if (fcinfo->flinfo->fn_strict)
 
2759
                {
 
2760
                        int                     j;
 
2761
 
 
2762
                        for (j = 0; j < fcinfo->nargs; j++)
 
2763
                        {
 
2764
                                if (fcinfo->argnull[j])
 
2765
                                {
 
2766
                                        callit = false;
 
2767
                                        break;
 
2768
                                }
 
2769
                        }
 
2770
                }
 
2771
 
 
2772
                if (callit)
 
2773
                {
 
2774
                        fcinfo->isnull = false;
 
2775
                        values[i] = FunctionCallInvoke(fcinfo);
 
2776
                }
 
2777
                else
 
2778
                        fcinfo->isnull = true;
 
2779
 
 
2780
                nulls[i] = fcinfo->isnull;
 
2781
                if (fcinfo->isnull)
 
2782
                        hasnulls = true;
 
2783
                else
 
2784
                {
 
2785
                        /* Ensure data is not toasted */
 
2786
                        if (typlen == -1)
 
2787
                                values[i] = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
 
2788
                        /* Update total result size */
 
2789
                        nbytes = att_addlength_datum(nbytes, typlen, values[i]);
 
2790
                        nbytes = att_align_nominal(nbytes, typalign);
 
2791
                        /* check for overflow of total request */
 
2792
                        if (!AllocSizeIsValid(nbytes))
 
2793
                                ereport(ERROR,
 
2794
                                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
2795
                                                 errmsg("array size exceeds the maximum allowed (%d)",
 
2796
                                                                (int) MaxAllocSize)));
 
2797
                }
 
2798
 
 
2799
                /* advance bitmap pointer if any */
 
2800
                if (bitmap)
 
2801
                {
 
2802
                        bitmask <<= 1;
 
2803
                        if (bitmask == 0x100)
 
2804
                        {
 
2805
                                bitmap++;
 
2806
                                bitmask = 1;
 
2807
                        }
 
2808
                }
 
2809
        }
 
2810
 
 
2811
        /* Allocate and initialize the result array */
 
2812
        if (hasnulls)
 
2813
        {
 
2814
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndim, nitems);
 
2815
                nbytes += dataoffset;
 
2816
        }
 
2817
        else
 
2818
        {
 
2819
                dataoffset = 0;                 /* marker for no null bitmap */
 
2820
                nbytes += ARR_OVERHEAD_NONULLS(ndim);
 
2821
        }
 
2822
        result = (ArrayType *) palloc0(nbytes);
 
2823
        SET_VARSIZE(result, nbytes);
 
2824
        result->ndim = ndim;
 
2825
        result->dataoffset = dataoffset;
 
2826
        result->elemtype = retType;
 
2827
        memcpy(ARR_DIMS(result), ARR_DIMS(v), 2 * ndim * sizeof(int));
 
2828
 
 
2829
        /*
 
2830
         * Note: do not risk trying to pfree the results of the called function
 
2831
         */
 
2832
        CopyArrayEls(result,
 
2833
                                 values, nulls, nitems,
 
2834
                                 typlen, typbyval, typalign,
 
2835
                                 false);
 
2836
 
 
2837
        pfree(values);
 
2838
        pfree(nulls);
 
2839
 
 
2840
        PG_RETURN_ARRAYTYPE_P(result);
 
2841
}
 
2842
 
 
2843
/*
 
2844
 * construct_array      --- simple method for constructing an array object
 
2845
 *
 
2846
 * elems: array of Datum items to become the array contents
 
2847
 *                (NULL element values are not supported).
 
2848
 * nelems: number of items
 
2849
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
 
2850
 *
 
2851
 * A palloc'd 1-D array object is constructed and returned.  Note that
 
2852
 * elem values will be copied into the object even if pass-by-ref type.
 
2853
 *
 
2854
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
 
2855
 * from the system catalogs, given the elmtype.  However, the caller is
 
2856
 * in a better position to cache this info across multiple uses, or even
 
2857
 * to hard-wire values if the element type is hard-wired.
 
2858
 */
 
2859
ArrayType *
 
2860
construct_array(Datum *elems, int nelems,
 
2861
                                Oid elmtype,
 
2862
                                int elmlen, bool elmbyval, char elmalign)
 
2863
{
 
2864
        int                     dims[1];
 
2865
        int                     lbs[1];
 
2866
 
 
2867
        dims[0] = nelems;
 
2868
        lbs[0] = 1;
 
2869
 
 
2870
        return construct_md_array(elems, NULL, 1, dims, lbs,
 
2871
                                                          elmtype, elmlen, elmbyval, elmalign);
 
2872
}
 
2873
 
 
2874
/*
 
2875
 * construct_md_array   --- simple method for constructing an array object
 
2876
 *                                                      with arbitrary dimensions and possible NULLs
 
2877
 *
 
2878
 * elems: array of Datum items to become the array contents
 
2879
 * nulls: array of is-null flags (can be NULL if no nulls)
 
2880
 * ndims: number of dimensions
 
2881
 * dims: integer array with size of each dimension
 
2882
 * lbs: integer array with lower bound of each dimension
 
2883
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
 
2884
 *
 
2885
 * A palloc'd ndims-D array object is constructed and returned.  Note that
 
2886
 * elem values will be copied into the object even if pass-by-ref type.
 
2887
 *
 
2888
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
 
2889
 * from the system catalogs, given the elmtype.  However, the caller is
 
2890
 * in a better position to cache this info across multiple uses, or even
 
2891
 * to hard-wire values if the element type is hard-wired.
 
2892
 */
 
2893
ArrayType *
 
2894
construct_md_array(Datum *elems,
 
2895
                                   bool *nulls,
 
2896
                                   int ndims,
 
2897
                                   int *dims,
 
2898
                                   int *lbs,
 
2899
                                   Oid elmtype, int elmlen, bool elmbyval, char elmalign)
 
2900
{
 
2901
        ArrayType  *result;
 
2902
        bool            hasnulls;
 
2903
        int32           nbytes;
 
2904
        int32           dataoffset;
 
2905
        int                     i;
 
2906
        int                     nelems;
 
2907
 
 
2908
        if (ndims < 0)                          /* we do allow zero-dimension arrays */
 
2909
                ereport(ERROR,
 
2910
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 
2911
                                 errmsg("invalid number of dimensions: %d", ndims)));
 
2912
        if (ndims > MAXDIM)
 
2913
                ereport(ERROR,
 
2914
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
2915
                                 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 
2916
                                                ndims, MAXDIM)));
 
2917
 
 
2918
        /* fast track for empty array */
 
2919
        if (ndims == 0)
 
2920
                return construct_empty_array(elmtype);
 
2921
 
 
2922
        nelems = ArrayGetNItems(ndims, dims);
 
2923
 
 
2924
        /* compute required space */
 
2925
        nbytes = 0;
 
2926
        hasnulls = false;
 
2927
        for (i = 0; i < nelems; i++)
 
2928
        {
 
2929
                if (nulls && nulls[i])
 
2930
                {
 
2931
                        hasnulls = true;
 
2932
                        continue;
 
2933
                }
 
2934
                /* make sure data is not toasted */
 
2935
                if (elmlen == -1)
 
2936
                        elems[i] = PointerGetDatum(PG_DETOAST_DATUM(elems[i]));
 
2937
                nbytes = att_addlength_datum(nbytes, elmlen, elems[i]);
 
2938
                nbytes = att_align_nominal(nbytes, elmalign);
 
2939
                /* check for overflow of total request */
 
2940
                if (!AllocSizeIsValid(nbytes))
 
2941
                        ereport(ERROR,
 
2942
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
2943
                                         errmsg("array size exceeds the maximum allowed (%d)",
 
2944
                                                        (int) MaxAllocSize)));
 
2945
        }
 
2946
 
 
2947
        /* Allocate and initialize result array */
 
2948
        if (hasnulls)
 
2949
        {
 
2950
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nelems);
 
2951
                nbytes += dataoffset;
 
2952
        }
 
2953
        else
 
2954
        {
 
2955
                dataoffset = 0;                 /* marker for no null bitmap */
 
2956
                nbytes += ARR_OVERHEAD_NONULLS(ndims);
 
2957
        }
 
2958
        result = (ArrayType *) palloc0(nbytes);
 
2959
        SET_VARSIZE(result, nbytes);
 
2960
        result->ndim = ndims;
 
2961
        result->dataoffset = dataoffset;
 
2962
        result->elemtype = elmtype;
 
2963
        memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
 
2964
        memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
 
2965
 
 
2966
        CopyArrayEls(result,
 
2967
                                 elems, nulls, nelems,
 
2968
                                 elmlen, elmbyval, elmalign,
 
2969
                                 false);
 
2970
 
 
2971
        return result;
 
2972
}
 
2973
 
 
2974
/*
 
2975
 * construct_empty_array        --- make a zero-dimensional array of given type
 
2976
 */
 
2977
ArrayType *
 
2978
construct_empty_array(Oid elmtype)
 
2979
{
 
2980
        ArrayType  *result;
 
2981
 
 
2982
        result = (ArrayType *) palloc0(sizeof(ArrayType));
 
2983
        SET_VARSIZE(result, sizeof(ArrayType));
 
2984
        result->ndim = 0;
 
2985
        result->dataoffset = 0;
 
2986
        result->elemtype = elmtype;
 
2987
        return result;
 
2988
}
 
2989
 
 
2990
/*
 
2991
 * deconstruct_array  --- simple method for extracting data from an array
 
2992
 *
 
2993
 * array: array object to examine (must not be NULL)
 
2994
 * elmtype, elmlen, elmbyval, elmalign: info for the datatype of the items
 
2995
 * elemsp: return value, set to point to palloc'd array of Datum values
 
2996
 * nullsp: return value, set to point to palloc'd array of isnull markers
 
2997
 * nelemsp: return value, set to number of extracted values
 
2998
 *
 
2999
 * The caller may pass nullsp == NULL if it does not support NULLs in the
 
3000
 * array.  Note that this produces a very uninformative error message,
 
3001
 * so do it only in cases where a NULL is really not expected.
 
3002
 *
 
3003
 * If array elements are pass-by-ref data type, the returned Datums will
 
3004
 * be pointers into the array object.
 
3005
 *
 
3006
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
 
3007
 * from the system catalogs, given the elmtype.  However, in most current
 
3008
 * uses the type is hard-wired into the caller and so we can save a lookup
 
3009
 * cycle by hard-wiring the type info as well.
 
3010
 */
 
3011
void
 
3012
deconstruct_array(ArrayType *array,
 
3013
                                  Oid elmtype,
 
3014
                                  int elmlen, bool elmbyval, char elmalign,
 
3015
                                  Datum **elemsp, bool **nullsp, int *nelemsp)
 
3016
{
 
3017
        Datum      *elems;
 
3018
        bool       *nulls;
 
3019
        int                     nelems;
 
3020
        char       *p;
 
3021
        bits8      *bitmap;
 
3022
        int                     bitmask;
 
3023
        int                     i;
 
3024
 
 
3025
        Assert(ARR_ELEMTYPE(array) == elmtype);
 
3026
 
 
3027
        nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
 
3028
        *elemsp = elems = (Datum *) palloc(nelems * sizeof(Datum));
 
3029
        if (nullsp)
 
3030
                *nullsp = nulls = (bool *) palloc0(nelems * sizeof(bool));
 
3031
        else
 
3032
                nulls = NULL;
 
3033
        *nelemsp = nelems;
 
3034
 
 
3035
        p = ARR_DATA_PTR(array);
 
3036
        bitmap = ARR_NULLBITMAP(array);
 
3037
        bitmask = 1;
 
3038
 
 
3039
        for (i = 0; i < nelems; i++)
 
3040
        {
 
3041
                /* Get source element, checking for NULL */
 
3042
                if (bitmap && (*bitmap & bitmask) == 0)
 
3043
                {
 
3044
                        elems[i] = (Datum) 0;
 
3045
                        if (nulls)
 
3046
                                nulls[i] = true;
 
3047
                        else
 
3048
                                ereport(ERROR,
 
3049
                                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 
3050
                                  errmsg("null array element not allowed in this context")));
 
3051
                }
 
3052
                else
 
3053
                {
 
3054
                        elems[i] = fetch_att(p, elmbyval, elmlen);
 
3055
                        p = att_addlength_pointer(p, elmlen, p);
 
3056
                        p = (char *) att_align_nominal(p, elmalign);
 
3057
                }
 
3058
 
 
3059
                /* advance bitmap pointer if any */
 
3060
                if (bitmap)
 
3061
                {
 
3062
                        bitmask <<= 1;
 
3063
                        if (bitmask == 0x100)
 
3064
                        {
 
3065
                                bitmap++;
 
3066
                                bitmask = 1;
 
3067
                        }
 
3068
                }
 
3069
        }
 
3070
}
 
3071
 
 
3072
/*
 
3073
 * array_contains_nulls --- detect whether an array has any null elements
 
3074
 *
 
3075
 * This gives an accurate answer, whereas testing ARR_HASNULL only tells
 
3076
 * if the array *might* contain a null.
 
3077
 */
 
3078
bool
 
3079
array_contains_nulls(ArrayType *array)
 
3080
{
 
3081
        int                     nelems;
 
3082
        bits8      *bitmap;
 
3083
        int                     bitmask;
 
3084
 
 
3085
        /* Easy answer if there's no null bitmap */
 
3086
        if (!ARR_HASNULL(array))
 
3087
                return false;
 
3088
 
 
3089
        nelems = ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array));
 
3090
 
 
3091
        bitmap = ARR_NULLBITMAP(array);
 
3092
 
 
3093
        /* check whole bytes of the bitmap byte-at-a-time */
 
3094
        while (nelems >= 8)
 
3095
        {
 
3096
                if (*bitmap != 0xFF)
 
3097
                        return true;
 
3098
                bitmap++;
 
3099
                nelems -= 8;
 
3100
        }
 
3101
 
 
3102
        /* check last partial byte */
 
3103
        bitmask = 1;
 
3104
        while (nelems > 0)
 
3105
        {
 
3106
                if ((*bitmap & bitmask) == 0)
 
3107
                        return true;
 
3108
                bitmask <<= 1;
 
3109
                nelems--;
 
3110
        }
 
3111
 
 
3112
        return false;
 
3113
}
 
3114
 
 
3115
 
 
3116
/*
 
3117
 * array_eq :
 
3118
 *                compares two arrays for equality
 
3119
 * result :
 
3120
 *                returns true if the arrays are equal, false otherwise.
 
3121
 *
 
3122
 * Note: we do not use array_cmp here, since equality may be meaningful in
 
3123
 * datatypes that don't have a total ordering (and hence no btree support).
 
3124
 */
 
3125
Datum
 
3126
array_eq(PG_FUNCTION_ARGS)
 
3127
{
 
3128
        ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
 
3129
        ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
 
3130
        Oid                     collation = PG_GET_COLLATION();
 
3131
        int                     ndims1 = ARR_NDIM(array1);
 
3132
        int                     ndims2 = ARR_NDIM(array2);
 
3133
        int                *dims1 = ARR_DIMS(array1);
 
3134
        int                *dims2 = ARR_DIMS(array2);
 
3135
        Oid                     element_type = ARR_ELEMTYPE(array1);
 
3136
        bool            result = true;
 
3137
        int                     nitems;
 
3138
        TypeCacheEntry *typentry;
 
3139
        int                     typlen;
 
3140
        bool            typbyval;
 
3141
        char            typalign;
 
3142
        char       *ptr1;
 
3143
        char       *ptr2;
 
3144
        bits8      *bitmap1;
 
3145
        bits8      *bitmap2;
 
3146
        int                     bitmask;
 
3147
        int                     i;
 
3148
        FunctionCallInfoData locfcinfo;
 
3149
 
 
3150
        if (element_type != ARR_ELEMTYPE(array2))
 
3151
                ereport(ERROR,
 
3152
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
 
3153
                                 errmsg("cannot compare arrays of different element types")));
 
3154
 
 
3155
        /* fast path if the arrays do not have the same dimensionality */
 
3156
        if (ndims1 != ndims2 ||
 
3157
                memcmp(dims1, dims2, 2 * ndims1 * sizeof(int)) != 0)
 
3158
                result = false;
 
3159
        else
 
3160
        {
 
3161
                /*
 
3162
                 * We arrange to look up the equality function only once per series of
 
3163
                 * calls, assuming the element type doesn't change underneath us.  The
 
3164
                 * typcache is used so that we have no memory leakage when being used
 
3165
                 * as an index support function.
 
3166
                 */
 
3167
                typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 
3168
                if (typentry == NULL ||
 
3169
                        typentry->type_id != element_type)
 
3170
                {
 
3171
                        typentry = lookup_type_cache(element_type,
 
3172
                                                                                 TYPECACHE_EQ_OPR_FINFO);
 
3173
                        if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
 
3174
                                ereport(ERROR,
 
3175
                                                (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
3176
                                errmsg("could not identify an equality operator for type %s",
 
3177
                                           format_type_be(element_type))));
 
3178
                        fcinfo->flinfo->fn_extra = (void *) typentry;
 
3179
                }
 
3180
                typlen = typentry->typlen;
 
3181
                typbyval = typentry->typbyval;
 
3182
                typalign = typentry->typalign;
 
3183
 
 
3184
                /*
 
3185
                 * apply the operator to each pair of array elements.
 
3186
                 */
 
3187
                InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
 
3188
                                                                 collation, NULL, NULL);
 
3189
 
 
3190
                /* Loop over source data */
 
3191
                nitems = ArrayGetNItems(ndims1, dims1);
 
3192
                ptr1 = ARR_DATA_PTR(array1);
 
3193
                ptr2 = ARR_DATA_PTR(array2);
 
3194
                bitmap1 = ARR_NULLBITMAP(array1);
 
3195
                bitmap2 = ARR_NULLBITMAP(array2);
 
3196
                bitmask = 1;                    /* use same bitmask for both arrays */
 
3197
 
 
3198
                for (i = 0; i < nitems; i++)
 
3199
                {
 
3200
                        Datum           elt1;
 
3201
                        Datum           elt2;
 
3202
                        bool            isnull1;
 
3203
                        bool            isnull2;
 
3204
                        bool            oprresult;
 
3205
 
 
3206
                        /* Get elements, checking for NULL */
 
3207
                        if (bitmap1 && (*bitmap1 & bitmask) == 0)
 
3208
                        {
 
3209
                                isnull1 = true;
 
3210
                                elt1 = (Datum) 0;
 
3211
                        }
 
3212
                        else
 
3213
                        {
 
3214
                                isnull1 = false;
 
3215
                                elt1 = fetch_att(ptr1, typbyval, typlen);
 
3216
                                ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
 
3217
                                ptr1 = (char *) att_align_nominal(ptr1, typalign);
 
3218
                        }
 
3219
 
 
3220
                        if (bitmap2 && (*bitmap2 & bitmask) == 0)
 
3221
                        {
 
3222
                                isnull2 = true;
 
3223
                                elt2 = (Datum) 0;
 
3224
                        }
 
3225
                        else
 
3226
                        {
 
3227
                                isnull2 = false;
 
3228
                                elt2 = fetch_att(ptr2, typbyval, typlen);
 
3229
                                ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
 
3230
                                ptr2 = (char *) att_align_nominal(ptr2, typalign);
 
3231
                        }
 
3232
 
 
3233
                        /* advance bitmap pointers if any */
 
3234
                        bitmask <<= 1;
 
3235
                        if (bitmask == 0x100)
 
3236
                        {
 
3237
                                if (bitmap1)
 
3238
                                        bitmap1++;
 
3239
                                if (bitmap2)
 
3240
                                        bitmap2++;
 
3241
                                bitmask = 1;
 
3242
                        }
 
3243
 
 
3244
                        /*
 
3245
                         * We consider two NULLs equal; NULL and not-NULL are unequal.
 
3246
                         */
 
3247
                        if (isnull1 && isnull2)
 
3248
                                continue;
 
3249
                        if (isnull1 || isnull2)
 
3250
                        {
 
3251
                                result = false;
 
3252
                                break;
 
3253
                        }
 
3254
 
 
3255
                        /*
 
3256
                         * Apply the operator to the element pair
 
3257
                         */
 
3258
                        locfcinfo.arg[0] = elt1;
 
3259
                        locfcinfo.arg[1] = elt2;
 
3260
                        locfcinfo.argnull[0] = false;
 
3261
                        locfcinfo.argnull[1] = false;
 
3262
                        locfcinfo.isnull = false;
 
3263
                        oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
 
3264
                        if (!oprresult)
 
3265
                        {
 
3266
                                result = false;
 
3267
                                break;
 
3268
                        }
 
3269
                }
 
3270
        }
 
3271
 
 
3272
        /* Avoid leaking memory when handed toasted input. */
 
3273
        PG_FREE_IF_COPY(array1, 0);
 
3274
        PG_FREE_IF_COPY(array2, 1);
 
3275
 
 
3276
        PG_RETURN_BOOL(result);
 
3277
}
 
3278
 
 
3279
 
 
3280
/*-----------------------------------------------------------------------------
 
3281
 * array-array bool operators:
 
3282
 *              Given two arrays, iterate comparison operators
 
3283
 *              over the array. Uses logic similar to text comparison
 
3284
 *              functions, except element-by-element instead of
 
3285
 *              character-by-character.
 
3286
 *----------------------------------------------------------------------------
 
3287
 */
 
3288
 
 
3289
Datum
 
3290
array_ne(PG_FUNCTION_ARGS)
 
3291
{
 
3292
        PG_RETURN_BOOL(!DatumGetBool(array_eq(fcinfo)));
 
3293
}
 
3294
 
 
3295
Datum
 
3296
array_lt(PG_FUNCTION_ARGS)
 
3297
{
 
3298
        PG_RETURN_BOOL(array_cmp(fcinfo) < 0);
 
3299
}
 
3300
 
 
3301
Datum
 
3302
array_gt(PG_FUNCTION_ARGS)
 
3303
{
 
3304
        PG_RETURN_BOOL(array_cmp(fcinfo) > 0);
 
3305
}
 
3306
 
 
3307
Datum
 
3308
array_le(PG_FUNCTION_ARGS)
 
3309
{
 
3310
        PG_RETURN_BOOL(array_cmp(fcinfo) <= 0);
 
3311
}
 
3312
 
 
3313
Datum
 
3314
array_ge(PG_FUNCTION_ARGS)
 
3315
{
 
3316
        PG_RETURN_BOOL(array_cmp(fcinfo) >= 0);
 
3317
}
 
3318
 
 
3319
Datum
 
3320
btarraycmp(PG_FUNCTION_ARGS)
 
3321
{
 
3322
        PG_RETURN_INT32(array_cmp(fcinfo));
 
3323
}
 
3324
 
 
3325
/*
 
3326
 * array_cmp()
 
3327
 * Internal comparison function for arrays.
 
3328
 *
 
3329
 * Returns -1, 0 or 1
 
3330
 */
 
3331
static int
 
3332
array_cmp(FunctionCallInfo fcinfo)
 
3333
{
 
3334
        ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
 
3335
        ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
 
3336
        Oid                     collation = PG_GET_COLLATION();
 
3337
        int                     ndims1 = ARR_NDIM(array1);
 
3338
        int                     ndims2 = ARR_NDIM(array2);
 
3339
        int                *dims1 = ARR_DIMS(array1);
 
3340
        int                *dims2 = ARR_DIMS(array2);
 
3341
        int                     nitems1 = ArrayGetNItems(ndims1, dims1);
 
3342
        int                     nitems2 = ArrayGetNItems(ndims2, dims2);
 
3343
        Oid                     element_type = ARR_ELEMTYPE(array1);
 
3344
        int                     result = 0;
 
3345
        TypeCacheEntry *typentry;
 
3346
        int                     typlen;
 
3347
        bool            typbyval;
 
3348
        char            typalign;
 
3349
        int                     min_nitems;
 
3350
        char       *ptr1;
 
3351
        char       *ptr2;
 
3352
        bits8      *bitmap1;
 
3353
        bits8      *bitmap2;
 
3354
        int                     bitmask;
 
3355
        int                     i;
 
3356
        FunctionCallInfoData locfcinfo;
 
3357
 
 
3358
        if (element_type != ARR_ELEMTYPE(array2))
 
3359
                ereport(ERROR,
 
3360
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
 
3361
                                 errmsg("cannot compare arrays of different element types")));
 
3362
 
 
3363
        /*
 
3364
         * We arrange to look up the comparison function only once per series of
 
3365
         * calls, assuming the element type doesn't change underneath us. The
 
3366
         * typcache is used so that we have no memory leakage when being used as
 
3367
         * an index support function.
 
3368
         */
 
3369
        typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 
3370
        if (typentry == NULL ||
 
3371
                typentry->type_id != element_type)
 
3372
        {
 
3373
                typentry = lookup_type_cache(element_type,
 
3374
                                                                         TYPECACHE_CMP_PROC_FINFO);
 
3375
                if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
 
3376
                        ereport(ERROR,
 
3377
                                        (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
3378
                           errmsg("could not identify a comparison function for type %s",
 
3379
                                          format_type_be(element_type))));
 
3380
                fcinfo->flinfo->fn_extra = (void *) typentry;
 
3381
        }
 
3382
        typlen = typentry->typlen;
 
3383
        typbyval = typentry->typbyval;
 
3384
        typalign = typentry->typalign;
 
3385
 
 
3386
        /*
 
3387
         * apply the operator to each pair of array elements.
 
3388
         */
 
3389
        InitFunctionCallInfoData(locfcinfo, &typentry->cmp_proc_finfo, 2,
 
3390
                                                         collation, NULL, NULL);
 
3391
 
 
3392
        /* Loop over source data */
 
3393
        min_nitems = Min(nitems1, nitems2);
 
3394
        ptr1 = ARR_DATA_PTR(array1);
 
3395
        ptr2 = ARR_DATA_PTR(array2);
 
3396
        bitmap1 = ARR_NULLBITMAP(array1);
 
3397
        bitmap2 = ARR_NULLBITMAP(array2);
 
3398
        bitmask = 1;                            /* use same bitmask for both arrays */
 
3399
 
 
3400
        for (i = 0; i < min_nitems; i++)
 
3401
        {
 
3402
                Datum           elt1;
 
3403
                Datum           elt2;
 
3404
                bool            isnull1;
 
3405
                bool            isnull2;
 
3406
                int32           cmpresult;
 
3407
 
 
3408
                /* Get elements, checking for NULL */
 
3409
                if (bitmap1 && (*bitmap1 & bitmask) == 0)
 
3410
                {
 
3411
                        isnull1 = true;
 
3412
                        elt1 = (Datum) 0;
 
3413
                }
 
3414
                else
 
3415
                {
 
3416
                        isnull1 = false;
 
3417
                        elt1 = fetch_att(ptr1, typbyval, typlen);
 
3418
                        ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
 
3419
                        ptr1 = (char *) att_align_nominal(ptr1, typalign);
 
3420
                }
 
3421
 
 
3422
                if (bitmap2 && (*bitmap2 & bitmask) == 0)
 
3423
                {
 
3424
                        isnull2 = true;
 
3425
                        elt2 = (Datum) 0;
 
3426
                }
 
3427
                else
 
3428
                {
 
3429
                        isnull2 = false;
 
3430
                        elt2 = fetch_att(ptr2, typbyval, typlen);
 
3431
                        ptr2 = att_addlength_pointer(ptr2, typlen, ptr2);
 
3432
                        ptr2 = (char *) att_align_nominal(ptr2, typalign);
 
3433
                }
 
3434
 
 
3435
                /* advance bitmap pointers if any */
 
3436
                bitmask <<= 1;
 
3437
                if (bitmask == 0x100)
 
3438
                {
 
3439
                        if (bitmap1)
 
3440
                                bitmap1++;
 
3441
                        if (bitmap2)
 
3442
                                bitmap2++;
 
3443
                        bitmask = 1;
 
3444
                }
 
3445
 
 
3446
                /*
 
3447
                 * We consider two NULLs equal; NULL > not-NULL.
 
3448
                 */
 
3449
                if (isnull1 && isnull2)
 
3450
                        continue;
 
3451
                if (isnull1)
 
3452
                {
 
3453
                        /* arg1 is greater than arg2 */
 
3454
                        result = 1;
 
3455
                        break;
 
3456
                }
 
3457
                if (isnull2)
 
3458
                {
 
3459
                        /* arg1 is less than arg2 */
 
3460
                        result = -1;
 
3461
                        break;
 
3462
                }
 
3463
 
 
3464
                /* Compare the pair of elements */
 
3465
                locfcinfo.arg[0] = elt1;
 
3466
                locfcinfo.arg[1] = elt2;
 
3467
                locfcinfo.argnull[0] = false;
 
3468
                locfcinfo.argnull[1] = false;
 
3469
                locfcinfo.isnull = false;
 
3470
                cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
 
3471
 
 
3472
                if (cmpresult == 0)
 
3473
                        continue;                       /* equal */
 
3474
 
 
3475
                if (cmpresult < 0)
 
3476
                {
 
3477
                        /* arg1 is less than arg2 */
 
3478
                        result = -1;
 
3479
                        break;
 
3480
                }
 
3481
                else
 
3482
                {
 
3483
                        /* arg1 is greater than arg2 */
 
3484
                        result = 1;
 
3485
                        break;
 
3486
                }
 
3487
        }
 
3488
 
 
3489
        /*
 
3490
         * If arrays contain same data (up to end of shorter one), apply
 
3491
         * additional rules to sort by dimensionality.  The relative significance
 
3492
         * of the different bits of information is historical; mainly we just care
 
3493
         * that we don't say "equal" for arrays of different dimensionality.
 
3494
         */
 
3495
        if (result == 0)
 
3496
        {
 
3497
                if (nitems1 != nitems2)
 
3498
                        result = (nitems1 < nitems2) ? -1 : 1;
 
3499
                else if (ndims1 != ndims2)
 
3500
                        result = (ndims1 < ndims2) ? -1 : 1;
 
3501
                else
 
3502
                {
 
3503
                        /* this relies on LB array immediately following DIMS array */
 
3504
                        for (i = 0; i < ndims1 * 2; i++)
 
3505
                        {
 
3506
                                if (dims1[i] != dims2[i])
 
3507
                                {
 
3508
                                        result = (dims1[i] < dims2[i]) ? -1 : 1;
 
3509
                                        break;
 
3510
                                }
 
3511
                        }
 
3512
                }
 
3513
        }
 
3514
 
 
3515
        /* Avoid leaking memory when handed toasted input. */
 
3516
        PG_FREE_IF_COPY(array1, 0);
 
3517
        PG_FREE_IF_COPY(array2, 1);
 
3518
 
 
3519
        return result;
 
3520
}
 
3521
 
 
3522
 
 
3523
/*-----------------------------------------------------------------------------
 
3524
 * array hashing
 
3525
 *              Hash the elements and combine the results.
 
3526
 *----------------------------------------------------------------------------
 
3527
 */
 
3528
 
 
3529
Datum
 
3530
hash_array(PG_FUNCTION_ARGS)
 
3531
{
 
3532
        ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
 
3533
        int                     ndims = ARR_NDIM(array);
 
3534
        int                *dims = ARR_DIMS(array);
 
3535
        Oid                     element_type = ARR_ELEMTYPE(array);
 
3536
        uint32          result = 0;
 
3537
        int                     nitems;
 
3538
        TypeCacheEntry *typentry;
 
3539
        int                     typlen;
 
3540
        bool            typbyval;
 
3541
        char            typalign;
 
3542
        char       *ptr;
 
3543
        bits8      *bitmap;
 
3544
        int                     bitmask;
 
3545
        int                     i;
 
3546
        FunctionCallInfoData locfcinfo;
 
3547
 
 
3548
        /*
 
3549
         * We arrange to look up the hash function only once per series of calls,
 
3550
         * assuming the element type doesn't change underneath us.  The typcache
 
3551
         * is used so that we have no memory leakage when being used as an index
 
3552
         * support function.
 
3553
         */
 
3554
        typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 
3555
        if (typentry == NULL ||
 
3556
                typentry->type_id != element_type)
 
3557
        {
 
3558
                typentry = lookup_type_cache(element_type,
 
3559
                                                                         TYPECACHE_HASH_PROC_FINFO);
 
3560
                if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
 
3561
                        ereport(ERROR,
 
3562
                                        (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
3563
                                         errmsg("could not identify a hash function for type %s",
 
3564
                                                        format_type_be(element_type))));
 
3565
                fcinfo->flinfo->fn_extra = (void *) typentry;
 
3566
        }
 
3567
        typlen = typentry->typlen;
 
3568
        typbyval = typentry->typbyval;
 
3569
        typalign = typentry->typalign;
 
3570
 
 
3571
        /*
 
3572
         * apply the hash function to each array element.
 
3573
         */
 
3574
        InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
 
3575
                                                         InvalidOid, NULL, NULL);
 
3576
 
 
3577
        /* Loop over source data */
 
3578
        nitems = ArrayGetNItems(ndims, dims);
 
3579
        ptr = ARR_DATA_PTR(array);
 
3580
        bitmap = ARR_NULLBITMAP(array);
 
3581
        bitmask = 1;
 
3582
 
 
3583
        for (i = 0; i < nitems; i++)
 
3584
        {
 
3585
                uint32          elthash;
 
3586
 
 
3587
                /* Get element, checking for NULL */
 
3588
                if (bitmap && (*bitmap & bitmask) == 0)
 
3589
                {
 
3590
                        /* Treat nulls as having hashvalue 0 */
 
3591
                        elthash = 0;
 
3592
                }
 
3593
                else
 
3594
                {
 
3595
                        Datum           elt;
 
3596
 
 
3597
                        elt = fetch_att(ptr, typbyval, typlen);
 
3598
                        ptr = att_addlength_pointer(ptr, typlen, ptr);
 
3599
                        ptr = (char *) att_align_nominal(ptr, typalign);
 
3600
 
 
3601
                        /* Apply the hash function */
 
3602
                        locfcinfo.arg[0] = elt;
 
3603
                        locfcinfo.argnull[0] = false;
 
3604
                        locfcinfo.isnull = false;
 
3605
                        elthash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
 
3606
                }
 
3607
 
 
3608
                /* advance bitmap pointer if any */
 
3609
                if (bitmap)
 
3610
                {
 
3611
                        bitmask <<= 1;
 
3612
                        if (bitmask == 0x100)
 
3613
                        {
 
3614
                                bitmap++;
 
3615
                                bitmask = 1;
 
3616
                        }
 
3617
                }
 
3618
 
 
3619
                /*
 
3620
                 * Combine hash values of successive elements by rotating the previous
 
3621
                 * value left 1 bit, then XOR'ing in the new element's hash value.
 
3622
                 */
 
3623
                result = (result << 1) | (result >> 31);
 
3624
                result ^= elthash;
 
3625
        }
 
3626
 
 
3627
        /* Avoid leaking memory when handed toasted input. */
 
3628
        PG_FREE_IF_COPY(array, 0);
 
3629
 
 
3630
        PG_RETURN_UINT32(result);
 
3631
}
 
3632
 
 
3633
 
 
3634
/*-----------------------------------------------------------------------------
 
3635
 * array overlap/containment comparisons
 
3636
 *              These use the same methods of comparing array elements as array_eq.
 
3637
 *              We consider only the elements of the arrays, ignoring dimensionality.
 
3638
 *----------------------------------------------------------------------------
 
3639
 */
 
3640
 
 
3641
/*
 
3642
 * array_contain_compare :
 
3643
 *                compares two arrays for overlap/containment
 
3644
 *
 
3645
 * When matchall is true, return true if all members of array1 are in array2.
 
3646
 * When matchall is false, return true if any members of array1 are in array2.
 
3647
 */
 
3648
static bool
 
3649
array_contain_compare(ArrayType *array1, ArrayType *array2, Oid collation,
 
3650
                                          bool matchall, void **fn_extra)
 
3651
{
 
3652
        bool            result = matchall;
 
3653
        Oid                     element_type = ARR_ELEMTYPE(array1);
 
3654
        TypeCacheEntry *typentry;
 
3655
        int                     nelems1;
 
3656
        Datum      *values2;
 
3657
        bool       *nulls2;
 
3658
        int                     nelems2;
 
3659
        int                     typlen;
 
3660
        bool            typbyval;
 
3661
        char            typalign;
 
3662
        char       *ptr1;
 
3663
        bits8      *bitmap1;
 
3664
        int                     bitmask;
 
3665
        int                     i;
 
3666
        int                     j;
 
3667
        FunctionCallInfoData locfcinfo;
 
3668
 
 
3669
        if (element_type != ARR_ELEMTYPE(array2))
 
3670
                ereport(ERROR,
 
3671
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
 
3672
                                 errmsg("cannot compare arrays of different element types")));
 
3673
 
 
3674
        /*
 
3675
         * We arrange to look up the equality function only once per series of
 
3676
         * calls, assuming the element type doesn't change underneath us.  The
 
3677
         * typcache is used so that we have no memory leakage when being used as
 
3678
         * an index support function.
 
3679
         */
 
3680
        typentry = (TypeCacheEntry *) *fn_extra;
 
3681
        if (typentry == NULL ||
 
3682
                typentry->type_id != element_type)
 
3683
        {
 
3684
                typentry = lookup_type_cache(element_type,
 
3685
                                                                         TYPECACHE_EQ_OPR_FINFO);
 
3686
                if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
 
3687
                        ereport(ERROR,
 
3688
                                        (errcode(ERRCODE_UNDEFINED_FUNCTION),
 
3689
                                errmsg("could not identify an equality operator for type %s",
 
3690
                                           format_type_be(element_type))));
 
3691
                *fn_extra = (void *) typentry;
 
3692
        }
 
3693
        typlen = typentry->typlen;
 
3694
        typbyval = typentry->typbyval;
 
3695
        typalign = typentry->typalign;
 
3696
 
 
3697
        /*
 
3698
         * Since we probably will need to scan array2 multiple times, it's
 
3699
         * worthwhile to use deconstruct_array on it.  We scan array1 the hard way
 
3700
         * however, since we very likely won't need to look at all of it.
 
3701
         */
 
3702
        deconstruct_array(array2, element_type, typlen, typbyval, typalign,
 
3703
                                          &values2, &nulls2, &nelems2);
 
3704
 
 
3705
        /*
 
3706
         * Apply the comparison operator to each pair of array elements.
 
3707
         */
 
3708
        InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
 
3709
                                                         collation, NULL, NULL);
 
3710
 
 
3711
        /* Loop over source data */
 
3712
        nelems1 = ArrayGetNItems(ARR_NDIM(array1), ARR_DIMS(array1));
 
3713
        ptr1 = ARR_DATA_PTR(array1);
 
3714
        bitmap1 = ARR_NULLBITMAP(array1);
 
3715
        bitmask = 1;
 
3716
 
 
3717
        for (i = 0; i < nelems1; i++)
 
3718
        {
 
3719
                Datum           elt1;
 
3720
                bool            isnull1;
 
3721
 
 
3722
                /* Get element, checking for NULL */
 
3723
                if (bitmap1 && (*bitmap1 & bitmask) == 0)
 
3724
                {
 
3725
                        isnull1 = true;
 
3726
                        elt1 = (Datum) 0;
 
3727
                }
 
3728
                else
 
3729
                {
 
3730
                        isnull1 = false;
 
3731
                        elt1 = fetch_att(ptr1, typbyval, typlen);
 
3732
                        ptr1 = att_addlength_pointer(ptr1, typlen, ptr1);
 
3733
                        ptr1 = (char *) att_align_nominal(ptr1, typalign);
 
3734
                }
 
3735
 
 
3736
                /* advance bitmap pointer if any */
 
3737
                bitmask <<= 1;
 
3738
                if (bitmask == 0x100)
 
3739
                {
 
3740
                        if (bitmap1)
 
3741
                                bitmap1++;
 
3742
                        bitmask = 1;
 
3743
                }
 
3744
 
 
3745
                /*
 
3746
                 * We assume that the comparison operator is strict, so a NULL can't
 
3747
                 * match anything.      XXX this diverges from the "NULL=NULL" behavior of
 
3748
                 * array_eq, should we act like that?
 
3749
                 */
 
3750
                if (isnull1)
 
3751
                {
 
3752
                        if (matchall)
 
3753
                        {
 
3754
                                result = false;
 
3755
                                break;
 
3756
                        }
 
3757
                        continue;
 
3758
                }
 
3759
 
 
3760
                for (j = 0; j < nelems2; j++)
 
3761
                {
 
3762
                        Datum           elt2 = values2[j];
 
3763
                        bool            isnull2 = nulls2[j];
 
3764
                        bool            oprresult;
 
3765
 
 
3766
                        if (isnull2)
 
3767
                                continue;               /* can't match */
 
3768
 
 
3769
                        /*
 
3770
                         * Apply the operator to the element pair
 
3771
                         */
 
3772
                        locfcinfo.arg[0] = elt1;
 
3773
                        locfcinfo.arg[1] = elt2;
 
3774
                        locfcinfo.argnull[0] = false;
 
3775
                        locfcinfo.argnull[1] = false;
 
3776
                        locfcinfo.isnull = false;
 
3777
                        oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
 
3778
                        if (oprresult)
 
3779
                                break;
 
3780
                }
 
3781
 
 
3782
                if (j < nelems2)
 
3783
                {
 
3784
                        /* found a match for elt1 */
 
3785
                        if (!matchall)
 
3786
                        {
 
3787
                                result = true;
 
3788
                                break;
 
3789
                        }
 
3790
                }
 
3791
                else
 
3792
                {
 
3793
                        /* no match for elt1 */
 
3794
                        if (matchall)
 
3795
                        {
 
3796
                                result = false;
 
3797
                                break;
 
3798
                        }
 
3799
                }
 
3800
        }
 
3801
 
 
3802
        pfree(values2);
 
3803
        pfree(nulls2);
 
3804
 
 
3805
        return result;
 
3806
}
 
3807
 
 
3808
Datum
 
3809
arrayoverlap(PG_FUNCTION_ARGS)
 
3810
{
 
3811
        ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
 
3812
        ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
 
3813
        Oid                     collation = PG_GET_COLLATION();
 
3814
        bool            result;
 
3815
 
 
3816
        result = array_contain_compare(array1, array2, collation, false,
 
3817
                                                                   &fcinfo->flinfo->fn_extra);
 
3818
 
 
3819
        /* Avoid leaking memory when handed toasted input. */
 
3820
        PG_FREE_IF_COPY(array1, 0);
 
3821
        PG_FREE_IF_COPY(array2, 1);
 
3822
 
 
3823
        PG_RETURN_BOOL(result);
 
3824
}
 
3825
 
 
3826
Datum
 
3827
arraycontains(PG_FUNCTION_ARGS)
 
3828
{
 
3829
        ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
 
3830
        ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
 
3831
        Oid                     collation = PG_GET_COLLATION();
 
3832
        bool            result;
 
3833
 
 
3834
        result = array_contain_compare(array2, array1, collation, true,
 
3835
                                                                   &fcinfo->flinfo->fn_extra);
 
3836
 
 
3837
        /* Avoid leaking memory when handed toasted input. */
 
3838
        PG_FREE_IF_COPY(array1, 0);
 
3839
        PG_FREE_IF_COPY(array2, 1);
 
3840
 
 
3841
        PG_RETURN_BOOL(result);
 
3842
}
 
3843
 
 
3844
Datum
 
3845
arraycontained(PG_FUNCTION_ARGS)
 
3846
{
 
3847
        ArrayType  *array1 = PG_GETARG_ARRAYTYPE_P(0);
 
3848
        ArrayType  *array2 = PG_GETARG_ARRAYTYPE_P(1);
 
3849
        Oid                     collation = PG_GET_COLLATION();
 
3850
        bool            result;
 
3851
 
 
3852
        result = array_contain_compare(array1, array2, collation, true,
 
3853
                                                                   &fcinfo->flinfo->fn_extra);
 
3854
 
 
3855
        /* Avoid leaking memory when handed toasted input. */
 
3856
        PG_FREE_IF_COPY(array1, 0);
 
3857
        PG_FREE_IF_COPY(array2, 1);
 
3858
 
 
3859
        PG_RETURN_BOOL(result);
 
3860
}
 
3861
 
 
3862
 
 
3863
/*-----------------------------------------------------------------------------
 
3864
 * Array iteration functions
 
3865
 *              These functions are used to iterate efficiently through arrays
 
3866
 *-----------------------------------------------------------------------------
 
3867
 */
 
3868
 
 
3869
/*
 
3870
 * array_create_iterator --- set up to iterate through an array
 
3871
 *
 
3872
 * If slice_ndim is zero, we will iterate element-by-element; the returned
 
3873
 * datums are of the array's element type.
 
3874
 *
 
3875
 * If slice_ndim is 1..ARR_NDIM(arr), we will iterate by slices: the
 
3876
 * returned datums are of the same array type as 'arr', but of size
 
3877
 * equal to the rightmost N dimensions of 'arr'.
 
3878
 *
 
3879
 * The passed-in array must remain valid for the lifetime of the iterator.
 
3880
 */
 
3881
ArrayIterator
 
3882
array_create_iterator(ArrayType *arr, int slice_ndim)
 
3883
{
 
3884
        ArrayIterator iterator = palloc0(sizeof(ArrayIteratorData));
 
3885
 
 
3886
        /*
 
3887
         * Sanity-check inputs --- caller should have got this right already
 
3888
         */
 
3889
        Assert(PointerIsValid(arr));
 
3890
        if (slice_ndim < 0 || slice_ndim > ARR_NDIM(arr))
 
3891
                elog(ERROR, "invalid arguments to array_create_iterator");
 
3892
 
 
3893
        /*
 
3894
         * Remember basic info about the array and its element type
 
3895
         */
 
3896
        iterator->arr = arr;
 
3897
        iterator->nullbitmap = ARR_NULLBITMAP(arr);
 
3898
        iterator->nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
 
3899
        get_typlenbyvalalign(ARR_ELEMTYPE(arr),
 
3900
                                                 &iterator->typlen,
 
3901
                                                 &iterator->typbyval,
 
3902
                                                 &iterator->typalign);
 
3903
 
 
3904
        /*
 
3905
         * Remember the slicing parameters.
 
3906
         */
 
3907
        iterator->slice_ndim = slice_ndim;
 
3908
 
 
3909
        if (slice_ndim > 0)
 
3910
        {
 
3911
                /*
 
3912
                 * Get pointers into the array's dims and lbound arrays to represent
 
3913
                 * the dims/lbound arrays of a slice.  These are the same as the
 
3914
                 * rightmost N dimensions of the array.
 
3915
                 */
 
3916
                iterator->slice_dims = ARR_DIMS(arr) + ARR_NDIM(arr) - slice_ndim;
 
3917
                iterator->slice_lbound = ARR_LBOUND(arr) + ARR_NDIM(arr) - slice_ndim;
 
3918
 
 
3919
                /*
 
3920
                 * Compute number of elements in a slice.
 
3921
                 */
 
3922
                iterator->slice_len = ArrayGetNItems(slice_ndim,
 
3923
                                                                                         iterator->slice_dims);
 
3924
 
 
3925
                /*
 
3926
                 * Create workspace for building sub-arrays.
 
3927
                 */
 
3928
                iterator->slice_values = (Datum *)
 
3929
                        palloc(iterator->slice_len * sizeof(Datum));
 
3930
                iterator->slice_nulls = (bool *)
 
3931
                        palloc(iterator->slice_len * sizeof(bool));
 
3932
        }
 
3933
 
 
3934
        /*
 
3935
         * Initialize our data pointer and linear element number.  These will
 
3936
         * advance through the array during array_iterate().
 
3937
         */
 
3938
        iterator->data_ptr = ARR_DATA_PTR(arr);
 
3939
        iterator->current_item = 0;
 
3940
 
 
3941
        return iterator;
 
3942
}
 
3943
 
 
3944
/*
 
3945
 * Iterate through the array referenced by 'iterator'.
 
3946
 *
 
3947
 * As long as there is another element (or slice), return it into
 
3948
 * *value / *isnull, and return true.  Return false when no more data.
 
3949
 */
 
3950
bool
 
3951
array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
 
3952
{
 
3953
        /* Done if we have reached the end of the array */
 
3954
        if (iterator->current_item >= iterator->nitems)
 
3955
                return false;
 
3956
 
 
3957
        if (iterator->slice_ndim == 0)
 
3958
        {
 
3959
                /*
 
3960
                 * Scalar case: return one element.
 
3961
                 */
 
3962
                if (array_get_isnull(iterator->nullbitmap, iterator->current_item++))
 
3963
                {
 
3964
                        *isnull = true;
 
3965
                        *value = (Datum) 0;
 
3966
                }
 
3967
                else
 
3968
                {
 
3969
                        /* non-NULL, so fetch the individual Datum to return */
 
3970
                        char       *p = iterator->data_ptr;
 
3971
 
 
3972
                        *isnull = false;
 
3973
                        *value = fetch_att(p, iterator->typbyval, iterator->typlen);
 
3974
 
 
3975
                        /* Move our data pointer forward to the next element */
 
3976
                        p = att_addlength_pointer(p, iterator->typlen, p);
 
3977
                        p = (char *) att_align_nominal(p, iterator->typalign);
 
3978
                        iterator->data_ptr = p;
 
3979
                }
 
3980
        }
 
3981
        else
 
3982
        {
 
3983
                /*
 
3984
                 * Slice case: build and return an array of the requested size.
 
3985
                 */
 
3986
                ArrayType  *result;
 
3987
                Datum      *values = iterator->slice_values;
 
3988
                bool       *nulls = iterator->slice_nulls;
 
3989
                char       *p = iterator->data_ptr;
 
3990
                int                     i;
 
3991
 
 
3992
                for (i = 0; i < iterator->slice_len; i++)
 
3993
                {
 
3994
                        if (array_get_isnull(iterator->nullbitmap,
 
3995
                                                                 iterator->current_item++))
 
3996
                        {
 
3997
                                nulls[i] = true;
 
3998
                                values[i] = (Datum) 0;
 
3999
                        }
 
4000
                        else
 
4001
                        {
 
4002
                                nulls[i] = false;
 
4003
                                values[i] = fetch_att(p, iterator->typbyval, iterator->typlen);
 
4004
 
 
4005
                                /* Move our data pointer forward to the next element */
 
4006
                                p = att_addlength_pointer(p, iterator->typlen, p);
 
4007
                                p = (char *) att_align_nominal(p, iterator->typalign);
 
4008
                        }
 
4009
                }
 
4010
 
 
4011
                iterator->data_ptr = p;
 
4012
 
 
4013
                result = construct_md_array(values,
 
4014
                                                                        nulls,
 
4015
                                                                        iterator->slice_ndim,
 
4016
                                                                        iterator->slice_dims,
 
4017
                                                                        iterator->slice_lbound,
 
4018
                                                                        ARR_ELEMTYPE(iterator->arr),
 
4019
                                                                        iterator->typlen,
 
4020
                                                                        iterator->typbyval,
 
4021
                                                                        iterator->typalign);
 
4022
 
 
4023
                *isnull = false;
 
4024
                *value = PointerGetDatum(result);
 
4025
        }
 
4026
 
 
4027
        return true;
 
4028
}
 
4029
 
 
4030
/*
 
4031
 * Release an ArrayIterator data structure
 
4032
 */
 
4033
void
 
4034
array_free_iterator(ArrayIterator iterator)
 
4035
{
 
4036
        if (iterator->slice_ndim > 0)
 
4037
        {
 
4038
                pfree(iterator->slice_values);
 
4039
                pfree(iterator->slice_nulls);
 
4040
        }
 
4041
        pfree(iterator);
 
4042
}
 
4043
 
 
4044
 
 
4045
/***************************************************************************/
 
4046
/******************|              Support  Routines                       |*****************/
 
4047
/***************************************************************************/
 
4048
 
 
4049
/*
 
4050
 * Check whether a specific array element is NULL
 
4051
 *
 
4052
 * nullbitmap: pointer to array's null bitmap (NULL if none)
 
4053
 * offset: 0-based linear element number of array element
 
4054
 */
 
4055
static bool
 
4056
array_get_isnull(const bits8 *nullbitmap, int offset)
 
4057
{
 
4058
        if (nullbitmap == NULL)
 
4059
                return false;                   /* assume not null */
 
4060
        if (nullbitmap[offset / 8] & (1 << (offset % 8)))
 
4061
                return false;                   /* not null */
 
4062
        return true;
 
4063
}
 
4064
 
 
4065
/*
 
4066
 * Set a specific array element's null-bitmap entry
 
4067
 *
 
4068
 * nullbitmap: pointer to array's null bitmap (mustn't be NULL)
 
4069
 * offset: 0-based linear element number of array element
 
4070
 * isNull: null status to set
 
4071
 */
 
4072
static void
 
4073
array_set_isnull(bits8 *nullbitmap, int offset, bool isNull)
 
4074
{
 
4075
        int                     bitmask;
 
4076
 
 
4077
        nullbitmap += offset / 8;
 
4078
        bitmask = 1 << (offset % 8);
 
4079
        if (isNull)
 
4080
                *nullbitmap &= ~bitmask;
 
4081
        else
 
4082
                *nullbitmap |= bitmask;
 
4083
}
 
4084
 
 
4085
/*
 
4086
 * Fetch array element at pointer, converted correctly to a Datum
 
4087
 *
 
4088
 * Caller must have handled case of NULL element
 
4089
 */
 
4090
static Datum
 
4091
ArrayCast(char *value, bool byval, int len)
 
4092
{
 
4093
        return fetch_att(value, byval, len);
 
4094
}
 
4095
 
 
4096
/*
 
4097
 * Copy datum to *dest and return total space used (including align padding)
 
4098
 *
 
4099
 * Caller must have handled case of NULL element
 
4100
 */
 
4101
static int
 
4102
ArrayCastAndSet(Datum src,
 
4103
                                int typlen,
 
4104
                                bool typbyval,
 
4105
                                char typalign,
 
4106
                                char *dest)
 
4107
{
 
4108
        int                     inc;
 
4109
 
 
4110
        if (typlen > 0)
 
4111
        {
 
4112
                if (typbyval)
 
4113
                        store_att_byval(dest, src, typlen);
 
4114
                else
 
4115
                        memmove(dest, DatumGetPointer(src), typlen);
 
4116
                inc = att_align_nominal(typlen, typalign);
 
4117
        }
 
4118
        else
 
4119
        {
 
4120
                Assert(!typbyval);
 
4121
                inc = att_addlength_datum(0, typlen, src);
 
4122
                memmove(dest, DatumGetPointer(src), inc);
 
4123
                inc = att_align_nominal(inc, typalign);
 
4124
        }
 
4125
 
 
4126
        return inc;
 
4127
}
 
4128
 
 
4129
/*
 
4130
 * Advance ptr over nitems array elements
 
4131
 *
 
4132
 * ptr: starting location in array
 
4133
 * offset: 0-based linear element number of first element (the one at *ptr)
 
4134
 * nullbitmap: start of array's null bitmap, or NULL if none
 
4135
 * nitems: number of array elements to advance over (>= 0)
 
4136
 * typlen, typbyval, typalign: storage parameters of array element datatype
 
4137
 *
 
4138
 * It is caller's responsibility to ensure that nitems is within range
 
4139
 */
 
4140
static char *
 
4141
array_seek(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 
4142
                   int typlen, bool typbyval, char typalign)
 
4143
{
 
4144
        int                     bitmask;
 
4145
        int                     i;
 
4146
 
 
4147
        /* easy if fixed-size elements and no NULLs */
 
4148
        if (typlen > 0 && !nullbitmap)
 
4149
                return ptr + nitems * ((Size) att_align_nominal(typlen, typalign));
 
4150
 
 
4151
        /* seems worth having separate loops for NULL and no-NULLs cases */
 
4152
        if (nullbitmap)
 
4153
        {
 
4154
                nullbitmap += offset / 8;
 
4155
                bitmask = 1 << (offset % 8);
 
4156
 
 
4157
                for (i = 0; i < nitems; i++)
 
4158
                {
 
4159
                        if (*nullbitmap & bitmask)
 
4160
                        {
 
4161
                                ptr = att_addlength_pointer(ptr, typlen, ptr);
 
4162
                                ptr = (char *) att_align_nominal(ptr, typalign);
 
4163
                        }
 
4164
                        bitmask <<= 1;
 
4165
                        if (bitmask == 0x100)
 
4166
                        {
 
4167
                                nullbitmap++;
 
4168
                                bitmask = 1;
 
4169
                        }
 
4170
                }
 
4171
        }
 
4172
        else
 
4173
        {
 
4174
                for (i = 0; i < nitems; i++)
 
4175
                {
 
4176
                        ptr = att_addlength_pointer(ptr, typlen, ptr);
 
4177
                        ptr = (char *) att_align_nominal(ptr, typalign);
 
4178
                }
 
4179
        }
 
4180
        return ptr;
 
4181
}
 
4182
 
 
4183
/*
 
4184
 * Compute total size of the nitems array elements starting at *ptr
 
4185
 *
 
4186
 * Parameters same as for array_seek
 
4187
 */
 
4188
static int
 
4189
array_nelems_size(char *ptr, int offset, bits8 *nullbitmap, int nitems,
 
4190
                                  int typlen, bool typbyval, char typalign)
 
4191
{
 
4192
        return array_seek(ptr, offset, nullbitmap, nitems,
 
4193
                                          typlen, typbyval, typalign) - ptr;
 
4194
}
 
4195
 
 
4196
/*
 
4197
 * Copy nitems array elements from srcptr to destptr
 
4198
 *
 
4199
 * destptr: starting destination location (must be enough room!)
 
4200
 * nitems: number of array elements to copy (>= 0)
 
4201
 * srcptr: starting location in source array
 
4202
 * offset: 0-based linear element number of first element (the one at *srcptr)
 
4203
 * nullbitmap: start of source array's null bitmap, or NULL if none
 
4204
 * typlen, typbyval, typalign: storage parameters of array element datatype
 
4205
 *
 
4206
 * Returns number of bytes copied
 
4207
 *
 
4208
 * NB: this does not take care of setting up the destination's null bitmap!
 
4209
 */
 
4210
static int
 
4211
array_copy(char *destptr, int nitems,
 
4212
                   char *srcptr, int offset, bits8 *nullbitmap,
 
4213
                   int typlen, bool typbyval, char typalign)
 
4214
{
 
4215
        int                     numbytes;
 
4216
 
 
4217
        numbytes = array_nelems_size(srcptr, offset, nullbitmap, nitems,
 
4218
                                                                 typlen, typbyval, typalign);
 
4219
        memcpy(destptr, srcptr, numbytes);
 
4220
        return numbytes;
 
4221
}
 
4222
 
 
4223
/*
 
4224
 * Copy nitems null-bitmap bits from source to destination
 
4225
 *
 
4226
 * destbitmap: start of destination array's null bitmap (mustn't be NULL)
 
4227
 * destoffset: 0-based linear element number of first dest element
 
4228
 * srcbitmap: start of source array's null bitmap, or NULL if none
 
4229
 * srcoffset: 0-based linear element number of first source element
 
4230
 * nitems: number of bits to copy (>= 0)
 
4231
 *
 
4232
 * If srcbitmap is NULL then we assume the source is all-non-NULL and
 
4233
 * fill 1's into the destination bitmap.  Note that only the specified
 
4234
 * bits in the destination map are changed, not any before or after.
 
4235
 *
 
4236
 * Note: this could certainly be optimized using standard bitblt methods.
 
4237
 * However, it's not clear that the typical Postgres array has enough elements
 
4238
 * to make it worth worrying too much.  For the moment, KISS.
 
4239
 */
 
4240
void
 
4241
array_bitmap_copy(bits8 *destbitmap, int destoffset,
 
4242
                                  const bits8 *srcbitmap, int srcoffset,
 
4243
                                  int nitems)
 
4244
{
 
4245
        int                     destbitmask,
 
4246
                                destbitval,
 
4247
                                srcbitmask,
 
4248
                                srcbitval;
 
4249
 
 
4250
        Assert(destbitmap);
 
4251
        if (nitems <= 0)
 
4252
                return;                                 /* don't risk fetch off end of memory */
 
4253
        destbitmap += destoffset / 8;
 
4254
        destbitmask = 1 << (destoffset % 8);
 
4255
        destbitval = *destbitmap;
 
4256
        if (srcbitmap)
 
4257
        {
 
4258
                srcbitmap += srcoffset / 8;
 
4259
                srcbitmask = 1 << (srcoffset % 8);
 
4260
                srcbitval = *srcbitmap;
 
4261
                while (nitems-- > 0)
 
4262
                {
 
4263
                        if (srcbitval & srcbitmask)
 
4264
                                destbitval |= destbitmask;
 
4265
                        else
 
4266
                                destbitval &= ~destbitmask;
 
4267
                        destbitmask <<= 1;
 
4268
                        if (destbitmask == 0x100)
 
4269
                        {
 
4270
                                *destbitmap++ = destbitval;
 
4271
                                destbitmask = 1;
 
4272
                                if (nitems > 0)
 
4273
                                        destbitval = *destbitmap;
 
4274
                        }
 
4275
                        srcbitmask <<= 1;
 
4276
                        if (srcbitmask == 0x100)
 
4277
                        {
 
4278
                                srcbitmap++;
 
4279
                                srcbitmask = 1;
 
4280
                                if (nitems > 0)
 
4281
                                        srcbitval = *srcbitmap;
 
4282
                        }
 
4283
                }
 
4284
                if (destbitmask != 1)
 
4285
                        *destbitmap = destbitval;
 
4286
        }
 
4287
        else
 
4288
        {
 
4289
                while (nitems-- > 0)
 
4290
                {
 
4291
                        destbitval |= destbitmask;
 
4292
                        destbitmask <<= 1;
 
4293
                        if (destbitmask == 0x100)
 
4294
                        {
 
4295
                                *destbitmap++ = destbitval;
 
4296
                                destbitmask = 1;
 
4297
                                if (nitems > 0)
 
4298
                                        destbitval = *destbitmap;
 
4299
                        }
 
4300
                }
 
4301
                if (destbitmask != 1)
 
4302
                        *destbitmap = destbitval;
 
4303
        }
 
4304
}
 
4305
 
 
4306
/*
 
4307
 * Compute space needed for a slice of an array
 
4308
 *
 
4309
 * We assume the caller has verified that the slice coordinates are valid.
 
4310
 */
 
4311
static int
 
4312
array_slice_size(char *arraydataptr, bits8 *arraynullsptr,
 
4313
                                 int ndim, int *dim, int *lb,
 
4314
                                 int *st, int *endp,
 
4315
                                 int typlen, bool typbyval, char typalign)
 
4316
{
 
4317
        int                     src_offset,
 
4318
                                span[MAXDIM],
 
4319
                                prod[MAXDIM],
 
4320
                                dist[MAXDIM],
 
4321
                                indx[MAXDIM];
 
4322
        char       *ptr;
 
4323
        int                     i,
 
4324
                                j,
 
4325
                                inc;
 
4326
        int                     count = 0;
 
4327
 
 
4328
        mda_get_range(ndim, span, st, endp);
 
4329
 
 
4330
        /* Pretty easy for fixed element length without nulls ... */
 
4331
        if (typlen > 0 && !arraynullsptr)
 
4332
                return ArrayGetNItems(ndim, span) * att_align_nominal(typlen, typalign);
 
4333
 
 
4334
        /* Else gotta do it the hard way */
 
4335
        src_offset = ArrayGetOffset(ndim, dim, lb, st);
 
4336
        ptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
 
4337
                                         typlen, typbyval, typalign);
 
4338
        mda_get_prod(ndim, dim, prod);
 
4339
        mda_get_offset_values(ndim, dist, prod, span);
 
4340
        for (i = 0; i < ndim; i++)
 
4341
                indx[i] = 0;
 
4342
        j = ndim - 1;
 
4343
        do
 
4344
        {
 
4345
                if (dist[j])
 
4346
                {
 
4347
                        ptr = array_seek(ptr, src_offset, arraynullsptr, dist[j],
 
4348
                                                         typlen, typbyval, typalign);
 
4349
                        src_offset += dist[j];
 
4350
                }
 
4351
                if (!array_get_isnull(arraynullsptr, src_offset))
 
4352
                {
 
4353
                        inc = att_addlength_pointer(0, typlen, ptr);
 
4354
                        inc = att_align_nominal(inc, typalign);
 
4355
                        ptr += inc;
 
4356
                        count += inc;
 
4357
                }
 
4358
                src_offset++;
 
4359
        } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
 
4360
        return count;
 
4361
}
 
4362
 
 
4363
/*
 
4364
 * Extract a slice of an array into consecutive elements in the destination
 
4365
 * array.
 
4366
 *
 
4367
 * We assume the caller has verified that the slice coordinates are valid,
 
4368
 * allocated enough storage for the result, and initialized the header
 
4369
 * of the new array.
 
4370
 */
 
4371
static void
 
4372
array_extract_slice(ArrayType *newarray,
 
4373
                                        int ndim,
 
4374
                                        int *dim,
 
4375
                                        int *lb,
 
4376
                                        char *arraydataptr,
 
4377
                                        bits8 *arraynullsptr,
 
4378
                                        int *st,
 
4379
                                        int *endp,
 
4380
                                        int typlen,
 
4381
                                        bool typbyval,
 
4382
                                        char typalign)
 
4383
{
 
4384
        char       *destdataptr = ARR_DATA_PTR(newarray);
 
4385
        bits8      *destnullsptr = ARR_NULLBITMAP(newarray);
 
4386
        char       *srcdataptr;
 
4387
        int                     src_offset,
 
4388
                                dest_offset,
 
4389
                                prod[MAXDIM],
 
4390
                                span[MAXDIM],
 
4391
                                dist[MAXDIM],
 
4392
                                indx[MAXDIM];
 
4393
        int                     i,
 
4394
                                j,
 
4395
                                inc;
 
4396
 
 
4397
        src_offset = ArrayGetOffset(ndim, dim, lb, st);
 
4398
        srcdataptr = array_seek(arraydataptr, 0, arraynullsptr, src_offset,
 
4399
                                                        typlen, typbyval, typalign);
 
4400
        mda_get_prod(ndim, dim, prod);
 
4401
        mda_get_range(ndim, span, st, endp);
 
4402
        mda_get_offset_values(ndim, dist, prod, span);
 
4403
        for (i = 0; i < ndim; i++)
 
4404
                indx[i] = 0;
 
4405
        dest_offset = 0;
 
4406
        j = ndim - 1;
 
4407
        do
 
4408
        {
 
4409
                if (dist[j])
 
4410
                {
 
4411
                        /* skip unwanted elements */
 
4412
                        srcdataptr = array_seek(srcdataptr, src_offset, arraynullsptr,
 
4413
                                                                        dist[j],
 
4414
                                                                        typlen, typbyval, typalign);
 
4415
                        src_offset += dist[j];
 
4416
                }
 
4417
                inc = array_copy(destdataptr, 1,
 
4418
                                                 srcdataptr, src_offset, arraynullsptr,
 
4419
                                                 typlen, typbyval, typalign);
 
4420
                if (destnullsptr)
 
4421
                        array_bitmap_copy(destnullsptr, dest_offset,
 
4422
                                                          arraynullsptr, src_offset,
 
4423
                                                          1);
 
4424
                destdataptr += inc;
 
4425
                srcdataptr += inc;
 
4426
                src_offset++;
 
4427
                dest_offset++;
 
4428
        } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
 
4429
}
 
4430
 
 
4431
/*
 
4432
 * Insert a slice into an array.
 
4433
 *
 
4434
 * ndim/dim[]/lb[] are dimensions of the original array.  A new array with
 
4435
 * those same dimensions is to be constructed.  destArray must already
 
4436
 * have been allocated and its header initialized.
 
4437
 *
 
4438
 * st[]/endp[] identify the slice to be replaced.  Elements within the slice
 
4439
 * volume are taken from consecutive elements of the srcArray; elements
 
4440
 * outside it are copied from origArray.
 
4441
 *
 
4442
 * We assume the caller has verified that the slice coordinates are valid.
 
4443
 */
 
4444
static void
 
4445
array_insert_slice(ArrayType *destArray,
 
4446
                                   ArrayType *origArray,
 
4447
                                   ArrayType *srcArray,
 
4448
                                   int ndim,
 
4449
                                   int *dim,
 
4450
                                   int *lb,
 
4451
                                   int *st,
 
4452
                                   int *endp,
 
4453
                                   int typlen,
 
4454
                                   bool typbyval,
 
4455
                                   char typalign)
 
4456
{
 
4457
        char       *destPtr = ARR_DATA_PTR(destArray);
 
4458
        char       *origPtr = ARR_DATA_PTR(origArray);
 
4459
        char       *srcPtr = ARR_DATA_PTR(srcArray);
 
4460
        bits8      *destBitmap = ARR_NULLBITMAP(destArray);
 
4461
        bits8      *origBitmap = ARR_NULLBITMAP(origArray);
 
4462
        bits8      *srcBitmap = ARR_NULLBITMAP(srcArray);
 
4463
        int                     orignitems = ArrayGetNItems(ARR_NDIM(origArray),
 
4464
                                                                                        ARR_DIMS(origArray));
 
4465
        int                     dest_offset,
 
4466
                                orig_offset,
 
4467
                                src_offset,
 
4468
                                prod[MAXDIM],
 
4469
                                span[MAXDIM],
 
4470
                                dist[MAXDIM],
 
4471
                                indx[MAXDIM];
 
4472
        int                     i,
 
4473
                                j,
 
4474
                                inc;
 
4475
 
 
4476
        dest_offset = ArrayGetOffset(ndim, dim, lb, st);
 
4477
        /* copy items before the slice start */
 
4478
        inc = array_copy(destPtr, dest_offset,
 
4479
                                         origPtr, 0, origBitmap,
 
4480
                                         typlen, typbyval, typalign);
 
4481
        destPtr += inc;
 
4482
        origPtr += inc;
 
4483
        if (destBitmap)
 
4484
                array_bitmap_copy(destBitmap, 0, origBitmap, 0, dest_offset);
 
4485
        orig_offset = dest_offset;
 
4486
        mda_get_prod(ndim, dim, prod);
 
4487
        mda_get_range(ndim, span, st, endp);
 
4488
        mda_get_offset_values(ndim, dist, prod, span);
 
4489
        for (i = 0; i < ndim; i++)
 
4490
                indx[i] = 0;
 
4491
        src_offset = 0;
 
4492
        j = ndim - 1;
 
4493
        do
 
4494
        {
 
4495
                /* Copy/advance over elements between here and next part of slice */
 
4496
                if (dist[j])
 
4497
                {
 
4498
                        inc = array_copy(destPtr, dist[j],
 
4499
                                                         origPtr, orig_offset, origBitmap,
 
4500
                                                         typlen, typbyval, typalign);
 
4501
                        destPtr += inc;
 
4502
                        origPtr += inc;
 
4503
                        if (destBitmap)
 
4504
                                array_bitmap_copy(destBitmap, dest_offset,
 
4505
                                                                  origBitmap, orig_offset,
 
4506
                                                                  dist[j]);
 
4507
                        dest_offset += dist[j];
 
4508
                        orig_offset += dist[j];
 
4509
                }
 
4510
                /* Copy new element at this slice position */
 
4511
                inc = array_copy(destPtr, 1,
 
4512
                                                 srcPtr, src_offset, srcBitmap,
 
4513
                                                 typlen, typbyval, typalign);
 
4514
                if (destBitmap)
 
4515
                        array_bitmap_copy(destBitmap, dest_offset,
 
4516
                                                          srcBitmap, src_offset,
 
4517
                                                          1);
 
4518
                destPtr += inc;
 
4519
                srcPtr += inc;
 
4520
                dest_offset++;
 
4521
                src_offset++;
 
4522
                /* Advance over old element at this slice position */
 
4523
                origPtr = array_seek(origPtr, orig_offset, origBitmap, 1,
 
4524
                                                         typlen, typbyval, typalign);
 
4525
                orig_offset++;
 
4526
        } while ((j = mda_next_tuple(ndim, indx, span)) != -1);
 
4527
 
 
4528
        /* don't miss any data at the end */
 
4529
        array_copy(destPtr, orignitems - orig_offset,
 
4530
                           origPtr, orig_offset, origBitmap,
 
4531
                           typlen, typbyval, typalign);
 
4532
        if (destBitmap)
 
4533
                array_bitmap_copy(destBitmap, dest_offset,
 
4534
                                                  origBitmap, orig_offset,
 
4535
                                                  orignitems - orig_offset);
 
4536
}
 
4537
 
 
4538
/*
 
4539
 * accumArrayResult - accumulate one (more) Datum for an array result
 
4540
 *
 
4541
 *      astate is working state (NULL on first call)
 
4542
 *      rcontext is where to keep working state
 
4543
 */
 
4544
ArrayBuildState *
 
4545
accumArrayResult(ArrayBuildState *astate,
 
4546
                                 Datum dvalue, bool disnull,
 
4547
                                 Oid element_type,
 
4548
                                 MemoryContext rcontext)
 
4549
{
 
4550
        MemoryContext arr_context,
 
4551
                                oldcontext;
 
4552
 
 
4553
        if (astate == NULL)
 
4554
        {
 
4555
                /* First time through --- initialize */
 
4556
 
 
4557
                /* Make a temporary context to hold all the junk */
 
4558
                arr_context = AllocSetContextCreate(rcontext,
 
4559
                                                                                        "accumArrayResult",
 
4560
                                                                                        ALLOCSET_DEFAULT_MINSIZE,
 
4561
                                                                                        ALLOCSET_DEFAULT_INITSIZE,
 
4562
                                                                                        ALLOCSET_DEFAULT_MAXSIZE);
 
4563
                oldcontext = MemoryContextSwitchTo(arr_context);
 
4564
                astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState));
 
4565
                astate->mcontext = arr_context;
 
4566
                astate->alen = 64;              /* arbitrary starting array size */
 
4567
                astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum));
 
4568
                astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool));
 
4569
                astate->nelems = 0;
 
4570
                astate->element_type = element_type;
 
4571
                get_typlenbyvalalign(element_type,
 
4572
                                                         &astate->typlen,
 
4573
                                                         &astate->typbyval,
 
4574
                                                         &astate->typalign);
 
4575
        }
 
4576
        else
 
4577
        {
 
4578
                oldcontext = MemoryContextSwitchTo(astate->mcontext);
 
4579
                Assert(astate->element_type == element_type);
 
4580
                /* enlarge dvalues[]/dnulls[] if needed */
 
4581
                if (astate->nelems >= astate->alen)
 
4582
                {
 
4583
                        astate->alen *= 2;
 
4584
                        astate->dvalues = (Datum *)
 
4585
                                repalloc(astate->dvalues, astate->alen * sizeof(Datum));
 
4586
                        astate->dnulls = (bool *)
 
4587
                                repalloc(astate->dnulls, astate->alen * sizeof(bool));
 
4588
                }
 
4589
        }
 
4590
 
 
4591
        /*
 
4592
         * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if
 
4593
         * it's varlena.  (You might think that detoasting is not needed here
 
4594
         * because construct_md_array can detoast the array elements later.
 
4595
         * However, we must not let construct_md_array modify the ArrayBuildState
 
4596
         * because that would mean array_agg_finalfn damages its input, which is
 
4597
         * verboten.  Also, this way frequently saves one copying step.)
 
4598
         */
 
4599
        if (!disnull && !astate->typbyval)
 
4600
        {
 
4601
                if (astate->typlen == -1)
 
4602
                        dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
 
4603
                else
 
4604
                        dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
 
4605
        }
 
4606
 
 
4607
        astate->dvalues[astate->nelems] = dvalue;
 
4608
        astate->dnulls[astate->nelems] = disnull;
 
4609
        astate->nelems++;
 
4610
 
 
4611
        MemoryContextSwitchTo(oldcontext);
 
4612
 
 
4613
        return astate;
 
4614
}
 
4615
 
 
4616
/*
 
4617
 * makeArrayResult - produce 1-D final result of accumArrayResult
 
4618
 *
 
4619
 *      astate is working state (not NULL)
 
4620
 *      rcontext is where to construct result
 
4621
 */
 
4622
Datum
 
4623
makeArrayResult(ArrayBuildState *astate,
 
4624
                                MemoryContext rcontext)
 
4625
{
 
4626
        int                     dims[1];
 
4627
        int                     lbs[1];
 
4628
 
 
4629
        dims[0] = astate->nelems;
 
4630
        lbs[0] = 1;
 
4631
 
 
4632
        return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true);
 
4633
}
 
4634
 
 
4635
/*
 
4636
 * makeMdArrayResult - produce multi-D final result of accumArrayResult
 
4637
 *
 
4638
 * beware: no check that specified dimensions match the number of values
 
4639
 * accumulated.
 
4640
 *
 
4641
 *      astate is working state (not NULL)
 
4642
 *      rcontext is where to construct result
 
4643
 *      release is true if okay to release working state
 
4644
 */
 
4645
Datum
 
4646
makeMdArrayResult(ArrayBuildState *astate,
 
4647
                                  int ndims,
 
4648
                                  int *dims,
 
4649
                                  int *lbs,
 
4650
                                  MemoryContext rcontext,
 
4651
                                  bool release)
 
4652
{
 
4653
        ArrayType  *result;
 
4654
        MemoryContext oldcontext;
 
4655
 
 
4656
        /* Build the final array result in rcontext */
 
4657
        oldcontext = MemoryContextSwitchTo(rcontext);
 
4658
 
 
4659
        result = construct_md_array(astate->dvalues,
 
4660
                                                                astate->dnulls,
 
4661
                                                                ndims,
 
4662
                                                                dims,
 
4663
                                                                lbs,
 
4664
                                                                astate->element_type,
 
4665
                                                                astate->typlen,
 
4666
                                                                astate->typbyval,
 
4667
                                                                astate->typalign);
 
4668
 
 
4669
        MemoryContextSwitchTo(oldcontext);
 
4670
 
 
4671
        /* Clean up all the junk */
 
4672
        if (release)
 
4673
                MemoryContextDelete(astate->mcontext);
 
4674
 
 
4675
        return PointerGetDatum(result);
 
4676
}
 
4677
 
 
4678
Datum
 
4679
array_larger(PG_FUNCTION_ARGS)
 
4680
{
 
4681
        ArrayType  *v1,
 
4682
                           *v2,
 
4683
                           *result;
 
4684
 
 
4685
        v1 = PG_GETARG_ARRAYTYPE_P(0);
 
4686
        v2 = PG_GETARG_ARRAYTYPE_P(1);
 
4687
 
 
4688
        result = ((array_cmp(fcinfo) > 0) ? v1 : v2);
 
4689
 
 
4690
        PG_RETURN_ARRAYTYPE_P(result);
 
4691
}
 
4692
 
 
4693
Datum
 
4694
array_smaller(PG_FUNCTION_ARGS)
 
4695
{
 
4696
        ArrayType  *v1,
 
4697
                           *v2,
 
4698
                           *result;
 
4699
 
 
4700
        v1 = PG_GETARG_ARRAYTYPE_P(0);
 
4701
        v2 = PG_GETARG_ARRAYTYPE_P(1);
 
4702
 
 
4703
        result = ((array_cmp(fcinfo) < 0) ? v1 : v2);
 
4704
 
 
4705
        PG_RETURN_ARRAYTYPE_P(result);
 
4706
}
 
4707
 
 
4708
 
 
4709
typedef struct generate_subscripts_fctx
 
4710
{
 
4711
        int4            lower;
 
4712
        int4            upper;
 
4713
        bool            reverse;
 
4714
} generate_subscripts_fctx;
 
4715
 
 
4716
/*
 
4717
 * generate_subscripts(array anyarray, dim int [, reverse bool])
 
4718
 *              Returns all subscripts of the array for any dimension
 
4719
 */
 
4720
Datum
 
4721
generate_subscripts(PG_FUNCTION_ARGS)
 
4722
{
 
4723
        FuncCallContext *funcctx;
 
4724
        MemoryContext oldcontext;
 
4725
        generate_subscripts_fctx *fctx;
 
4726
 
 
4727
        /* stuff done only on the first call of the function */
 
4728
        if (SRF_IS_FIRSTCALL())
 
4729
        {
 
4730
                ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
 
4731
                int                     reqdim = PG_GETARG_INT32(1);
 
4732
                int                *lb,
 
4733
                                   *dimv;
 
4734
 
 
4735
                /* create a function context for cross-call persistence */
 
4736
                funcctx = SRF_FIRSTCALL_INIT();
 
4737
 
 
4738
                /* Sanity check: does it look like an array at all? */
 
4739
                if (ARR_NDIM(v) <= 0 || ARR_NDIM(v) > MAXDIM)
 
4740
                        SRF_RETURN_DONE(funcctx);
 
4741
 
 
4742
                /* Sanity check: was the requested dim valid */
 
4743
                if (reqdim <= 0 || reqdim > ARR_NDIM(v))
 
4744
                        SRF_RETURN_DONE(funcctx);
 
4745
 
 
4746
                /*
 
4747
                 * switch to memory context appropriate for multiple function calls
 
4748
                 */
 
4749
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
4750
                fctx = (generate_subscripts_fctx *) palloc(sizeof(generate_subscripts_fctx));
 
4751
 
 
4752
                lb = ARR_LBOUND(v);
 
4753
                dimv = ARR_DIMS(v);
 
4754
 
 
4755
                fctx->lower = lb[reqdim - 1];
 
4756
                fctx->upper = dimv[reqdim - 1] + lb[reqdim - 1] - 1;
 
4757
                fctx->reverse = (PG_NARGS() < 3) ? false : PG_GETARG_BOOL(2);
 
4758
 
 
4759
                funcctx->user_fctx = fctx;
 
4760
 
 
4761
                MemoryContextSwitchTo(oldcontext);
 
4762
        }
 
4763
 
 
4764
        funcctx = SRF_PERCALL_SETUP();
 
4765
 
 
4766
        fctx = funcctx->user_fctx;
 
4767
 
 
4768
        if (fctx->lower <= fctx->upper)
 
4769
        {
 
4770
                if (!fctx->reverse)
 
4771
                        SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->lower++));
 
4772
                else
 
4773
                        SRF_RETURN_NEXT(funcctx, Int32GetDatum(fctx->upper--));
 
4774
        }
 
4775
        else
 
4776
                /* done when there are no more elements left */
 
4777
                SRF_RETURN_DONE(funcctx);
 
4778
}
 
4779
 
 
4780
/*
 
4781
 * generate_subscripts_nodir
 
4782
 *              Implements the 2-argument version of generate_subscripts
 
4783
 */
 
4784
Datum
 
4785
generate_subscripts_nodir(PG_FUNCTION_ARGS)
 
4786
{
 
4787
        /* just call the other one -- it can handle both cases */
 
4788
        return generate_subscripts(fcinfo);
 
4789
}
 
4790
 
 
4791
/*
 
4792
 * array_fill_with_lower_bounds
 
4793
 *              Create and fill array with defined lower bounds.
 
4794
 */
 
4795
Datum
 
4796
array_fill_with_lower_bounds(PG_FUNCTION_ARGS)
 
4797
{
 
4798
        ArrayType  *dims;
 
4799
        ArrayType  *lbs;
 
4800
        ArrayType  *result;
 
4801
        Oid                     elmtype;
 
4802
        Datum           value;
 
4803
        bool            isnull;
 
4804
 
 
4805
        if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
 
4806
                ereport(ERROR,
 
4807
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 
4808
                           errmsg("dimension array or low bound array cannot be NULL")));
 
4809
 
 
4810
        dims = PG_GETARG_ARRAYTYPE_P(1);
 
4811
        lbs = PG_GETARG_ARRAYTYPE_P(2);
 
4812
 
 
4813
        if (!PG_ARGISNULL(0))
 
4814
        {
 
4815
                value = PG_GETARG_DATUM(0);
 
4816
                isnull = false;
 
4817
        }
 
4818
        else
 
4819
        {
 
4820
                value = 0;
 
4821
                isnull = true;
 
4822
        }
 
4823
 
 
4824
        elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
4825
        if (!OidIsValid(elmtype))
 
4826
                elog(ERROR, "could not determine data type of input");
 
4827
 
 
4828
        result = array_fill_internal(dims, lbs, value, isnull, elmtype, fcinfo);
 
4829
        PG_RETURN_ARRAYTYPE_P(result);
 
4830
}
 
4831
 
 
4832
/*
 
4833
 * array_fill
 
4834
 *              Create and fill array with default lower bounds.
 
4835
 */
 
4836
Datum
 
4837
array_fill(PG_FUNCTION_ARGS)
 
4838
{
 
4839
        ArrayType  *dims;
 
4840
        ArrayType  *result;
 
4841
        Oid                     elmtype;
 
4842
        Datum           value;
 
4843
        bool            isnull;
 
4844
 
 
4845
        if (PG_ARGISNULL(1))
 
4846
                ereport(ERROR,
 
4847
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 
4848
                           errmsg("dimension array or low bound array cannot be NULL")));
 
4849
 
 
4850
        dims = PG_GETARG_ARRAYTYPE_P(1);
 
4851
 
 
4852
        if (!PG_ARGISNULL(0))
 
4853
        {
 
4854
                value = PG_GETARG_DATUM(0);
 
4855
                isnull = false;
 
4856
        }
 
4857
        else
 
4858
        {
 
4859
                value = 0;
 
4860
                isnull = true;
 
4861
        }
 
4862
 
 
4863
        elmtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
4864
        if (!OidIsValid(elmtype))
 
4865
                elog(ERROR, "could not determine data type of input");
 
4866
 
 
4867
        result = array_fill_internal(dims, NULL, value, isnull, elmtype, fcinfo);
 
4868
        PG_RETURN_ARRAYTYPE_P(result);
 
4869
}
 
4870
 
 
4871
static ArrayType *
 
4872
create_array_envelope(int ndims, int *dimv, int *lbsv, int nbytes,
 
4873
                                          Oid elmtype, int dataoffset)
 
4874
{
 
4875
        ArrayType  *result;
 
4876
 
 
4877
        result = (ArrayType *) palloc0(nbytes);
 
4878
        SET_VARSIZE(result, nbytes);
 
4879
        result->ndim = ndims;
 
4880
        result->dataoffset = dataoffset;
 
4881
        result->elemtype = elmtype;
 
4882
        memcpy(ARR_DIMS(result), dimv, ndims * sizeof(int));
 
4883
        memcpy(ARR_LBOUND(result), lbsv, ndims * sizeof(int));
 
4884
 
 
4885
        return result;
 
4886
}
 
4887
 
 
4888
static ArrayType *
 
4889
array_fill_internal(ArrayType *dims, ArrayType *lbs,
 
4890
                                        Datum value, bool isnull, Oid elmtype,
 
4891
                                        FunctionCallInfo fcinfo)
 
4892
{
 
4893
        ArrayType  *result;
 
4894
        int                *dimv;
 
4895
        int                *lbsv;
 
4896
        int                     ndims;
 
4897
        int                     nitems;
 
4898
        int                     deflbs[MAXDIM];
 
4899
        int16           elmlen;
 
4900
        bool            elmbyval;
 
4901
        char            elmalign;
 
4902
        ArrayMetaState *my_extra;
 
4903
 
 
4904
        /*
 
4905
         * Params checks
 
4906
         */
 
4907
        if (ARR_NDIM(dims) != 1)
 
4908
                ereport(ERROR,
 
4909
                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
4910
                                 errmsg("wrong number of array subscripts"),
 
4911
                                 errdetail("Dimension array must be one dimensional.")));
 
4912
 
 
4913
        if (ARR_LBOUND(dims)[0] != 1)
 
4914
                ereport(ERROR,
 
4915
                                (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
4916
                                 errmsg("wrong range of array subscripts"),
 
4917
                                 errdetail("Lower bound of dimension array must be one.")));
 
4918
 
 
4919
        if (array_contains_nulls(dims))
 
4920
                ereport(ERROR,
 
4921
                                (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 
4922
                                 errmsg("dimension values cannot be null")));
 
4923
 
 
4924
        dimv = (int *) ARR_DATA_PTR(dims);
 
4925
        ndims = ARR_DIMS(dims)[0];
 
4926
 
 
4927
        if (ndims < 0)                          /* we do allow zero-dimension arrays */
 
4928
                ereport(ERROR,
 
4929
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 
4930
                                 errmsg("invalid number of dimensions: %d", ndims)));
 
4931
        if (ndims > MAXDIM)
 
4932
                ereport(ERROR,
 
4933
                                (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
4934
                                 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
 
4935
                                                ndims, MAXDIM)));
 
4936
 
 
4937
        if (lbs != NULL)
 
4938
        {
 
4939
                if (ARR_NDIM(lbs) != 1)
 
4940
                        ereport(ERROR,
 
4941
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
4942
                                         errmsg("wrong number of array subscripts"),
 
4943
                                         errdetail("Dimension array must be one dimensional.")));
 
4944
 
 
4945
                if (ARR_LBOUND(lbs)[0] != 1)
 
4946
                        ereport(ERROR,
 
4947
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
4948
                                         errmsg("wrong range of array subscripts"),
 
4949
                                  errdetail("Lower bound of dimension array must be one.")));
 
4950
 
 
4951
                if (array_contains_nulls(lbs))
 
4952
                        ereport(ERROR,
 
4953
                                        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
 
4954
                                         errmsg("dimension values cannot be null")));
 
4955
 
 
4956
                if (ARR_DIMS(lbs)[0] != ndims)
 
4957
                        ereport(ERROR,
 
4958
                                        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
 
4959
                                         errmsg("wrong number of array subscripts"),
 
4960
                                         errdetail("Low bound array has different size than dimensions array.")));
 
4961
 
 
4962
                lbsv = (int *) ARR_DATA_PTR(lbs);
 
4963
        }
 
4964
        else
 
4965
        {
 
4966
                int                     i;
 
4967
 
 
4968
                for (i = 0; i < MAXDIM; i++)
 
4969
                        deflbs[i] = 1;
 
4970
 
 
4971
                lbsv = deflbs;
 
4972
        }
 
4973
 
 
4974
        /* fast track for empty array */
 
4975
        if (ndims == 0)
 
4976
                return construct_empty_array(elmtype);
 
4977
 
 
4978
        nitems = ArrayGetNItems(ndims, dimv);
 
4979
 
 
4980
        /*
 
4981
         * We arrange to look up info about element type only once per series of
 
4982
         * calls, assuming the element type doesn't change underneath us.
 
4983
         */
 
4984
        my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
4985
        if (my_extra == NULL)
 
4986
        {
 
4987
                fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
 
4988
                                                                                                          sizeof(ArrayMetaState));
 
4989
                my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
 
4990
                my_extra->element_type = InvalidOid;
 
4991
        }
 
4992
 
 
4993
        if (my_extra->element_type != elmtype)
 
4994
        {
 
4995
                /* Get info about element type */
 
4996
                get_typlenbyvalalign(elmtype,
 
4997
                                                         &my_extra->typlen,
 
4998
                                                         &my_extra->typbyval,
 
4999
                                                         &my_extra->typalign);
 
5000
                my_extra->element_type = elmtype;
 
5001
        }
 
5002
 
 
5003
        elmlen = my_extra->typlen;
 
5004
        elmbyval = my_extra->typbyval;
 
5005
        elmalign = my_extra->typalign;
 
5006
 
 
5007
        /* compute required space */
 
5008
        if (!isnull)
 
5009
        {
 
5010
                int                     i;
 
5011
                char       *p;
 
5012
                int                     nbytes;
 
5013
                int                     totbytes;
 
5014
 
 
5015
                /* make sure data is not toasted */
 
5016
                if (elmlen == -1)
 
5017
                        value = PointerGetDatum(PG_DETOAST_DATUM(value));
 
5018
 
 
5019
                nbytes = att_addlength_datum(0, elmlen, value);
 
5020
                nbytes = att_align_nominal(nbytes, elmalign);
 
5021
                Assert(nbytes > 0);
 
5022
 
 
5023
                totbytes = nbytes * nitems;
 
5024
 
 
5025
                /* check for overflow of multiplication or total request */
 
5026
                if (totbytes / nbytes != nitems ||
 
5027
                        !AllocSizeIsValid(totbytes))
 
5028
                        ereport(ERROR,
 
5029
                                        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
 
5030
                                         errmsg("array size exceeds the maximum allowed (%d)",
 
5031
                                                        (int) MaxAllocSize)));
 
5032
 
 
5033
                /*
 
5034
                 * This addition can't overflow, but it might cause us to go past
 
5035
                 * MaxAllocSize.  We leave it to palloc to complain in that case.
 
5036
                 */
 
5037
                totbytes += ARR_OVERHEAD_NONULLS(ndims);
 
5038
 
 
5039
                result = create_array_envelope(ndims, dimv, lbsv, totbytes,
 
5040
                                                                           elmtype, 0);
 
5041
 
 
5042
                p = ARR_DATA_PTR(result);
 
5043
                for (i = 0; i < nitems; i++)
 
5044
                        p += ArrayCastAndSet(value, elmlen, elmbyval, elmalign, p);
 
5045
        }
 
5046
        else
 
5047
        {
 
5048
                int                     nbytes;
 
5049
                int                     dataoffset;
 
5050
 
 
5051
                dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
 
5052
                nbytes = dataoffset;
 
5053
 
 
5054
                result = create_array_envelope(ndims, dimv, lbsv, nbytes,
 
5055
                                                                           elmtype, dataoffset);
 
5056
 
 
5057
                /* create_array_envelope already zeroed the bitmap, so we're done */
 
5058
        }
 
5059
 
 
5060
        return result;
 
5061
}
 
5062
 
 
5063
 
 
5064
/*
 
5065
 * UNNEST
 
5066
 */
 
5067
Datum
 
5068
array_unnest(PG_FUNCTION_ARGS)
 
5069
{
 
5070
        typedef struct
 
5071
        {
 
5072
                ArrayType  *arr;
 
5073
                int                     nextelem;
 
5074
                int                     numelems;
 
5075
                char       *elemdataptr;        /* this moves with nextelem */
 
5076
                bits8      *arraynullsptr;              /* this does not */
 
5077
                int16           elmlen;
 
5078
                bool            elmbyval;
 
5079
                char            elmalign;
 
5080
        } array_unnest_fctx;
 
5081
 
 
5082
        FuncCallContext *funcctx;
 
5083
        array_unnest_fctx *fctx;
 
5084
        MemoryContext oldcontext;
 
5085
 
 
5086
        /* stuff done only on the first call of the function */
 
5087
        if (SRF_IS_FIRSTCALL())
 
5088
        {
 
5089
                ArrayType  *arr;
 
5090
 
 
5091
                /* create a function context for cross-call persistence */
 
5092
                funcctx = SRF_FIRSTCALL_INIT();
 
5093
 
 
5094
                /*
 
5095
                 * switch to memory context appropriate for multiple function calls
 
5096
                 */
 
5097
                oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
5098
 
 
5099
                /*
 
5100
                 * Get the array value and detoast if needed.  We can't do this
 
5101
                 * earlier because if we have to detoast, we want the detoasted copy
 
5102
                 * to be in multi_call_memory_ctx, so it will go away when we're done
 
5103
                 * and not before.      (If no detoast happens, we assume the originally
 
5104
                 * passed array will stick around till then.)
 
5105
                 */
 
5106
                arr = PG_GETARG_ARRAYTYPE_P(0);
 
5107
 
 
5108
                /* allocate memory for user context */
 
5109
                fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx));
 
5110
 
 
5111
                /* initialize state */
 
5112
                fctx->arr = arr;
 
5113
                fctx->nextelem = 0;
 
5114
                fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
 
5115
 
 
5116
                fctx->elemdataptr = ARR_DATA_PTR(arr);
 
5117
                fctx->arraynullsptr = ARR_NULLBITMAP(arr);
 
5118
 
 
5119
                get_typlenbyvalalign(ARR_ELEMTYPE(arr),
 
5120
                                                         &fctx->elmlen,
 
5121
                                                         &fctx->elmbyval,
 
5122
                                                         &fctx->elmalign);
 
5123
 
 
5124
                funcctx->user_fctx = fctx;
 
5125
                MemoryContextSwitchTo(oldcontext);
 
5126
        }
 
5127
 
 
5128
        /* stuff done on every call of the function */
 
5129
        funcctx = SRF_PERCALL_SETUP();
 
5130
        fctx = funcctx->user_fctx;
 
5131
 
 
5132
        if (fctx->nextelem < fctx->numelems)
 
5133
        {
 
5134
                int                     offset = fctx->nextelem++;
 
5135
                Datum           elem;
 
5136
 
 
5137
                /*
 
5138
                 * Check for NULL array element
 
5139
                 */
 
5140
                if (array_get_isnull(fctx->arraynullsptr, offset))
 
5141
                {
 
5142
                        fcinfo->isnull = true;
 
5143
                        elem = (Datum) 0;
 
5144
                        /* elemdataptr does not move */
 
5145
                }
 
5146
                else
 
5147
                {
 
5148
                        /*
 
5149
                         * OK, get the element
 
5150
                         */
 
5151
                        char       *ptr = fctx->elemdataptr;
 
5152
 
 
5153
                        fcinfo->isnull = false;
 
5154
                        elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen);
 
5155
 
 
5156
                        /*
 
5157
                         * Advance elemdataptr over it
 
5158
                         */
 
5159
                        ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr);
 
5160
                        ptr = (char *) att_align_nominal(ptr, fctx->elmalign);
 
5161
                        fctx->elemdataptr = ptr;
 
5162
                }
 
5163
 
 
5164
                SRF_RETURN_NEXT(funcctx, elem);
 
5165
        }
 
5166
        else
 
5167
        {
 
5168
                /* do when there is no more left */
 
5169
                SRF_RETURN_DONE(funcctx);
 
5170
        }
 
5171
}