Coverage Report

Created: 2026-10-16 06:48

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/array_userfuncs.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * array_userfuncs.c
4
 *    Misc user-visible array support functions
5
 *
6
 * Copyright (c) 2003-2026, PostgreSQL Global Development Group
7
 *
8
 * IDENTIFICATION
9
 *    src/backend/utils/adt/array_userfuncs.c
10
 *
11
 *-------------------------------------------------------------------------
12
 */
13
#include "postgres.h"
14
15
#include "catalog/pg_operator_d.h"
16
#include "catalog/pg_type.h"
17
#include "common/int.h"
18
#include "common/pg_prng.h"
19
#include "libpq/pqformat.h"
20
#include "miscadmin.h"
21
#include "nodes/supportnodes.h"
22
#include "port/pg_bitutils.h"
23
#include "utils/array.h"
24
#include "utils/builtins.h"
25
#include "utils/datum.h"
26
#include "utils/lsyscache.h"
27
#include "utils/memutils.h"
28
#include "utils/tuplesort.h"
29
#include "utils/typcache.h"
30
31
/*
32
 * SerialIOData
33
 *    Used for caching element-type data in array_agg_serialize
34
 */
35
typedef struct SerialIOData
36
{
37
  FmgrInfo  typsend;
38
} SerialIOData;
39
40
/*
41
 * DeserialIOData
42
 *    Used for caching element-type data in array_agg_deserialize
43
 */
44
typedef struct DeserialIOData
45
{
46
  FmgrInfo  typreceive;
47
  Oid     typioparam;
48
} DeserialIOData;
49
50
/*
51
 * ArraySortCachedInfo
52
 *    Used for caching catalog data in array_sort
53
 */
54
typedef struct ArraySortCachedInfo
55
{
56
  ArrayMetaState array_meta;  /* metadata for array_create_iterator */
57
  Oid     elem_lt_opr;  /* "<" operator for element type */
58
  Oid     elem_gt_opr;  /* ">" operator for element type */
59
  Oid     array_type;   /* pg_type OID of array type */
60
} ArraySortCachedInfo;
61
62
static Datum array_position_common(FunctionCallInfo fcinfo);
63
64
65
/*
66
 * fetch_array_arg_replace_nulls
67
 *
68
 * Fetch an array-valued argument in expanded form; if it's null, construct an
69
 * empty array value of the proper data type.  Also cache basic element type
70
 * information in fn_extra.
71
 *
72
 * Caution: if the input is a read/write pointer, this returns the input
73
 * argument; so callers must be sure that their changes are "safe", that is
74
 * they cannot leave the array in a corrupt state.
75
 *
76
 * If we're being called as an aggregate function, make sure any newly-made
77
 * expanded array is allocated in the aggregate state context, so as to save
78
 * copying operations.
79
 */
80
static ExpandedArrayHeader *
81
fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
82
0
{
83
0
  ExpandedArrayHeader *eah;
84
0
  Oid     element_type;
85
0
  ArrayMetaState *my_extra;
86
0
  MemoryContext resultcxt;
87
88
  /* If first time through, create datatype cache struct */
89
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
90
0
  if (my_extra == NULL)
91
0
  {
92
0
    my_extra = (ArrayMetaState *)
93
0
      MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
94
0
                 sizeof(ArrayMetaState));
95
0
    my_extra->element_type = InvalidOid;
96
0
    fcinfo->flinfo->fn_extra = my_extra;
97
0
  }
98
99
  /* Figure out which context we want the result in */
100
0
  if (!AggCheckCallContext(fcinfo, &resultcxt))
101
0
    resultcxt = CurrentMemoryContext;
102
103
  /* Now collect the array value */
104
0
  if (!PG_ARGISNULL(argno))
105
0
  {
106
0
    MemoryContext oldcxt = MemoryContextSwitchTo(resultcxt);
107
108
0
    eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
109
0
    MemoryContextSwitchTo(oldcxt);
110
0
  }
111
0
  else
112
0
  {
113
    /* We have to look up the array type and element type */
114
0
    Oid     arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
115
116
0
    if (!OidIsValid(arr_typeid))
117
0
      ereport(ERROR,
118
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
119
0
           errmsg("could not determine input data type")));
120
0
    element_type = get_element_type(arr_typeid);
121
0
    if (!OidIsValid(element_type))
122
0
      ereport(ERROR,
123
0
          (errcode(ERRCODE_DATATYPE_MISMATCH),
124
0
           errmsg("input data type is not an array")));
125
126
0
    eah = construct_empty_expanded_array(element_type,
127
0
                       resultcxt,
128
0
                       my_extra);
129
0
  }
130
131
0
  return eah;
132
0
}
133
134
/*-----------------------------------------------------------------------------
135
 * array_append :
136
 *    push an element onto the end of a one-dimensional array
137
 *----------------------------------------------------------------------------
138
 */
