Coverage Report

Created: 2025-07-03 06:49

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