139
Datum
140
array_append(PG_FUNCTION_ARGS)
141
0
{
142
0
  ExpandedArrayHeader *eah;
143
0
  Datum   newelem;
144
0
  bool    isNull;
145
0
  Datum   result;
146
0
  int      *dimv,
147
0
         *lb;
148
0
  int     indx;
149
0
  ArrayMetaState *my_extra;
150
151
0
  eah = fetch_array_arg_replace_nulls(fcinfo, 0);
152
0
  isNull = PG_ARGISNULL(1);
153
0
  if (isNull)
154
0
    newelem = (Datum) 0;
155
0
  else
156
0
    newelem = PG_GETARG_DATUM(1);
157
158
0
  if (eah->ndims == 1)
159
0
  {
160
    /* append newelem */
161
0
    lb = eah->lbound;
162
0
    dimv = eah->dims;
163
164
    /* index of added elem is at lb[0] + (dimv[0] - 1) + 1 */
165
0
    if (pg_add_s32_overflow(lb[0], dimv[0], &indx))
166
0
      ereport(ERROR,
167
0
          (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
168
0
           errmsg("integer out of range")));
169
0
  }
170
0
  else if (eah->ndims == 0)
171
0
    indx = 1;
172
0
  else
173
0
    ereport(ERROR,
174
0
        (errcode(ERRCODE_DATA_EXCEPTION),
175
0
         errmsg("argument must be empty or one-dimensional array")));
176
177
  /* Perform element insertion */
178
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
179
180
0
  result = array_set_element(EOHPGetRWDatum(&eah->hdr),
181
0
                 1, &indx, newelem, isNull,
182
0
                 -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
183
184
0
  PG_RETURN_DATUM(result);
185
0
}
186
187
/*
188
 * array_append_support()
189
 *
190
 * Planner support function for array_append()
191
 */
192
Datum
193
array_append_support(PG_FUNCTION_ARGS)
194
0
{
195
0
  Node     *rawreq = (Node *) PG_GETARG_POINTER(0);
196
0
  Node     *ret = NULL;
197
198
0
  if (IsA(rawreq, SupportRequestModifyInPlace))
199
0
  {
200
    /*
201
     * We can optimize in-place appends if the function's array argument
202
     * is the array being assigned to.  We don't need to worry about array
203
     * references within the other argument.
204
     */
205
0
    SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
206
0
    Param    *arg = (Param *) linitial(req->args);
207
208
0
    if (arg && IsA(arg, Param) &&
209
0
      arg->paramkind == PARAM_EXTERN &&
210
0
      arg->paramid == req->paramid)
211
0
      ret = (Node *) arg;
212
0
  }
213
214
0
  PG_RETURN_POINTER(ret);
215
0
}
216
217
/*-----------------------------------------------------------------------------
218
 * array_prepend :
219
 *    push an element onto the front of a one-dimensional array
220
 *----------------------------------------------------------------------------
221
 */
222
Datum
223
array_prepend(PG_FUNCTION_ARGS)
224
0
{
225
0
  ExpandedArrayHeader *eah;
226
0
  Datum   newelem;
227
0
  bool    isNull;
228
0
  Datum   result;
229
0
  int      *lb;
230
0
  int     indx;
231
0
  int     lb0;
232
0
  ArrayMetaState *my_extra;
233
234
0
  isNull = PG_ARGISNULL(0);
235
0
  if (isNull)
236
0
    newelem = (Datum) 0;
237
0
  else
238
0
    newelem = PG_GETARG_DATUM(0);
239
0
  eah = fetch_array_arg_replace_nulls(fcinfo, 1);
240
241
0
  if (eah->ndims == 1)
242
0
  {
243
    /* prepend newelem */
244
0
    lb = eah->lbound;
245
0
    lb0 = lb[0];
246
247
0
    if (pg_sub_s32_overflow(lb0, 1, &indx))
248
0
      ereport(ERROR,
249
0
          (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
250
0
           errmsg("integer out of range")));
251
0
  }
252
0
  else if (eah->ndims == 0)
253
0
  {
254
0
    indx = 1;
255
0
    lb0 = 1;
256
0
  }
257
0
  else
258
0
    ereport(ERROR,
259
0
        (errcode(ERRCODE_DATA_EXCEPTION),
260
0
         errmsg("argument must be empty or one-dimensional array")));
261
262
  /* Perform element insertion */
263
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
264
265
0
  result = array_set_element(EOHPGetRWDatum(&eah->hdr),
266
0
                 1, &indx, newelem, isNull,
267
0
                 -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
268
269
  /* Readjust result's LB to match the input's, as expected for prepend */
270
0
  Assert(result == EOHPGetRWDatum(&eah->hdr));
271
0
  if (eah->ndims == 1)
272
0
  {
273
    /* This is ok whether we've deconstructed or not */
274
0
    eah->lbound[0] = lb0;
275
0
  }
276
277
0
  PG_RETURN_DATUM(result);
278
0
}
279
280
/*
281
 * array_prepend_support()
282
 *
283
 * Planner support function for array_prepend()
284
 */
285
Datum
286
array_prepend_support(PG_FUNCTION_ARGS)
287
0
{
288
0
  Node     *rawreq = (Node *) PG_GETARG_POINTER(0);
289
0
  Node     *ret = NULL;
290
291
0
  if (IsA(rawreq, SupportRequestModifyInPlace))
292
0
  {
293
    /*
294
     * We can optimize in-place prepends if the function's array argument
295
     * is the array being assigned to.  We don't need to worry about array
296
     * references within the other argument.
297
     */
298
0
    SupportRequestModifyInPlace *req = (SupportRequestModifyInPlace *) rawreq;
299
0
    Param    *arg = (Param *) lsecond(req->args);
300
301
0
    if (arg && IsA(arg, Param) &&
302
0
      arg->paramkind == PARAM_EXTERN &&
303
0
      arg->paramid == req->paramid)
304
0
      ret = (Node *) arg;
305
0
  }
306
307
0
  PG_RETURN_POINTER(ret);
308
0
}
309
310
/*-----------------------------------------------------------------------------
311
 * array_cat :
312
 *    concatenate two nD arrays to form an nD array, or
313
 *    push an (n-1)D array onto the end of an nD array
314
 *----------------------------------------------------------------------------
315
 */
316
Datum
317
array_cat(PG_FUNCTION_ARGS)
318
0
{
319
0
  ArrayType  *v1,
320
0
         *v2;
321
0
  ArrayType  *result;
322
0
  int      *dims,
323
0
         *lbs,
324
0
        ndims,
325
0
        nitems,
326
0
        ndatabytes,
327
0
        nbytes;
328
0
  int      *dims1,
329
0
         *lbs1,
330
0
        ndims1,
331
0
        nitems1,
332
0
        ndatabytes1;
333
0
  int      *dims2,
334
0
         *lbs2,
335
0
        ndims2,
336
0
        nitems2,
337
0
        ndatabytes2;
338
0
  int     i;
339
0
  char     *dat1,
340
0
         *dat2;
341
0
  uint8    *bitmap1,
342
0
         *bitmap2;
343
0
  Oid     element_type;
344
0
  Oid     element_type1;
345
0
  Oid     element_type2;
346
0
  int32   dataoffset;
347
348
  /* Concatenating a null array is a no-op, just return the other input */
349
0
  if (PG_ARGISNULL(0))
350
0
  {
351
0
    if (PG_ARGISNULL(1))
352
0
      PG_RETURN_NULL();
353
0
    result = PG_GETARG_ARRAYTYPE_P(1);
354
0
    PG_RETURN_ARRAYTYPE_P(result);
355
0
  }
356
0
  if (PG_ARGISNULL(1))
357
0
  {
358
0
    result = PG_GETARG_ARRAYTYPE_P(0);
359
0
    PG_RETURN_ARRAYTYPE_P(result);
360
0
  }
361
362
0
  v1 = PG_GETARG_ARRAYTYPE_P(0);
363
0
  v2 = PG_GETARG_ARRAYTYPE_P(1);
364
365
0
  element_type1 = ARR_ELEMTYPE(v1);
366
0
  element_type2 = ARR_ELEMTYPE(v2);
367
368
  /* Check we have matching element types */
369
0
  if (element_type1 != element_type2)
370
0
    ereport(ERROR,
371
0
        (errcode(ERRCODE_DATATYPE_MISMATCH),
372
0
         errmsg("cannot concatenate incompatible arrays"),
373
0
         errdetail("Arrays with element types %s and %s are not "
374
0
               "compatible for concatenation.",
375
0
               format_type_be(element_type1),
376
0
               format_type_be(element_type2))));
377
378
  /* OK, use it */
379
0
  element_type = element_type1;
380
381
  /*----------
382
   * We must have one of the following combinations of inputs:
383
   * 1) one empty array, and one non-empty array
384
   * 2) both arrays empty
385
   * 3) two arrays with ndims1 == ndims2
386
   * 4) ndims1 == ndims2 - 1
387
   * 5) ndims1 == ndims2 + 1
388
   *----------
389
   */
390
0
  ndims1 = ARR_NDIM(v1);
391
0
  ndims2 = ARR_NDIM(v2);
392
393
  /*
394
   * short circuit - if one input array is empty, and the other is not, we
395
   * return the non-empty one as the result
396
   *
397
   * if both are empty, return the first one
398
   */
399
0
  if (ndims1 == 0 && ndims2 > 0)
400
0
    PG_RETURN_ARRAYTYPE_P(v2);
401
402
0
  if (ndims2 == 0)
403
0
    PG_RETURN_ARRAYTYPE_P(v1);
404
405
  /* the rest fall under rule 3, 4, or 5 */
406
0
  if (ndims1 != ndims2 &&
407
0
    ndims1 != ndims2 - 1 &&
408
0
    ndims1 != ndims2 + 1)
409
0
    ereport(ERROR,
410
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
411
0
         errmsg("cannot concatenate incompatible arrays"),
412
0
         errdetail("Arrays of %d and %d dimensions are not "
413
0
               "compatible for concatenation.",
414
0
               ndims1, ndims2)));
415
416
  /* get argument array details */
417
0
  lbs1 = ARR_LBOUND(v1);
418
0
  lbs2 = ARR_LBOUND(v2);
419
0
  dims1 = ARR_DIMS(v1);
420
0
  dims2 = ARR_DIMS(v2);
421
0
  dat1 = ARR_DATA_PTR(v1);
422
0
  dat2 = ARR_DATA_PTR(v2);
423
0
  bitmap1 = ARR_NULLBITMAP(v1);
424
0
  bitmap2 = ARR_NULLBITMAP(v2);
425
0
  nitems1 = ArrayGetNItems(ndims1, dims1);
426
0
  nitems2 = ArrayGetNItems(ndims2, dims2);
427
0
  ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
428
0
  ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
429
430
0
  if (ndims1 == ndims2)
431
0
  {
432
    /*
433
     * resulting array is made up of the elements (possibly arrays
434
     * themselves) of the input argument arrays
435
     */
436
0
    ndims = ndims1;
437
0
    dims = palloc_array(int, ndims);
438
0
    lbs = palloc_array(int, ndims);
439
440
0
    dims[0] = dims1[0] + dims2[0];
441
0
    lbs[0] = lbs1[0];
442
443
0
    for (i = 1; i < ndims; i++)
444
0
    {
445
0
      if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
446
0
        ereport(ERROR,
447
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
448
0
             errmsg("cannot concatenate incompatible arrays"),
449
0
             errdetail("Arrays with differing element dimensions are "
450
0
                   "not compatible for concatenation.")));
451
452
0
      dims[i] = dims1[i];
453
0
      lbs[i] = lbs1[i];
454
0
    }
455
0
  }
456
0
  else if (ndims1 == ndims2 - 1)
457
0
  {
458
    /*
459
     * resulting array has the second argument as the outer array, with
460
     * the first argument inserted at the front of the outer dimension
461
     */
462
0
    ndims = ndims2;
463
0
    dims = palloc_array(int, ndims);
464
0
    lbs = palloc_array(int, ndims);
465
0
    memcpy(dims, dims2, ndims * sizeof(int));
466
0
    memcpy(lbs, lbs2, ndims * sizeof(int));
467
468
    /* increment number of elements in outer array */
469
0
    dims[0] += 1;
470
471
    /* make sure the added element matches our existing elements */
472
0
    for (i = 0; i < ndims1; i++)
473
0
    {
474
0
      if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
475
0
        ereport(ERROR,
476
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
477
0
             errmsg("cannot concatenate incompatible arrays"),
478
0
             errdetail("Arrays with differing dimensions are not "
479
0
                   "compatible for concatenation.")));
480
0
    }
481
0
  }
482
0
  else
483
0
  {
484
    /*
485
     * (ndims1 == ndims2 + 1)
486
     *
487
     * resulting array has the first argument as the outer array, with the
488
     * second argument appended to the end of the outer dimension
489
     */
490
0
    ndims = ndims1;
491
0
    dims = palloc_array(int, ndims);
492
0
    lbs = palloc_array(int, ndims);
493
0
    memcpy(dims, dims1, ndims * sizeof(int));
494
0
    memcpy(lbs, lbs1, ndims * sizeof(int));
495
496
    /* increment number of elements in outer array */
497
0
    dims[0] += 1;
498
499
    /* make sure the added element matches our existing elements */
500
0
    for (i = 0; i < ndims2; i++)
501
0
    {
502
0
      if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
503
0
        ereport(ERROR,
504
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
505
0
             errmsg("cannot concatenate incompatible arrays"),
506
0
             errdetail("Arrays with differing dimensions are not "
507
0
                   "compatible for concatenation.")));
508
0
    }
509
0
  }
510
511
  /* Do this mainly for overflow checking */
512
0
  nitems = ArrayGetNItems(ndims, dims);
513
0
  ArrayCheckBounds(ndims, dims, lbs);
514
515
  /* build the result array */
516
0
  ndatabytes = ndatabytes1 + ndatabytes2;
517
0
  if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
518
0
  {
519
0
    dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
520
0
    nbytes = ndatabytes + dataoffset;
521
0
  }
522
0
  else
523
0
  {
524
0
    dataoffset = 0;     /* marker for no null bitmap */
525
0
    nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
526
0
  }
527
0
  result = (ArrayType *) palloc0(nbytes);
528
0
  SET_VARSIZE(result, nbytes);
529
0
  result->ndim = ndims;
530
0
  result->dataoffset = dataoffset;
531
0
  result->elemtype = element_type;
532
0
  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
533
0
  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
534
  /* data area is arg1 then arg2 */
535
0
  memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
536
0
  memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
537
  /* handle the null bitmap if needed */
538
0
  if (ARR_HASNULL(result))
539
0
  {
540
0
    array_bitmap_copy(ARR_NULLBITMAP(result), 0,
541
0
              bitmap1, 0,
542
0
              nitems1);
543
0
    array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
544
0
              bitmap2, 0,
545
0
              nitems2);
546
0
  }
547
548
0
  PG_RETURN_ARRAYTYPE_P(result);
549
0
}
550
551
552
/*
553
 * ARRAY_AGG(anynonarray) aggregate function
554
 */
555
Datum
556
array_agg_transfn(PG_FUNCTION_ARGS)
557
0
{
558
0
  Oid     arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
559
0
  MemoryContext aggcontext;
560
0
  ArrayBuildState *state;
561
0
  Datum   elem;
562
563
0
  if (arg1_typeid == InvalidOid)
564
0
    ereport(ERROR,
565
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
566
0
         errmsg("could not determine input data type")));
567
568
  /*
569
   * Note: we do not need a run-time check about whether arg1_typeid is a
570
   * valid array element type, because the parser would have verified that
571
   * while resolving the input/result types of this polymorphic aggregate.
572
   */
573
574
0
  if (!AggCheckCallContext(fcinfo, &aggcontext))
575
0
  {
576
    /* cannot be called directly because of internal-type argument */
577
0
    elog(ERROR, "array_agg_transfn called in non-aggregate context");
578
0
  }
579
580
0
  if (PG_ARGISNULL(0))
581
0
    state = initArrayResult(arg1_typeid, aggcontext, false);
582
0
  else
583
0
    state = (ArrayBuildState *) PG_GETARG_POINTER(0);
584
585
0
  elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
586
587
0
  state = accumArrayResult(state,
588
0
               elem,
589
0
               PG_ARGISNULL(1),
590
0
               arg1_typeid,
591
0
               aggcontext);
592
593
  /*
594
   * The transition type for array_agg() is declared to be "internal", which
595
   * is a pass-by-value type the same size as a pointer.  So we can safely
596
   * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
597
   */
598
0
  PG_RETURN_POINTER(state);
599
0
}
600
601
Datum
602
array_agg_combine(PG_FUNCTION_ARGS)
603
0
{
604
0
  ArrayBuildState *state1;
605
0
  ArrayBuildState *state2;
606
0
  MemoryContext agg_context;
607
0
  MemoryContext old_context;
608
609
0
  if (!AggCheckCallContext(fcinfo, &agg_context))
610
0
    elog(ERROR, "aggregate function called in non-aggregate context");
611
612
0
  state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
613
0
  state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(1);
614
615
0
  if (state2 == NULL)
616
0
  {
617
    /*
618
     * NULL state2 is easy, just return state1, which we know is already
619
     * in the agg_context
620
     */
621
0
    if (state1 == NULL)
622
0
      PG_RETURN_NULL();
623
0
    PG_RETURN_POINTER(state1);
624
0
  }
625
626
0
  if (state1 == NULL)
627
0
  {
628
    /* We must copy state2's data into the agg_context */
629
0
    state1 = initArrayResultWithSize(state2->element_type, agg_context,
630
0
                     false, state2->alen);
631
632
0
    old_context = MemoryContextSwitchTo(agg_context);
633
634
0
    for (int i = 0; i < state2->nelems; i++)
635
0
    {
636
0
      if (!state2->dnulls[i])
637
0
        state1->dvalues[i] = datumCopy(state2->dvalues[i],
638
0
                         state1->typbyval,
639
0
                         state1->typlen);
640
0
      else
641
0
        state1->dvalues[i] = (Datum) 0;
642
0
    }
643
644
0
    MemoryContextSwitchTo(old_context);
645
646
0
    memcpy(state1->dnulls, state2->dnulls, sizeof(bool) * state2->nelems);
647
648
0
    state1->nelems = state2->nelems;
649
650
0
    PG_RETURN_POINTER(state1);
651
0
  }
652
0
  else if (state2->nelems > 0)
653
0
  {
654
    /* We only need to combine the two states if state2 has any elements */
655
0
    int     reqsize = state1->nelems + state2->nelems;
656
0
    MemoryContext oldContext = MemoryContextSwitchTo(state1->mcontext);
657
658
0
    Assert(state1->element_type == state2->element_type);
659
660
    /* Enlarge state1 arrays if needed */
661
0
    if (state1->alen < reqsize)
662
0
    {
663
      /* Use a power of 2 size rather than allocating just reqsize */
664
0
      state1->alen = pg_nextpower2_32(reqsize);
665
0
      state1->dvalues = repalloc_array(state1->dvalues, Datum, state1->alen);
666
0
      state1->dnulls = repalloc_array(state1->dnulls, bool, state1->alen);
667
0
    }
668
669
    /* Copy in the state2 elements to the end of the state1 arrays */
670
0
    for (int i = 0; i < state2->nelems; i++)
671
0
    {
672
0
      if (!state2->dnulls[i])
673
0
        state1->dvalues[i + state1->nelems] =
674
0
          datumCopy(state2->dvalues[i],
675
0
                state1->typbyval,
676
0
                state1->typlen);
677
0
      else
678
0
        state1->dvalues[i + state1->nelems] = (Datum) 0;
679
0
    }
680
681
0
    memcpy(&state1->dnulls[state1->nelems], state2->dnulls,
682
0
         sizeof(bool) * state2->nelems);
683
684
0
    state1->nelems = reqsize;
685
686
0
    MemoryContextSwitchTo(oldContext);
687
0
  }
688
689
0
  PG_RETURN_POINTER(state1);
690
0
}
691
692
/*
693
 * array_agg_serialize
694
 *    Serialize ArrayBuildState into bytea.
695
 */
696
Datum
697
array_agg_serialize(PG_FUNCTION_ARGS)
698
0
{
699
0
  ArrayBuildState *state;
700
0
  StringInfoData buf;
701
0
  bytea    *result;
702
703
  /* cannot be called directly because of internal-type argument */
704
0
  Assert(AggCheckCallContext(fcinfo, NULL));
705
706
0
  state = (ArrayBuildState *) PG_GETARG_POINTER(0);
707
708
0
  pq_begintypsend(&buf);
709
710
  /*
711
   * element_type. Putting this first is more convenient in deserialization
712
   */
713
0
  pq_sendint32(&buf, state->element_type);
714
715
  /*
716
   * nelems -- send first so we know how large to make the dvalues and
717
   * dnulls array during deserialization.
718
   */
719
0
  pq_sendint64(&buf, state->nelems);
720
721
  /* alen can be decided during deserialization */
722
723
  /* typlen */
724
0
  pq_sendint16(&buf, state->typlen);
725
726
  /* typbyval */
727
0
  pq_sendbyte(&buf, state->typbyval);
728
729
  /* typalign */
730
0
  pq_sendbyte(&buf, state->typalign);
731
732
  /* dnulls */
733
0
  pq_sendbytes(&buf, state->dnulls, sizeof(bool) * state->nelems);
734
735
  /*
736
   * dvalues.  By agreement with array_agg_deserialize, when the element
737
   * type is byval, we just transmit the Datum array as-is, including any
738
   * null elements.  For by-ref types, we must invoke the element type's
739
   * send function, and we skip null elements (which is why the nulls flags
740
   * must be sent first).
741
   */
742
0
  if (state->typbyval)
743
0
    pq_sendbytes(&buf, state->dvalues, sizeof(Datum) * state->nelems);
744
0
  else
745
0
  {
746
0
    SerialIOData *iodata;
747
0
    int     i;
748
749
    /* Avoid repeat catalog lookups for typsend function */
750
0
    iodata = (SerialIOData *) fcinfo->flinfo->fn_extra;
751
0
    if (iodata == NULL)
752
0
    {
753
0
      Oid     typsend;
754
0
      bool    typisvarlena;
755
756
0
      iodata = (SerialIOData *)
757
0
        MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
758
0
                   sizeof(SerialIOData));
759
0
      getTypeBinaryOutputInfo(state->element_type, &typsend,
760
0
                  &typisvarlena);
761
0
      fmgr_info_cxt(typsend, &iodata->typsend,
762
0
              fcinfo->flinfo->fn_mcxt);
763
0
      fcinfo->flinfo->fn_extra = iodata;
764
0
    }
765
766
0
    for (i = 0; i < state->nelems; i++)
767
0
    {
768
0
      bytea    *outputbytes;
769
770
0
      if (state->dnulls[i])
771
0
        continue;
772
0
      outputbytes = SendFunctionCall(&iodata->typsend,
773
0
                       state->dvalues[i]);
774
0
      pq_sendint32(&buf, VARSIZE(outputbytes) - VARHDRSZ);
775
0
      pq_sendbytes(&buf, VARDATA(outputbytes),
776
0
             VARSIZE(outputbytes) - VARHDRSZ);
777
0
    }
778
0
  }
779
780
0
  result = pq_endtypsend(&buf);
781
782
0
  PG_RETURN_BYTEA_P(result);
783
0
}
784
785
Datum
786
array_agg_deserialize(PG_FUNCTION_ARGS)
787
0
{
788
0
  bytea    *sstate;
789
0
  ArrayBuildState *result;
790
0
  StringInfoData buf;
791
0
  Oid     element_type;
792
0
  int64   nelems;
793
0
  const char *temp;
794
795
0
  if (!AggCheckCallContext(fcinfo, NULL))
796
0
    elog(ERROR, "aggregate function called in non-aggregate context");
797
798
0
  sstate = PG_GETARG_BYTEA_PP(0);
799
800
  /*
801
   * Initialize a StringInfo so that we can "receive" it using the standard
802
   * recv-function infrastructure.
803
   */
804
0
  initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
805
0
               VARSIZE_ANY_EXHDR(sstate));
806
807
  /* element_type */
808
0
  element_type = pq_getmsgint(&buf, 4);
809
810
  /* nelems */
811
0
  nelems = pq_getmsgint64(&buf);
812
813
  /* Create output ArrayBuildState with the needed number of elements */
814
0
  result = initArrayResultWithSize(element_type, CurrentMemoryContext,
815
0
                   false, nelems);
816
0
  result->nelems = nelems;
817
818
  /* typlen */
819
0
  result->typlen = pq_getmsgint(&buf, 2);
820
821
  /* typbyval */
822
0
  result->typbyval = pq_getmsgbyte(&buf);
823
824
  /* typalign */
825
0
  result->typalign = pq_getmsgbyte(&buf);
826
827
  /* dnulls */
828
0
  temp = pq_getmsgbytes(&buf, sizeof(bool) * nelems);
829
0
  memcpy(result->dnulls, temp, sizeof(bool) * nelems);
830
831
  /* dvalues --- see comment in array_agg_serialize */
832
0
  if (result->typbyval)
833
0
  {
834
0
    temp = pq_getmsgbytes(&buf, sizeof(Datum) * nelems);
835
0
    memcpy(result->dvalues, temp, sizeof(Datum) * nelems);
836
0
  }
837
0
  else
838
0
  {
839
0
    DeserialIOData *iodata;
840
841
    /* Avoid repeat catalog lookups for typreceive function */
842
0
    iodata = (DeserialIOData *) fcinfo->flinfo->fn_extra;
843
0
    if (iodata == NULL)
844
0
    {
845
0
      Oid     typreceive;
846
847
0
      iodata = (DeserialIOData *)
848
0
        MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
849
0
                   sizeof(DeserialIOData));
850
0
      getTypeBinaryInputInfo(element_type, &typreceive,
851
0
                   &iodata->typioparam);
852
0
      fmgr_info_cxt(typreceive, &iodata->typreceive,
853
0
              fcinfo->flinfo->fn_mcxt);
854
0
      fcinfo->flinfo->fn_extra = iodata;
855
0
    }
856
857
0
    for (int i = 0; i < nelems; i++)
858
0
    {
859
0
      int     itemlen;
860
0
      StringInfoData elem_buf;
861
862
0
      if (result->dnulls[i])
863
0
      {
864
0
        result->dvalues[i] = (Datum) 0;
865
0
        continue;
866
0
      }
867
868
0
      itemlen = pq_getmsgint(&buf, 4);
869
0
      if (itemlen < 0 || itemlen > (buf.len - buf.cursor))
870
0
        ereport(ERROR,
871
0
            (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
872
0
             errmsg("insufficient data left in message")));
873
874
      /*
875
       * Rather than copying data around, we just initialize a
876
       * StringInfo pointing to the correct portion of the message
877
       * buffer.
878
       */
879
0
      initReadOnlyStringInfo(&elem_buf, &buf.data[buf.cursor], itemlen);
880
881
0
      buf.cursor += itemlen;
882
883
      /* Now call the element's receiveproc */
884
0
      result->dvalues[i] = ReceiveFunctionCall(&iodata->typreceive,
885
0
                           &elem_buf,
886
0
                           iodata->typioparam,
887
0
                           -1);
888
0
    }
889
0
  }
890
891
0
  pq_getmsgend(&buf);
892
893
0
  PG_RETURN_POINTER(result);
894
0
}
895
896
Datum
897
array_agg_finalfn(PG_FUNCTION_ARGS)
898
0
{
899
0
  Datum   result;
900
0
  ArrayBuildState *state;
901
0
  int     dims[1];
902
0
  int     lbs[1];
903
904
  /* cannot be called directly because of internal-type argument */
905
0
  Assert(AggCheckCallContext(fcinfo, NULL));
906
907
0
  state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
908
909
0
  if (state == NULL)
910
0
    PG_RETURN_NULL();   /* returns null iff no input values */
911
912
0
  dims[0] = state->nelems;
913
0
  lbs[0] = 1;
914
915
  /*
916
   * Make the result.  We cannot release the ArrayBuildState because
917
   * sometimes aggregate final functions are re-executed.  Rather, it is
918
   * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
919
   * so.
920
   */
921
0
  result = makeMdArrayResult(state, 1, dims, lbs,
922
0
                 CurrentMemoryContext,
923
0
                 false);
924
925
0
  PG_RETURN_DATUM(result);
926
0
}
927
928
/*
929
 * ARRAY_AGG(anyarray) aggregate function
930
 */
931
Datum
932
array_agg_array_transfn(PG_FUNCTION_ARGS)
933
0
{
934
0
  Oid     arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
935
0
  MemoryContext aggcontext;
936
0
  ArrayBuildStateArr *state;
937
938
0
  if (arg1_typeid == InvalidOid)
939
0
    ereport(ERROR,
940
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
941
0
         errmsg("could not determine input data type")));
942
943
  /*
944
   * Note: we do not need a run-time check about whether arg1_typeid is a
945
   * valid array type, because the parser would have verified that while
946
   * resolving the input/result types of this polymorphic aggregate.
947
   */
948
949
0
  if (!AggCheckCallContext(fcinfo, &aggcontext))
950
0
  {
951
    /* cannot be called directly because of internal-type argument */
952
0
    elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
953
0
  }
954
955
956
0
  if (PG_ARGISNULL(0))
957
0
    state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
958
0
  else
959
0
    state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
960
961
0
  state = accumArrayResultArr(state,
962
0
                PG_GETARG_DATUM(1),
963
0
                PG_ARGISNULL(1),
964
0
                arg1_typeid,
965
0
                aggcontext);
966
967
  /*
968
   * The transition type for array_agg() is declared to be "internal", which
969
   * is a pass-by-value type the same size as a pointer.  So we can safely
970
   * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
971
   */
972
0
  PG_RETURN_POINTER(state);
973
0
}
974
975
Datum
976
array_agg_array_combine(PG_FUNCTION_ARGS)
977
0
{
978
0
  ArrayBuildStateArr *state1;
979
0
  ArrayBuildStateArr *state2;
980
0
  MemoryContext agg_context;
981
0
  MemoryContext old_context;
982
983
0
  if (!AggCheckCallContext(fcinfo, &agg_context))
984
0
    elog(ERROR, "aggregate function called in non-aggregate context");
985
986
0
  state1 = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
987
0
  state2 = PG_ARGISNULL(1) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(1);
988
989
0
  if (state2 == NULL)
990
0
  {
991
    /*
992
     * NULL state2 is easy, just return state1, which we know is already
993
     * in the agg_context
994
     */
995
0
    if (state1 == NULL)
996
0
      PG_RETURN_NULL();
997
0
    PG_RETURN_POINTER(state1);
998
0
  }
999
1000
0
  if (state1 == NULL)
1001
0
  {
1002
    /* We must copy state2's data into the agg_context */
1003
0
    old_context = MemoryContextSwitchTo(agg_context);
1004
1005
0
    state1 = initArrayResultArr(state2->array_type, InvalidOid,
1006
0
                  agg_context, false);
1007
1008
0
    state1->abytes = state2->abytes;
1009
0
    state1->data = (char *) palloc(state1->abytes);
1010
1011
0
    if (state2->nullbitmap)
1012
0
    {
1013
0
      int     size = (state2->aitems + 7) / 8;
1014
1015
0
      state1->nullbitmap = (uint8 *) palloc(size);
1016
0
      memcpy(state1->nullbitmap, state2->nullbitmap, size);
1017
0
    }
1018
1019
0
    memcpy(state1->data, state2->data, state2->nbytes);
1020
0
    state1->nbytes = state2->nbytes;
1021
0
    state1->aitems = state2->aitems;
1022
0
    state1->nitems = state2->nitems;
1023
0
    state1->ndims = state2->ndims;
1024
0
    memcpy(state1->dims, state2->dims, sizeof(state2->dims));
1025
0
    memcpy(state1->lbs, state2->lbs, sizeof(state2->lbs));
1026
0
    state1->array_type = state2->array_type;
1027
0
    state1->element_type = state2->element_type;
1028
1029
0
    MemoryContextSwitchTo(old_context);
1030
1031
0
    PG_RETURN_POINTER(state1);
1032
0
  }
1033
1034
  /* We only need to combine the two states if state2 has any items */
1035
0
  if (state2->nitems > 0)
1036
0
  {
1037
0
    MemoryContext oldContext;
1038
0
    int     reqsize;
1039
0
    int     newnitems;
1040
0
    int     i;
1041
1042
    /*
1043
     * Check the states are compatible with each other.  Ensure we use the
1044
     * same error messages that are listed in accumArrayResultArr so that
1045
     * the same error is shown as would have been if we'd not used the
1046
     * combine function for the aggregation.
1047
     */
1048
0
    if (state1->ndims != state2->ndims)
1049
0
      ereport(ERROR,
1050
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1051
0
           errmsg("cannot accumulate arrays of different dimensionality")));
1052
1053
    /* Check dimensions match ignoring the first dimension. */
1054
0
    for (i = 1; i < state1->ndims; i++)
1055
0
    {
1056
0
      if (state1->dims[i] != state2->dims[i] || state1->lbs[i] != state2->lbs[i])
1057
0
        ereport(ERROR,
1058
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1059
0
             errmsg("cannot accumulate arrays of different dimensionality")));
1060
0
    }
1061
1062
    /* Types should match already. */
1063
0
    Assert(state1->array_type == state2->array_type);
1064
0
    Assert(state1->element_type == state2->element_type);
1065
1066
    /* Calculate new sizes, guarding against overflow. */
1067
0
    if (pg_add_s32_overflow(state1->nbytes, state2->nbytes, &reqsize) ||
1068
0
      pg_add_s32_overflow(state1->nitems, state2->nitems, &newnitems))
1069
0
      ereport(ERROR,
1070
0
          (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1071
0
           errmsg("array size exceeds the maximum allowed (%zu)",
1072
0
              MaxArraySize)));
1073
1074
0
    oldContext = MemoryContextSwitchTo(state1->mcontext);
1075
1076
    /*
1077
     * If there's not enough space in state1 then we'll need to reallocate
1078
     * more.
1079
     */
1080
0
    if (state1->abytes < reqsize)
1081
0
    {
1082
      /* use a power of 2 size rather than allocating just reqsize */
1083
0
      state1->abytes = pg_nextpower2_32(reqsize);
1084
0
      state1->data = (char *) repalloc(state1->data, state1->abytes);
1085
0
    }
1086
1087
    /* Combine the null bitmaps, if present. */
1088
0
    if (state1->nullbitmap || state2->nullbitmap)
1089
0
    {
1090
0
      if (state1->nullbitmap == NULL)
1091
0
      {
1092
        /*
1093
         * First input with nulls; we must retrospectively handle any
1094
         * previous inputs by marking all their items non-null.
1095
         */
1096
0
        state1->aitems = pg_nextpower2_32(Max(256, newnitems));
1097
0
        state1->nullbitmap = palloc_array(uint8, (state1->aitems + 7) / 8);
1098
0
        array_bitmap_copy(state1->nullbitmap, 0,
1099
0
                  NULL, 0,
1100
0
                  state1->nitems);
1101
0
      }
1102
0
      else if (newnitems > state1->aitems)
1103
0
      {
1104
0
        state1->aitems = pg_nextpower2_32(newnitems);
1105
0
        state1->nullbitmap = repalloc_array(state1->nullbitmap,
1106
0
                          uint8, (state1->aitems + 7) / 8);
1107
0
      }
1108
      /* This will do the right thing if state2->nullbitmap is NULL: */
1109
0
      array_bitmap_copy(state1->nullbitmap, state1->nitems,
1110
0
                state2->nullbitmap, 0,
1111
0
                state2->nitems);
1112
0
    }
1113
1114
    /* Finally, combine the data and adjust sizes. */
1115
0
    memcpy(state1->data + state1->nbytes, state2->data, state2->nbytes);
1116
0
    state1->nbytes += state2->nbytes;
1117
0
    state1->nitems += state2->nitems;
1118
1119
0
    state1->dims[0] += state2->dims[0];
1120
    /* remaining dims already match, per test above */
1121
1122
0
    MemoryContextSwitchTo(oldContext);
1123
0
  }
1124
1125
0
  PG_RETURN_POINTER(state1);
1126
0
}
1127
1128
/*
1129
 * array_agg_array_serialize
1130
 *    Serialize ArrayBuildStateArr into bytea.
1131
 */
1132
Datum
1133
array_agg_array_serialize(PG_FUNCTION_ARGS)
1134
0
{
1135
0
  ArrayBuildStateArr *state;
1136
0
  StringInfoData buf;
1137
0
  bytea    *result;
1138
1139
  /* cannot be called directly because of internal-type argument */
1140
0
  Assert(AggCheckCallContext(fcinfo, NULL));
1141
1142
0
  state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1143
1144
0
  pq_begintypsend(&buf);
1145
1146
  /*
1147
   * element_type. Putting this first is more convenient in deserialization
1148
   * so that we can init the new state sooner.
1149
   */
1150
0
  pq_sendint32(&buf, state->element_type);
1151
1152
  /* array_type */
1153
0
  pq_sendint32(&buf, state->array_type);
1154
1155
  /* nbytes */
1156
0
  pq_sendint32(&buf, state->nbytes);
1157
1158
  /* data */
1159
0
  pq_sendbytes(&buf, state->data, state->nbytes);
1160
1161
  /* abytes */
1162
0
  pq_sendint32(&buf, state->abytes);
1163
1164
  /* aitems */
1165
0
  pq_sendint32(&buf, state->aitems);
1166
1167
  /* nullbitmap */
1168
0
  if (state->nullbitmap)
1169
0
  {
1170
0
    Assert(state->aitems > 0);
1171
0
    pq_sendbytes(&buf, state->nullbitmap, (state->aitems + 7) / 8);
1172
0
  }
1173
1174
  /* nitems */
1175
0
  pq_sendint32(&buf, state->nitems);
1176
1177
  /* ndims */
1178
0
  pq_sendint32(&buf, state->ndims);
1179
1180
  /* dims: XXX should we just send ndims elements? */
1181
0
  pq_sendbytes(&buf, state->dims, sizeof(state->dims));
1182
1183
  /* lbs */
1184
0
  pq_sendbytes(&buf, state->lbs, sizeof(state->lbs));
1185
1186
0
  result = pq_endtypsend(&buf);
1187
1188
0
  PG_RETURN_BYTEA_P(result);
1189
0
}
1190
1191
Datum
1192
array_agg_array_deserialize(PG_FUNCTION_ARGS)
1193
0
{
1194
0
  bytea    *sstate;
1195
0
  ArrayBuildStateArr *result;
1196
0
  StringInfoData buf;
1197
0
  Oid     element_type;
1198
0
  Oid     array_type;
1199
0
  int     nbytes;
1200
0
  const char *temp;
1201
1202
  /* cannot be called directly because of internal-type argument */
1203
0
  Assert(AggCheckCallContext(fcinfo, NULL));
1204
1205
0
  sstate = PG_GETARG_BYTEA_PP(0);
1206
1207
  /*
1208
   * Initialize a StringInfo so that we can "receive" it using the standard
1209
   * recv-function infrastructure.
1210
   */
1211
0
  initReadOnlyStringInfo(&buf, VARDATA_ANY(sstate),
1212
0
               VARSIZE_ANY_EXHDR(sstate));
1213
1214
  /* element_type */
1215
0
  element_type = pq_getmsgint(&buf, 4);
1216
1217
  /* array_type */
1218
0
  array_type = pq_getmsgint(&buf, 4);
1219
1220
  /* nbytes */
1221
0
  nbytes = pq_getmsgint(&buf, 4);
1222
1223
0
  result = initArrayResultArr(array_type, element_type,
1224
0
                CurrentMemoryContext, false);
1225
1226
0
  result->abytes = 1024;
1227
0
  while (result->abytes < nbytes)
1228
0
    result->abytes *= 2;
1229
1230
0
  result->data = (char *) palloc(result->abytes);
1231
1232
  /* data */
1233
0
  temp = pq_getmsgbytes(&buf, nbytes);
1234
0
  memcpy(result->data, temp, nbytes);
1235
0
  result->nbytes = nbytes;
1236
1237
  /* abytes */
1238
0
  result->abytes = pq_getmsgint(&buf, 4);
1239
1240
  /* aitems: might be 0 */
1241
0
  result->aitems = pq_getmsgint(&buf, 4);
1242
1243
  /* nullbitmap */
1244
0
  if (result->aitems > 0)
1245
0
  {
1246
0
    int     size = (result->aitems + 7) / 8;
1247
1248
0
    result->nullbitmap = (uint8 *) palloc(size);
1249
0
    temp = pq_getmsgbytes(&buf, size);
1250
0
    memcpy(result->nullbitmap, temp, size);
1251
0
  }
1252
0
  else
1253
0
    result->nullbitmap = NULL;
1254
1255
  /* nitems */
1256
0
  result->nitems = pq_getmsgint(&buf, 4);
1257
1258
  /* ndims */
1259
0
  result->ndims = pq_getmsgint(&buf, 4);
1260
1261
  /* dims */
1262
0
  temp = pq_getmsgbytes(&buf, sizeof(result->dims));
1263
0
  memcpy(result->dims, temp, sizeof(result->dims));
1264
1265
  /* lbs */
1266
0
  temp = pq_getmsgbytes(&buf, sizeof(result->lbs));
1267
0
  memcpy(result->lbs, temp, sizeof(result->lbs));
1268
1269
0
  pq_getmsgend(&buf);
1270
1271
0
  PG_RETURN_POINTER(result);
1272
0
}
1273
1274
Datum
1275
array_agg_array_finalfn(PG_FUNCTION_ARGS)
1276
0
{
1277
0
  Datum   result;
1278
0
  ArrayBuildStateArr *state;
1279
1280
  /* cannot be called directly because of internal-type argument */
1281
0
  Assert(AggCheckCallContext(fcinfo, NULL));
1282
1283
0
  state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
1284
1285
0
  if (state == NULL)
1286
0
    PG_RETURN_NULL();   /* returns null iff no input values */
1287
1288
  /*
1289
   * Make the result.  We cannot release the ArrayBuildStateArr because
1290
   * sometimes aggregate final functions are re-executed.  Rather, it is
1291
   * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
1292
   * so.
1293
   */
1294
0
  result = makeArrayResultArr(state, CurrentMemoryContext, false);
1295
1296
0
  PG_RETURN_DATUM(result);
1297
0
}
1298
1299
/*-----------------------------------------------------------------------------
1300
 * array_position, array_position_start :
1301
 *      return the offset of a value in an array.
1302
 *
1303
 * IS NOT DISTINCT FROM semantics are used for comparisons.  Return NULL when
1304
 * the value is not found.
1305
 *-----------------------------------------------------------------------------
1306
 */
1307
Datum
1308
array_position(PG_FUNCTION_ARGS)
1309
0
{
1310
0
  return array_position_common(fcinfo);
1311
0
}
1312
1313
Datum
1314
array_position_start(PG_FUNCTION_ARGS)
1315
0
{
1316
0
  return array_position_common(fcinfo);
1317
0
}
1318
1319
/*
1320
 * array_position_common
1321
 *    Common code for array_position and array_position_start
1322
 *
1323
 * These are separate wrappers for the sake of opr_sanity regression test.
1324
 * They are not strict so we have to test for null inputs explicitly.
1325
 */
1326
static Datum
1327
array_position_common(FunctionCallInfo fcinfo)
1328
0
{
1329
0
  ArrayType  *array;
1330
0
  Oid     collation = PG_GET_COLLATION();
1331
0
  Oid     element_type;
1332
0
  Datum   searched_element,
1333
0
        value;
1334
0
  bool    isnull;
1335
0
  int     position,
1336
0
        position_min;
1337
0
  bool    found = false;
1338
0
  TypeCacheEntry *typentry;
1339
0
  ArrayMetaState *my_extra;
1340
0
  bool    null_search;
1341
0
  ArrayIterator array_iterator;
1342
1343
0
  if (PG_ARGISNULL(0))
1344
0
    PG_RETURN_NULL();
1345
1346
0
  array = PG_GETARG_ARRAYTYPE_P(0);
1347
1348
  /*
1349
   * We refuse to search for elements in multi-dimensional arrays, since we
1350
   * have no good way to report the element's location in the array.
1351
   */
1352
0
  if (ARR_NDIM(array) > 1)
1353
0
    ereport(ERROR,
1354
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1355
0
         errmsg("searching for elements in multidimensional arrays is not supported")));
1356
1357
  /* Searching in an empty array is well-defined, though: it always fails */
1358
0
  if (ARR_NDIM(array) < 1)
1359
0
    PG_RETURN_NULL();
1360
1361
0
  if (PG_ARGISNULL(1))
1362
0
  {
1363
    /* fast return when the array doesn't have nulls */
1364
0
    if (!array_contains_nulls(array))
1365
0
      PG_RETURN_NULL();
1366
0
    searched_element = (Datum) 0;
1367
0
    null_search = true;
1368
0
  }
1369
0
  else
1370
0
  {
1371
0
    searched_element = PG_GETARG_DATUM(1);
1372
0
    null_search = false;
1373
0
  }
1374
1375
0
  element_type = ARR_ELEMTYPE(array);
1376
0
  position = (ARR_LBOUND(array))[0] - 1;
1377
1378
  /* figure out where to start */
1379
0
  if (PG_NARGS() == 3)
1380
0
  {
1381
0
    if (PG_ARGISNULL(2))
1382
0
      ereport(ERROR,
1383
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1384
0
           errmsg("initial position must not be null")));
1385
1386
0
    position_min = PG_GETARG_INT32(2);
1387
0
  }
1388
0
  else
1389
0
    position_min = (ARR_LBOUND(array))[0];
1390
1391
  /*
1392
   * We arrange to look up type info for array_create_iterator only once per
1393
   * series of calls, assuming the element type doesn't change underneath
1394
   * us.
1395
   */
1396
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1397
0
  if (my_extra == NULL)
1398
0
  {
1399
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1400
0
                            sizeof(ArrayMetaState));
1401
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1402
0
    my_extra->element_type = ~element_type;
1403
0
  }
1404
1405
0
  if (my_extra->element_type != element_type)
1406
0
  {
1407
0
    get_typlenbyvalalign(element_type,
1408
0
               &my_extra->typlen,
1409
0
               &my_extra->typbyval,
1410
0
               &my_extra->typalign);
1411
1412
0
    typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1413
1414
0
    if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1415
0
      ereport(ERROR,
1416
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
1417
0
           errmsg("could not identify an equality operator for type %s",
1418
0
              format_type_be(element_type))));
1419
1420
0
    my_extra->element_type = element_type;
1421
0
    fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1422
0
            fcinfo->flinfo->fn_mcxt);
1423
0
  }
1424
1425
  /* Examine each array element until we find a match. */
1426
0
  array_iterator = array_create_iterator(array, 0, my_extra);
1427
0
  while (array_iterate(array_iterator, &value, &isnull))
1428
0
  {
1429
0
    position++;
1430
1431
    /* skip initial elements if caller requested so */
1432
0
    if (position < position_min)
1433
0
      continue;
1434
1435
    /*
1436
     * Can't look at the array element's value if it's null; but if we
1437
     * search for null, we have a hit and are done.
1438
     */
1439
0
    if (isnull || null_search)
1440
0
    {
1441
0
      if (isnull && null_search)
1442
0
      {
1443
0
        found = true;
1444
0
        break;
1445
0
      }
1446
0
      else
1447
0
        continue;
1448
0
    }
1449
1450
    /* not nulls, so run the operator */
1451
0
    if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1452
0
                       searched_element, value)))
1453
0
    {
1454
0
      found = true;
1455
0
      break;
1456
0
    }
1457
0
  }
1458
1459
0
  array_free_iterator(array_iterator);
1460
1461
  /* Avoid leaking memory when handed toasted input */
1462
0
  PG_FREE_IF_COPY(array, 0);
1463
1464
0
  if (!found)
1465
0
    PG_RETURN_NULL();
1466
1467
0
  PG_RETURN_INT32(position);
1468
0
}
1469
1470
/*-----------------------------------------------------------------------------
1471
 * array_positions :
1472
 *      return an array of positions of a value in an array.
1473
 *
1474
 * IS NOT DISTINCT FROM semantics are used for comparisons.  Returns NULL when
1475
 * the input array is NULL.  When the value is not found in the array, returns
1476
 * an empty array.
1477
 *
1478
 * This is not strict so we have to test for null inputs explicitly.
1479
 *-----------------------------------------------------------------------------
1480
 */
1481
Datum
1482
array_positions(PG_FUNCTION_ARGS)
1483
0
{
1484
0
  ArrayType  *array;
1485
0
  Oid     collation = PG_GET_COLLATION();
1486
0
  Oid     element_type;
1487
0
  Datum   searched_element,
1488
0
        value;
1489
0
  bool    isnull;
1490
0
  int     position;
1491
0
  TypeCacheEntry *typentry;
1492
0
  ArrayMetaState *my_extra;
1493
0
  bool    null_search;
1494
0
  ArrayIterator array_iterator;
1495
0
  ArrayBuildState *astate = NULL;
1496
1497
0
  if (PG_ARGISNULL(0))
1498
0
    PG_RETURN_NULL();
1499
1500
0
  array = PG_GETARG_ARRAYTYPE_P(0);
1501
1502
  /*
1503
   * We refuse to search for elements in multi-dimensional arrays, since we
1504
   * have no good way to report the element's location in the array.
1505
   */
1506
0
  if (ARR_NDIM(array) > 1)
1507
0
    ereport(ERROR,
1508
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1509
0
         errmsg("searching for elements in multidimensional arrays is not supported")));
1510
1511
0
  astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
1512
1513
  /* Searching in an empty array is well-defined, though: it always fails */
1514
0
  if (ARR_NDIM(array) < 1)
1515
0
    PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1516
1517
0
  if (PG_ARGISNULL(1))
1518
0
  {
1519
    /* fast return when the array doesn't have nulls */
1520
0
    if (!array_contains_nulls(array))
1521
0
      PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1522
0
    searched_element = (Datum) 0;
1523
0
    null_search = true;
1524
0
  }
1525
0
  else
1526
0
  {
1527
0
    searched_element = PG_GETARG_DATUM(1);
1528
0
    null_search = false;
1529
0
  }
1530
1531
0
  element_type = ARR_ELEMTYPE(array);
1532
0
  position = (ARR_LBOUND(array))[0] - 1;
1533
1534
  /*
1535
   * We arrange to look up type info for array_create_iterator only once per
1536
   * series of calls, assuming the element type doesn't change underneath
1537
   * us.
1538
   */
1539
0
  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1540
0
  if (my_extra == NULL)
1541
0
  {
1542
0
    fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1543
0
                            sizeof(ArrayMetaState));
1544
0
    my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
1545
0
    my_extra->element_type = ~element_type;
1546
0
  }
1547
1548
0
  if (my_extra->element_type != element_type)
1549
0
  {
1550
0
    get_typlenbyvalalign(element_type,
1551
0
               &my_extra->typlen,
1552
0
               &my_extra->typbyval,
1553
0
               &my_extra->typalign);
1554
1555
0
    typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
1556
1557
0
    if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
1558
0
      ereport(ERROR,
1559
0
          (errcode(ERRCODE_UNDEFINED_FUNCTION),
1560
0
           errmsg("could not identify an equality operator for type %s",
1561
0
              format_type_be(element_type))));
1562
1563
0
    my_extra->element_type = element_type;
1564
0
    fmgr_info_cxt(typentry->eq_opr_finfo.fn_oid, &my_extra->proc,
1565
0
            fcinfo->flinfo->fn_mcxt);
1566
0
  }
1567
1568
  /*
1569
   * Accumulate each array position iff the element matches the given
1570
   * element.
1571
   */
1572
0
  array_iterator = array_create_iterator(array, 0, my_extra);
1573
0
  while (array_iterate(array_iterator, &value, &isnull))
1574
0
  {
1575
0
    position += 1;
1576
1577
    /*
1578
     * Can't look at the array element's value if it's null; but if we
1579
     * search for null, we have a hit.
1580
     */
1581
0
    if (isnull || null_search)
1582
0
    {
1583
0
      if (isnull && null_search)
1584
0
        astate =
1585
0
          accumArrayResult(astate, Int32GetDatum(position), false,
1586
0
                   INT4OID, CurrentMemoryContext);
1587
1588
0
      continue;
1589
0
    }
1590
1591
    /* not nulls, so run the operator */
1592
0
    if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
1593
0
                       searched_element, value)))
1594
0
      astate =
1595
0
        accumArrayResult(astate, Int32GetDatum(position), false,
1596
0
                 INT4OID, CurrentMemoryContext);
1597
0
  }
1598
1599
0
  array_free_iterator(array_iterator);
1600
1601
  /* Avoid leaking memory when handed toasted input */
1602
0
  PG_FREE_IF_COPY(array, 0);
1603
1604
0
  PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
1605
0
}
1606
1607
/*
1608
 * array_shuffle_n
1609
 *    Return a copy of array with n randomly chosen items.
1610
 *
1611
 * The number of items must not exceed the size of the first dimension of the
1612
 * array.  We preserve the first dimension's lower bound if keep_lb,
1613
 * else it's set to 1.  Lower-order dimensions are preserved in any case.
1614
 *
1615
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
1616
 * from the system catalogs, given only the elmtyp. However, the caller is
1617
 * in a better position to cache this info across multiple calls.
1618
 */
1619
static ArrayType *
1620
array_shuffle_n(ArrayType *array, int n, bool keep_lb,
1621
        Oid elmtyp, TypeCacheEntry *typentry)
1622
0
{
1623
0
  ArrayType  *result;
1624
0
  int     ndim,
1625
0
         *dims,
1626
0
         *lbs,
1627
0
        nelm,
1628
0
        nitem,
1629
0
        rdims[MAXDIM],
1630
0
        rlbs[MAXDIM];
1631
0
  int16   elmlen;
1632
0
  bool    elmbyval;
1633
0
  char    elmalign;
1634
0
  Datum    *elms,
1635
0
         *ielms;
1636
0
  bool     *nuls,
1637
0
         *inuls;
1638
1639
0
  ndim = ARR_NDIM(array);
1640
0
  dims = ARR_DIMS(array);
1641
0
  lbs = ARR_LBOUND(array);
1642
1643
0
  elmlen = typentry->typlen;
1644
0
  elmbyval = typentry->typbyval;
1645
0
  elmalign = typentry->typalign;
1646
1647
  /* If the target array is empty, exit fast */
1648
0
  if (ndim < 1 || dims[0] < 1 || n < 1)
1649
0
    return construct_empty_array(elmtyp);
1650
1651
0
  deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1652
0
            &elms, &nuls, &nelm);
1653
1654
0
  nitem = dims[0];      /* total number of items */
1655
0
  nelm /= nitem;        /* number of elements per item */
1656
1657
0
  Assert(n <= nitem);     /* else it's caller error */
1658
1659
  /*
1660
   * Shuffle array using Fisher-Yates algorithm.  Scan the array and swap
1661
   * current item (nelm datums starting at ielms) with a randomly chosen
1662
   * later item (nelm datums starting at jelms) in each iteration.  We can
1663
   * stop once we've done n iterations; then first n items are the result.
1664
   */
1665
0
  ielms = elms;
1666
0
  inuls = nuls;
1667
0
  for (int i = 0; i < n; i++)
1668
0
  {
1669
0
    int     j = (int) pg_prng_uint64_range(&pg_global_prng_state, i, nitem - 1) * nelm;
1670
0
    Datum    *jelms = elms + j;
1671
0
    bool     *jnuls = nuls + j;
1672
1673
    /* Swap i'th and j'th items; advance ielms/inuls to next item */
1674
0
    for (int k = 0; k < nelm; k++)
1675
0
    {
1676
0
      Datum   elm = *ielms;
1677
0
      bool    nul = *inuls;
1678
1679
0
      *ielms++ = *jelms;
1680
0
      *inuls++ = *jnuls;
1681
0
      *jelms++ = elm;
1682
0
      *jnuls++ = nul;
1683
0
    }
1684
0
  }
1685
1686
  /* Set up dimensions of the result */
1687
0
  memcpy(rdims, dims, ndim * sizeof(int));
1688
0
  memcpy(rlbs, lbs, ndim * sizeof(int));
1689
0
  rdims[0] = n;
1690
0
  if (!keep_lb)
1691
0
    rlbs[0] = 1;
1692
1693
0
  result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1694
0
                elmtyp, elmlen, elmbyval, elmalign);
1695
1696
0
  pfree(elms);
1697
0
  pfree(nuls);
1698
1699
0
  return result;
1700
0
}
1701
1702
/*
1703
 * array_shuffle
1704
 *
1705
 * Returns an array with the same dimensions as the input array, with its
1706
 * first-dimension elements in random order.
1707
 */
1708
Datum
1709
array_shuffle(PG_FUNCTION_ARGS)
1710
0
{
1711
0
  ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
1712
0
  ArrayType  *result;
1713
0
  Oid     elmtyp;
1714
0
  TypeCacheEntry *typentry;
1715
1716
  /*
1717
   * There is no point in shuffling empty arrays or arrays with less than
1718
   * two items.
1719
   */
1720
0
  if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
1721
0
    PG_RETURN_ARRAYTYPE_P(array);
1722
1723
0
  elmtyp = ARR_ELEMTYPE(array);
1724
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1725
0
  if (typentry == NULL || typentry->type_id != elmtyp)
1726
0
  {
1727
0
    typentry = lookup_type_cache(elmtyp, 0);
1728
0
    fcinfo->flinfo->fn_extra = typentry;
1729
0
  }
1730
1731
0
  result = array_shuffle_n(array, ARR_DIMS(array)[0], true, elmtyp, typentry);
1732
1733
0
  PG_RETURN_ARRAYTYPE_P(result);
1734
0
}
1735
1736
/*
1737
 * array_sample
1738
 *
1739
 * Returns an array of n randomly chosen first-dimension elements
1740
 * from the input array.
1741
 */
1742
Datum
1743
array_sample(PG_FUNCTION_ARGS)
1744
0
{
1745
0
  ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
1746
0
  int     n = PG_GETARG_INT32(1);
1747
0
  ArrayType  *result;
1748
0
  Oid     elmtyp;
1749
0
  TypeCacheEntry *typentry;
1750
0
  int     nitem;
1751
1752
0
  nitem = (ARR_NDIM(array) < 1) ? 0 : ARR_DIMS(array)[0];
1753
1754
0
  if (n < 0 || n > nitem)
1755
0
    ereport(ERROR,
1756
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1757
0
         errmsg("sample size must be between 0 and %d", nitem)));
1758
1759
0
  elmtyp = ARR_ELEMTYPE(array);
1760
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1761
0
  if (typentry == NULL || typentry->type_id != elmtyp)
1762
0
  {
1763
0
    typentry = lookup_type_cache(elmtyp, 0);
1764
0
    fcinfo->flinfo->fn_extra = typentry;
1765
0
  }
1766
1767
0
  result = array_shuffle_n(array, n, false, elmtyp, typentry);
1768
1769
0
  PG_RETURN_ARRAYTYPE_P(result);
1770
0
}
1771
1772
1773
/*
1774
 * array_reverse_n
1775
 *    Return a copy of array with reversed items.
1776
 *
1777
 * NOTE: it would be cleaner to look up the elmlen/elmbval/elmalign info
1778
 * from the system catalogs, given only the elmtyp. However, the caller is
1779
 * in a better position to cache this info across multiple calls.
1780
 */
1781
static ArrayType *
1782
array_reverse_n(ArrayType *array, Oid elmtyp, TypeCacheEntry *typentry)
1783
0
{
1784
0
  ArrayType  *result;
1785
0
  int     ndim,
1786
0
         *dims,
1787
0
         *lbs,
1788
0
        nelm,
1789
0
        nitem,
1790
0
        rdims[MAXDIM],
1791
0
        rlbs[MAXDIM];
1792
0
  int16   elmlen;
1793
0
  bool    elmbyval;
1794
0
  char    elmalign;
1795
0
  Datum    *elms,
1796
0
         *ielms;
1797
0
  bool     *nuls,
1798
0
         *inuls;
1799
1800
0
  ndim = ARR_NDIM(array);
1801
0
  dims = ARR_DIMS(array);
1802
0
  lbs = ARR_LBOUND(array);
1803
1804
0
  elmlen = typentry->typlen;
1805
0
  elmbyval = typentry->typbyval;
1806
0
  elmalign = typentry->typalign;
1807
1808
0
  deconstruct_array(array, elmtyp, elmlen, elmbyval, elmalign,
1809
0
            &elms, &nuls, &nelm);
1810
1811
0
  nitem = dims[0];      /* total number of items */
1812
0
  nelm /= nitem;        /* number of elements per item */
1813
1814
  /* Reverse the array */
1815
0
  ielms = elms;
1816
0
  inuls = nuls;
1817
0
  for (int i = 0; i < nitem / 2; i++)
1818
0
  {
1819
0
    int     j = (nitem - i - 1) * nelm;
1820
0
    Datum    *jelms = elms + j;
1821
0
    bool     *jnuls = nuls + j;
1822
1823
    /* Swap i'th and j'th items; advance ielms/inuls to next item */
1824
0
    for (int k = 0; k < nelm; k++)
1825
0
    {
1826
0
      Datum   elm = *ielms;
1827
0
      bool    nul = *inuls;
1828
1829
0
      *ielms++ = *jelms;
1830
0
      *inuls++ = *jnuls;
1831
0
      *jelms++ = elm;
1832
0
      *jnuls++ = nul;
1833
0
    }
1834
0
  }
1835
1836
  /* Set up dimensions of the result */
1837
0
  memcpy(rdims, dims, ndim * sizeof(int));
1838
0
  memcpy(rlbs, lbs, ndim * sizeof(int));
1839
0
  rdims[0] = nitem;
1840
1841
0
  result = construct_md_array(elms, nuls, ndim, rdims, rlbs,
1842
0
                elmtyp, elmlen, elmbyval, elmalign);
1843
1844
0
  pfree(elms);
1845
0
  pfree(nuls);
1846
1847
0
  return result;
1848
0
}
1849
1850
/*
1851
 * array_reverse
1852
 *
1853
 * Returns an array with the same dimensions as the input array, with its
1854
 * first-dimension elements in reverse order.
1855
 */
1856
Datum
1857
array_reverse(PG_FUNCTION_ARGS)
1858
0
{
1859
0
  ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
1860
0
  ArrayType  *result;
1861
0
  Oid     elmtyp;
1862
0
  TypeCacheEntry *typentry;
1863
1864
  /*
1865
   * There is no point in reversing empty arrays or arrays with less than
1866
   * two items.
1867
   */
1868
0
  if (ARR_NDIM(array) < 1 || ARR_DIMS(array)[0] < 2)
1869
0
    PG_RETURN_ARRAYTYPE_P(array);
1870
1871
0
  elmtyp = ARR_ELEMTYPE(array);
1872
0
  typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
1873
0
  if (typentry == NULL || typentry->type_id != elmtyp)
1874
0
  {
1875
0
    typentry = lookup_type_cache(elmtyp, 0);
1876
0
    fcinfo->flinfo->fn_extra = (void *) typentry;
1877
0
  }
1878
1879
0
  result = array_reverse_n(array, elmtyp, typentry);
1880
1881
0
  PG_RETURN_ARRAYTYPE_P(result);
1882
0
}
1883
1884
/*
1885
 * array_sort
1886
 *
1887
 * Sorts the first dimension of the array.
1888
 */
1889
static ArrayType *
1890
array_sort_internal(ArrayType *array, bool descending, bool nulls_first,
1891
          FunctionCallInfo fcinfo)
1892
0
{
1893
0
  ArrayType  *newarray;
1894
0
  Oid     collation = PG_GET_COLLATION();
1895
0
  int     ndim,
1896
0
         *dims,
1897
0
         *lbs;
1898
0
  ArraySortCachedInfo *cache_info;
1899
0
  Oid     elmtyp;
1900
0
  Oid     sort_typ;
1901
0
  Oid     sort_opr;
1902
0
  Tuplesortstate *tuplesortstate;
1903
0
  ArrayIterator array_iterator;
1904
0
  Datum   value;
1905
0
  bool    isnull;
1906
0
  ArrayBuildStateAny *astate = NULL;
1907
1908
0
  ndim = ARR_NDIM(array);
1909
0
  dims = ARR_DIMS(array);
1910
0
  lbs = ARR_LBOUND(array);
1911
1912
  /* Quick exit if we don't need to sort */
1913
0
  if (ndim < 1 || dims[0] < 2)
1914
0
    return array;
1915
1916
  /* Set up cache area if we didn't already */
1917
0
  cache_info = (ArraySortCachedInfo *) fcinfo->flinfo->fn_extra;
1918
0
  if (cache_info == NULL)
1919
0
  {
1920
0
    cache_info = (ArraySortCachedInfo *)
1921
0
      MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
1922
0
                   sizeof(ArraySortCachedInfo));
1923
0
    fcinfo->flinfo->fn_extra = cache_info;
1924
0
  }
1925
1926
  /* Fetch and cache required data if we don't have it */
1927
0
  elmtyp = ARR_ELEMTYPE(array);
1928
0
  if (elmtyp != cache_info->array_meta.element_type)
1929
0
  {
1930
0
    TypeCacheEntry *typentry;
1931
1932
0
    typentry = lookup_type_cache(elmtyp,
1933
0
                   TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
1934
0
    cache_info->array_meta.element_type = elmtyp;
1935
0
    cache_info->array_meta.typlen = typentry->typlen;
1936
0
    cache_info->array_meta.typbyval = typentry->typbyval;
1937
0
    cache_info->array_meta.typalign = typentry->typalign;
1938
0
    cache_info->elem_lt_opr = typentry->lt_opr;
1939
0
    cache_info->elem_gt_opr = typentry->gt_opr;
1940
0
    cache_info->array_type = typentry->typarray;
1941
0
  }
1942
1943
  /* Identify the sort operator to use */
1944
0
  if (ndim == 1)
1945
0
  {
1946
    /* Need to sort the element type */
1947
0
    sort_typ = elmtyp;
1948
0
    sort_opr = (descending ? cache_info->elem_gt_opr : cache_info->elem_lt_opr);
1949
0
  }
1950
0
  else
1951
0
  {
1952
    /* Otherwise we're sorting arrays */
1953
0
    sort_typ = cache_info->array_type;
1954
0
    if (!OidIsValid(sort_typ))
1955
0
      ereport(ERROR,
1956
0
          (errcode(ERRCODE_UNDEFINED_OBJECT),
1957
0
           errmsg("could not find array type for data type %s",
1958
0
              format_type_be(elmtyp))));
1959
    /* We know what operators to use for arrays */
1960
0
    sort_opr = (descending ? ARRAY_GT_OP : ARRAY_LT_OP);
1961
0
  }
1962
1963
  /*
1964
   * Fail if we don't know how to sort.  The error message is chosen to
1965
   * match what array_lt()/array_gt() will say in the multidimensional case.
1966
   */
1967
0
  if (!OidIsValid(sort_opr))
1968
0
    ereport(ERROR,
1969
0
        errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1970
0
        errmsg("could not identify a comparison function for type %s",
1971
0
             format_type_be(elmtyp)));
1972
1973
  /* Put the things to be sorted (elements or sub-arrays) into a tuplesort */
1974
0
  tuplesortstate = tuplesort_begin_datum(sort_typ,
1975
0
                       sort_opr,
1976
0
                       collation,
1977
0
                       nulls_first,
1978
0
                       work_mem,
1979
0
                       NULL,
1980
0
                       TUPLESORT_NONE);
1981
1982
0
  array_iterator = array_create_iterator(array, ndim - 1,
1983
0
                       &cache_info->array_meta);
1984
0
  while (array_iterate(array_iterator, &value, &isnull))
1985
0
  {
1986
0
    tuplesort_putdatum(tuplesortstate, value, isnull);
1987
0
  }
1988
0
  array_free_iterator(array_iterator);
1989
1990
  /* Do the sort */
1991
0
  tuplesort_performsort(tuplesortstate);
1992
1993
  /* Extract results into a new array */
1994
0
  while (tuplesort_getdatum(tuplesortstate, true, false, &value, &isnull, NULL))
1995
0
  {
1996
0
    astate = accumArrayResultAny(astate, value, isnull,
1997
0
                   sort_typ, CurrentMemoryContext);
1998
0
  }
1999
0
  tuplesort_end(tuplesortstate);
2000
2001
0
  newarray = DatumGetArrayTypeP(makeArrayResultAny(astate,
2002
0
                           CurrentMemoryContext,
2003
0
                           true));
2004
2005
  /* Adjust lower bound to match the input */
2006
0
  ARR_LBOUND(newarray)[0] = lbs[0];
2007
2008
0
  return newarray;
2009
0
}
2010
2011
Datum
2012
array_sort(PG_FUNCTION_ARGS)
2013
0
{
2014
0
  ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
2015
2016
0
  PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
2017
0
                        false,
2018
0
                        false,
2019
0
                        fcinfo));
2020
0
}
2021
2022
Datum
2023
array_sort_order(PG_FUNCTION_ARGS)
2024
0
{
2025
0
  ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
2026
0
  bool    descending = PG_GETARG_BOOL(1);
2027
2028
0
  PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
2029
0
                        descending,
2030
0
                        descending,
2031
0
                        fcinfo));
2032
0
}
2033
2034
Datum
2035
array_sort_order_nulls_first(PG_FUNCTION_ARGS)
2036
0
{
2037
0
  ArrayType  *array = PG_GETARG_ARRAYTYPE_P(0);
2038
0
  bool    descending = PG_GETARG_BOOL(1);
2039
0
  bool    nulls_first = PG_GETARG_BOOL(2);
2040
2041
  PG_RETURN_ARRAYTYPE_P(array_sort_internal(array,
2042
0
                        descending,
2043
0
                        nulls_first,
2044
0
                        fcinfo));
2045
0
}