PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
array_userfuncs.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * array_userfuncs.c
4  * Misc user-visible array support functions
5  *
6  * Copyright (c) 2003-2015, 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_type.h"
16 #include "utils/array.h"
17 #include "utils/builtins.h"
18 #include "utils/lsyscache.h"
19 #include "utils/typcache.h"
20 
21 
23 
24 
25 /*
26  * fetch_array_arg_replace_nulls
27  *
28  * Fetch an array-valued argument in expanded form; if it's null, construct an
29  * empty array value of the proper data type. Also cache basic element type
30  * information in fn_extra.
31  *
32  * Caution: if the input is a read/write pointer, this returns the input
33  * argument; so callers must be sure that their changes are "safe", that is
34  * they cannot leave the array in a corrupt state.
35  */
36 static ExpandedArrayHeader *
38 {
40  Oid element_type;
41  ArrayMetaState *my_extra;
42 
43  /* If first time through, create datatype cache struct */
44  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
45  if (my_extra == NULL)
46  {
47  my_extra = (ArrayMetaState *)
49  sizeof(ArrayMetaState));
50  my_extra->element_type = InvalidOid;
51  fcinfo->flinfo->fn_extra = my_extra;
52  }
53 
54  /* Now collect the array value */
55  if (!PG_ARGISNULL(argno))
56  {
57  eah = PG_GETARG_EXPANDED_ARRAYX(argno, my_extra);
58  }
59  else
60  {
61  /* We have to look up the array type and element type */
62  Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
63 
64  if (!OidIsValid(arr_typeid))
65  ereport(ERROR,
66  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
67  errmsg("could not determine input data type")));
68  element_type = get_element_type(arr_typeid);
69  if (!OidIsValid(element_type))
70  ereport(ERROR,
71  (errcode(ERRCODE_DATATYPE_MISMATCH),
72  errmsg("input data type is not an array")));
73 
74  eah = construct_empty_expanded_array(element_type,
76  my_extra);
77  }
78 
79  return eah;
80 }
81 
82 /*-----------------------------------------------------------------------------
83  * array_append :
84  * push an element onto the end of a one-dimensional array
85  *----------------------------------------------------------------------------
86  */
87 Datum
89 {
91  Datum newelem;
92  bool isNull;
93  Datum result;
94  int *dimv,
95  *lb;
96  int indx;
97  ArrayMetaState *my_extra;
98 
99  eah = fetch_array_arg_replace_nulls(fcinfo, 0);
100  isNull = PG_ARGISNULL(1);
101  if (isNull)
102  newelem = (Datum) 0;
103  else
104  newelem = PG_GETARG_DATUM(1);
105 
106  if (eah->ndims == 1)
107  {
108  /* append newelem */
109  int ub;
110 
111  lb = eah->lbound;
112  dimv = eah->dims;
113  ub = dimv[0] + lb[0] - 1;
114  indx = ub + 1;
115 
116  /* overflow? */
117  if (indx < ub)
118  ereport(ERROR,
119  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
120  errmsg("integer out of range")));
121  }
122  else if (eah->ndims == 0)
123  indx = 1;
124  else
125  ereport(ERROR,
126  (errcode(ERRCODE_DATA_EXCEPTION),
127  errmsg("argument must be empty or one-dimensional array")));
128 
129  /* Perform element insertion */
130  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
131 
132  result = array_set_element(EOHPGetRWDatum(&eah->hdr),
133  1, &indx, newelem, isNull,
134  -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
135 
136  PG_RETURN_DATUM(result);
137 }
138 
139 /*-----------------------------------------------------------------------------
140  * array_prepend :
141  * push an element onto the front of a one-dimensional array
142  *----------------------------------------------------------------------------
143  */
144 Datum
146 {
147  ExpandedArrayHeader *eah;
148  Datum newelem;
149  bool isNull;
150  Datum result;
151  int *lb;
152  int indx;
153  int lb0;
154  ArrayMetaState *my_extra;
155 
156  isNull = PG_ARGISNULL(0);
157  if (isNull)
158  newelem = (Datum) 0;
159  else
160  newelem = PG_GETARG_DATUM(0);
161  eah = fetch_array_arg_replace_nulls(fcinfo, 1);
162 
163  if (eah->ndims == 1)
164  {
165  /* prepend newelem */
166  lb = eah->lbound;
167  indx = lb[0] - 1;
168  lb0 = lb[0];
169 
170  /* overflow? */
171  if (indx > lb[0])
172  ereport(ERROR,
173  (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
174  errmsg("integer out of range")));
175  }
176  else if (eah->ndims == 0)
177  {
178  indx = 1;
179  lb0 = 1;
180  }
181  else
182  ereport(ERROR,
183  (errcode(ERRCODE_DATA_EXCEPTION),
184  errmsg("argument must be empty or one-dimensional array")));
185 
186  /* Perform element insertion */
187  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
188 
189  result = array_set_element(EOHPGetRWDatum(&eah->hdr),
190  1, &indx, newelem, isNull,
191  -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
192 
193  /* Readjust result's LB to match the input's, as expected for prepend */
194  Assert(result == EOHPGetRWDatum(&eah->hdr));
195  if (eah->ndims == 1)
196  {
197  /* This is ok whether we've deconstructed or not */
198  eah->lbound[0] = lb0;
199  }
200 
201  PG_RETURN_DATUM(result);
202 }
203 
204 /*-----------------------------------------------------------------------------
205  * array_cat :
206  * concatenate two nD arrays to form an nD array, or
207  * push an (n-1)D array onto the end of an nD array
208  *----------------------------------------------------------------------------
209  */
210 Datum
212 {
213  ArrayType *v1,
214  *v2;
215  ArrayType *result;
216  int *dims,
217  *lbs,
218  ndims,
219  nitems,
220  ndatabytes,
221  nbytes;
222  int *dims1,
223  *lbs1,
224  ndims1,
225  nitems1,
226  ndatabytes1;
227  int *dims2,
228  *lbs2,
229  ndims2,
230  nitems2,
231  ndatabytes2;
232  int i;
233  char *dat1,
234  *dat2;
235  bits8 *bitmap1,
236  *bitmap2;
237  Oid element_type;
238  Oid element_type1;
239  Oid element_type2;
240  int32 dataoffset;
241 
242  /* Concatenating a null array is a no-op, just return the other input */
243  if (PG_ARGISNULL(0))
244  {
245  if (PG_ARGISNULL(1))
246  PG_RETURN_NULL();
247  result = PG_GETARG_ARRAYTYPE_P(1);
248  PG_RETURN_ARRAYTYPE_P(result);
249  }
250  if (PG_ARGISNULL(1))
251  {
252  result = PG_GETARG_ARRAYTYPE_P(0);
253  PG_RETURN_ARRAYTYPE_P(result);
254  }
255 
256  v1 = PG_GETARG_ARRAYTYPE_P(0);
257  v2 = PG_GETARG_ARRAYTYPE_P(1);
258 
259  element_type1 = ARR_ELEMTYPE(v1);
260  element_type2 = ARR_ELEMTYPE(v2);
261 
262  /* Check we have matching element types */
263  if (element_type1 != element_type2)
264  ereport(ERROR,
265  (errcode(ERRCODE_DATATYPE_MISMATCH),
266  errmsg("cannot concatenate incompatible arrays"),
267  errdetail("Arrays with element types %s and %s are not "
268  "compatible for concatenation.",
269  format_type_be(element_type1),
270  format_type_be(element_type2))));
271 
272  /* OK, use it */
273  element_type = element_type1;
274 
275  /*----------
276  * We must have one of the following combinations of inputs:
277  * 1) one empty array, and one non-empty array
278  * 2) both arrays empty
279  * 3) two arrays with ndims1 == ndims2
280  * 4) ndims1 == ndims2 - 1
281  * 5) ndims1 == ndims2 + 1
282  *----------
283  */
284  ndims1 = ARR_NDIM(v1);
285  ndims2 = ARR_NDIM(v2);
286 
287  /*
288  * short circuit - if one input array is empty, and the other is not, we
289  * return the non-empty one as the result
290  *
291  * if both are empty, return the first one
292  */
293  if (ndims1 == 0 && ndims2 > 0)
295 
296  if (ndims2 == 0)
298 
299  /* the rest fall under rule 3, 4, or 5 */
300  if (ndims1 != ndims2 &&
301  ndims1 != ndims2 - 1 &&
302  ndims1 != ndims2 + 1)
303  ereport(ERROR,
304  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
305  errmsg("cannot concatenate incompatible arrays"),
306  errdetail("Arrays of %d and %d dimensions are not "
307  "compatible for concatenation.",
308  ndims1, ndims2)));
309 
310  /* get argument array details */
311  lbs1 = ARR_LBOUND(v1);
312  lbs2 = ARR_LBOUND(v2);
313  dims1 = ARR_DIMS(v1);
314  dims2 = ARR_DIMS(v2);
315  dat1 = ARR_DATA_PTR(v1);
316  dat2 = ARR_DATA_PTR(v2);
317  bitmap1 = ARR_NULLBITMAP(v1);
318  bitmap2 = ARR_NULLBITMAP(v2);
319  nitems1 = ArrayGetNItems(ndims1, dims1);
320  nitems2 = ArrayGetNItems(ndims2, dims2);
321  ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
322  ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
323 
324  if (ndims1 == ndims2)
325  {
326  /*
327  * resulting array is made up of the elements (possibly arrays
328  * themselves) of the input argument arrays
329  */
330  ndims = ndims1;
331  dims = (int *) palloc(ndims * sizeof(int));
332  lbs = (int *) palloc(ndims * sizeof(int));
333 
334  dims[0] = dims1[0] + dims2[0];
335  lbs[0] = lbs1[0];
336 
337  for (i = 1; i < ndims; i++)
338  {
339  if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
340  ereport(ERROR,
341  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
342  errmsg("cannot concatenate incompatible arrays"),
343  errdetail("Arrays with differing element dimensions are "
344  "not compatible for concatenation.")));
345 
346  dims[i] = dims1[i];
347  lbs[i] = lbs1[i];
348  }
349  }
350  else if (ndims1 == ndims2 - 1)
351  {
352  /*
353  * resulting array has the second argument as the outer array, with
354  * the first argument inserted at the front of the outer dimension
355  */
356  ndims = ndims2;
357  dims = (int *) palloc(ndims * sizeof(int));
358  lbs = (int *) palloc(ndims * sizeof(int));
359  memcpy(dims, dims2, ndims * sizeof(int));
360  memcpy(lbs, lbs2, ndims * sizeof(int));
361 
362  /* increment number of elements in outer array */
363  dims[0] += 1;
364 
365  /* make sure the added element matches our existing elements */
366  for (i = 0; i < ndims1; i++)
367  {
368  if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
369  ereport(ERROR,
370  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
371  errmsg("cannot concatenate incompatible arrays"),
372  errdetail("Arrays with differing dimensions are not "
373  "compatible for concatenation.")));
374  }
375  }
376  else
377  {
378  /*
379  * (ndims1 == ndims2 + 1)
380  *
381  * resulting array has the first argument as the outer array, with the
382  * second argument appended to the end of the outer dimension
383  */
384  ndims = ndims1;
385  dims = (int *) palloc(ndims * sizeof(int));
386  lbs = (int *) palloc(ndims * sizeof(int));
387  memcpy(dims, dims1, ndims * sizeof(int));
388  memcpy(lbs, lbs1, ndims * sizeof(int));
389 
390  /* increment number of elements in outer array */
391  dims[0] += 1;
392 
393  /* make sure the added element matches our existing elements */
394  for (i = 0; i < ndims2; i++)
395  {
396  if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
397  ereport(ERROR,
398  (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
399  errmsg("cannot concatenate incompatible arrays"),
400  errdetail("Arrays with differing dimensions are not "
401  "compatible for concatenation.")));
402  }
403  }
404 
405  /* Do this mainly for overflow checking */
406  nitems = ArrayGetNItems(ndims, dims);
407 
408  /* build the result array */
409  ndatabytes = ndatabytes1 + ndatabytes2;
410  if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
411  {
412  dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
413  nbytes = ndatabytes + dataoffset;
414  }
415  else
416  {
417  dataoffset = 0; /* marker for no null bitmap */
418  nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
419  }
420  result = (ArrayType *) palloc0(nbytes);
421  SET_VARSIZE(result, nbytes);
422  result->ndim = ndims;
423  result->dataoffset = dataoffset;
424  result->elemtype = element_type;
425  memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
426  memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
427  /* data area is arg1 then arg2 */
428  memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
429  memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
430  /* handle the null bitmap if needed */
431  if (ARR_HASNULL(result))
432  {
434  bitmap1, 0,
435  nitems1);
436  array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
437  bitmap2, 0,
438  nitems2);
439  }
440 
441  PG_RETURN_ARRAYTYPE_P(result);
442 }
443 
444 
445 /*
446  * used by text_to_array() in varlena.c
447  */
448 ArrayType *
450  Oid element_type,
451  Datum element,
452  bool isNull,
453  int ndims)
454 {
455  Datum dvalues[1];
456  bool nulls[1];
457  int16 typlen;
458  bool typbyval;
459  char typalign;
460  int dims[MAXDIM];
461  int lbs[MAXDIM];
462  int i;
463  ArrayMetaState *my_extra;
464 
465  if (ndims < 1)
466  ereport(ERROR,
467  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
468  errmsg("invalid number of dimensions: %d", ndims)));
469  if (ndims > MAXDIM)
470  ereport(ERROR,
471  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
472  errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
473  ndims, MAXDIM)));
474 
475  dvalues[0] = element;
476  nulls[0] = isNull;
477 
478  for (i = 0; i < ndims; i++)
479  {
480  dims[i] = 1;
481  lbs[i] = 1;
482  }
483 
484  /*
485  * We arrange to look up info about element type only once per series of
486  * calls, assuming the element type doesn't change underneath us.
487  */
488  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
489  if (my_extra == NULL)
490  {
491  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
492  sizeof(ArrayMetaState));
493  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
494  my_extra->element_type = ~element_type;
495  }
496 
497  if (my_extra->element_type != element_type)
498  {
499  /* Get info about element type */
500  get_typlenbyvalalign(element_type,
501  &my_extra->typlen,
502  &my_extra->typbyval,
503  &my_extra->typalign);
504  my_extra->element_type = element_type;
505  }
506  typlen = my_extra->typlen;
507  typbyval = my_extra->typbyval;
508  typalign = my_extra->typalign;
509 
510  return construct_md_array(dvalues, nulls, ndims, dims, lbs, element_type,
511  typlen, typbyval, typalign);
512 }
513 
514 
515 /*
516  * ARRAY_AGG(anynonarray) aggregate function
517  */
518 Datum
520 {
521  Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
522  MemoryContext aggcontext;
524  Datum elem;
525 
526  if (arg1_typeid == InvalidOid)
527  ereport(ERROR,
528  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
529  errmsg("could not determine input data type")));
530 
531  /*
532  * Note: we do not need a run-time check about whether arg1_typeid is a
533  * valid array element type, because the parser would have verified that
534  * while resolving the input/result types of this polymorphic aggregate.
535  */
536 
537  if (!AggCheckCallContext(fcinfo, &aggcontext))
538  {
539  /* cannot be called directly because of internal-type argument */
540  elog(ERROR, "array_agg_transfn called in non-aggregate context");
541  }
542 
543  if (PG_ARGISNULL(0))
544  state = initArrayResult(arg1_typeid, aggcontext, false);
545  else
546  state = (ArrayBuildState *) PG_GETARG_POINTER(0);
547 
548  elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
549 
550  state = accumArrayResult(state,
551  elem,
552  PG_ARGISNULL(1),
553  arg1_typeid,
554  aggcontext);
555 
556  /*
557  * The transition type for array_agg() is declared to be "internal", which
558  * is a pass-by-value type the same size as a pointer. So we can safely
559  * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
560  */
561  PG_RETURN_POINTER(state);
562 }
563 
564 Datum
566 {
567  Datum result;
569  int dims[1];
570  int lbs[1];
571 
572  /* cannot be called directly because of internal-type argument */
573  Assert(AggCheckCallContext(fcinfo, NULL));
574 
575  state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
576 
577  if (state == NULL)
578  PG_RETURN_NULL(); /* returns null iff no input values */
579 
580  dims[0] = state->nelems;
581  lbs[0] = 1;
582 
583  /*
584  * Make the result. We cannot release the ArrayBuildState because
585  * sometimes aggregate final functions are re-executed. Rather, it is
586  * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
587  * so.
588  */
589  result = makeMdArrayResult(state, 1, dims, lbs,
591  false);
592 
593  PG_RETURN_DATUM(result);
594 }
595 
596 /*
597  * ARRAY_AGG(anyarray) aggregate function
598  */
599 Datum
601 {
602  Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
603  MemoryContext aggcontext;
605 
606  if (arg1_typeid == InvalidOid)
607  ereport(ERROR,
608  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
609  errmsg("could not determine input data type")));
610 
611  /*
612  * Note: we do not need a run-time check about whether arg1_typeid is a
613  * valid array type, because the parser would have verified that while
614  * resolving the input/result types of this polymorphic aggregate.
615  */
616 
617  if (!AggCheckCallContext(fcinfo, &aggcontext))
618  {
619  /* cannot be called directly because of internal-type argument */
620  elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
621  }
622 
623 
624  if (PG_ARGISNULL(0))
625  state = initArrayResultArr(arg1_typeid, InvalidOid, aggcontext, false);
626  else
627  state = (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
628 
629  state = accumArrayResultArr(state,
630  PG_GETARG_DATUM(1),
631  PG_ARGISNULL(1),
632  arg1_typeid,
633  aggcontext);
634 
635  /*
636  * The transition type for array_agg() is declared to be "internal", which
637  * is a pass-by-value type the same size as a pointer. So we can safely
638  * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
639  */
640  PG_RETURN_POINTER(state);
641 }
642 
643 Datum
645 {
646  Datum result;
648 
649  /* cannot be called directly because of internal-type argument */
650  Assert(AggCheckCallContext(fcinfo, NULL));
651 
653 
654  if (state == NULL)
655  PG_RETURN_NULL(); /* returns null iff no input values */
656 
657  /*
658  * Make the result. We cannot release the ArrayBuildStateArr because
659  * sometimes aggregate final functions are re-executed. Rather, it is
660  * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
661  * so.
662  */
663  result = makeArrayResultArr(state, CurrentMemoryContext, false);
664 
665  PG_RETURN_DATUM(result);
666 }
667 
668 /*-----------------------------------------------------------------------------
669  * array_position, array_position_start :
670  * return the offset of a value in an array.
671  *
672  * IS NOT DISTINCT FROM semantics are used for comparisons. Return NULL when
673  * the value is not found.
674  *-----------------------------------------------------------------------------
675  */
676 Datum
678 {
679  return array_position_common(fcinfo);
680 }
681 
682 Datum
684 {
685  return array_position_common(fcinfo);
686 }
687 
688 /*
689  * array_position_common
690  * Common code for array_position and array_position_start
691  *
692  * These are separate wrappers for the sake of opr_sanity regression test.
693  * They are not strict so we have to test for null inputs explicitly.
694  */
695 static Datum
697 {
698  ArrayType *array;
699  Oid collation = PG_GET_COLLATION();
700  Oid element_type;
701  Datum searched_element,
702  value;
703  bool isnull;
704  int position,
705  position_min;
706  bool found = false;
707  TypeCacheEntry *typentry;
708  ArrayMetaState *my_extra;
709  bool null_search;
711 
712  if (PG_ARGISNULL(0))
713  PG_RETURN_NULL();
714 
715  array = PG_GETARG_ARRAYTYPE_P(0);
716  element_type = ARR_ELEMTYPE(array);
717 
718  /*
719  * We refuse to search for elements in multi-dimensional arrays, since we
720  * have no good way to report the element's location in the array.
721  */
722  if (ARR_NDIM(array) > 1)
723  ereport(ERROR,
724  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
725  errmsg("searching for elements in multidimensional arrays is not supported")));
726 
727  if (PG_ARGISNULL(1))
728  {
729  /* fast return when the array doesn't have nulls */
730  if (!array_contains_nulls(array))
731  PG_RETURN_NULL();
732  searched_element = (Datum) 0;
733  null_search = true;
734  }
735  else
736  {
737  searched_element = PG_GETARG_DATUM(1);
738  null_search = false;
739  }
740 
741  position = (ARR_LBOUND(array))[0] - 1;
742 
743  /* figure out where to start */
744  if (PG_NARGS() == 3)
745  {
746  if (PG_ARGISNULL(2))
747  ereport(ERROR,
748  (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
749  errmsg("initial position should not be NULL")));
750 
751  position_min = PG_GETARG_INT32(2);
752  }
753  else
754  position_min = (ARR_LBOUND(array))[0];
755 
756  /*
757  * We arrange to look up type info for array_create_iterator only once per
758  * series of calls, assuming the element type doesn't change underneath
759  * us.
760  */
761  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
762  if (my_extra == NULL)
763  {
764  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
765  sizeof(ArrayMetaState));
766  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
767  my_extra->element_type = ~element_type;
768  }
769 
770  if (my_extra->element_type != element_type)
771  {
772  get_typlenbyvalalign(element_type,
773  &my_extra->typlen,
774  &my_extra->typbyval,
775  &my_extra->typalign);
776 
777  typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
778 
779  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
780  ereport(ERROR,
781  (errcode(ERRCODE_UNDEFINED_FUNCTION),
782  errmsg("could not identify an equality operator for type %s",
783  format_type_be(element_type))));
784 
785  my_extra->element_type = element_type;
786  fmgr_info(typentry->eq_opr_finfo.fn_oid, &my_extra->proc);
787  }
788 
789  /* Examine each array element until we find a match. */
790  array_iterator = array_create_iterator(array, 0, my_extra);
791  while (array_iterate(array_iterator, &value, &isnull))
792  {
793  position++;
794 
795  /* skip initial elements if caller requested so */
796  if (position < position_min)
797  continue;
798 
799  /*
800  * Can't look at the array element's value if it's null; but if we
801  * search for null, we have a hit and are done.
802  */
803  if (isnull || null_search)
804  {
805  if (isnull && null_search)
806  {
807  found = true;
808  break;
809  }
810  else
811  continue;
812  }
813 
814  /* not nulls, so run the operator */
815  if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
816  searched_element, value)))
817  {
818  found = true;
819  break;
820  }
821  }
822 
823  array_free_iterator(array_iterator);
824 
825  /* Avoid leaking memory when handed toasted input */
826  PG_FREE_IF_COPY(array, 0);
827 
828  if (!found)
829  PG_RETURN_NULL();
830 
831  PG_RETURN_INT32(position);
832 }
833 
834 /*-----------------------------------------------------------------------------
835  * array_positions :
836  * return an array of positions of a value in an array.
837  *
838  * IS NOT DISTINCT FROM semantics are used for comparisons. Returns NULL when
839  * the input array is NULL. When the value is not found in the array, returns
840  * an empty array.
841  *
842  * This is not strict so we have to test for null inputs explicitly.
843  *-----------------------------------------------------------------------------
844  */
845 Datum
847 {
848  ArrayType *array;
849  Oid collation = PG_GET_COLLATION();
850  Oid element_type;
851  Datum searched_element,
852  value;
853  bool isnull;
854  int position;
855  TypeCacheEntry *typentry;
856  ArrayMetaState *my_extra;
857  bool null_search;
859  ArrayBuildState *astate = NULL;
860 
861  if (PG_ARGISNULL(0))
862  PG_RETURN_NULL();
863 
864  array = PG_GETARG_ARRAYTYPE_P(0);
865  element_type = ARR_ELEMTYPE(array);
866 
867  position = (ARR_LBOUND(array))[0] - 1;
868 
869  /*
870  * We refuse to search for elements in multi-dimensional arrays, since we
871  * have no good way to report the element's location in the array.
872  */
873  if (ARR_NDIM(array) > 1)
874  ereport(ERROR,
875  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
876  errmsg("searching for elements in multidimensional arrays is not supported")));
877 
878  astate = initArrayResult(INT4OID, CurrentMemoryContext, false);
879 
880  if (PG_ARGISNULL(1))
881  {
882  /* fast return when the array doesn't have nulls */
883  if (!array_contains_nulls(array))
885  searched_element = (Datum) 0;
886  null_search = true;
887  }
888  else
889  {
890  searched_element = PG_GETARG_DATUM(1);
891  null_search = false;
892  }
893 
894  /*
895  * We arrange to look up type info for array_create_iterator only once per
896  * series of calls, assuming the element type doesn't change underneath
897  * us.
898  */
899  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
900  if (my_extra == NULL)
901  {
902  fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
903  sizeof(ArrayMetaState));
904  my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
905  my_extra->element_type = ~element_type;
906  }
907 
908  if (my_extra->element_type != element_type)
909  {
910  get_typlenbyvalalign(element_type,
911  &my_extra->typlen,
912  &my_extra->typbyval,
913  &my_extra->typalign);
914 
915  typentry = lookup_type_cache(element_type, TYPECACHE_EQ_OPR_FINFO);
916 
917  if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
918  ereport(ERROR,
919  (errcode(ERRCODE_UNDEFINED_FUNCTION),
920  errmsg("could not identify an equality operator for type %s",
921  format_type_be(element_type))));
922 
923  my_extra->element_type = element_type;
924  fmgr_info(typentry->eq_opr_finfo.fn_oid, &my_extra->proc);
925  }
926 
927  /*
928  * Accumulate each array position iff the element matches the given
929  * element.
930  */
931  array_iterator = array_create_iterator(array, 0, my_extra);
932  while (array_iterate(array_iterator, &value, &isnull))
933  {
934  position += 1;
935 
936  /*
937  * Can't look at the array element's value if it's null; but if we
938  * search for null, we have a hit.
939  */
940  if (isnull || null_search)
941  {
942  if (isnull && null_search)
943  astate =
944  accumArrayResult(astate, Int32GetDatum(position), false,
946 
947  continue;
948  }
949 
950  /* not nulls, so run the operator */
951  if (DatumGetBool(FunctionCall2Coll(&my_extra->proc, collation,
952  searched_element, value)))
953  astate =
954  accumArrayResult(astate, Int32GetDatum(position), false,
956  }
957 
958  array_free_iterator(array_iterator);
959 
960  /* Avoid leaking memory when handed toasted input */
961  PG_FREE_IF_COPY(array, 0);
962 
964 }
signed short int16
Definition: c.h:241
#define PG_RETURN_POINTER(x)
Definition: fmgr.h:305
ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim, ArrayMetaState *mstate)
Definition: arrayfuncs.c:4226
Datum makeArrayResultArr(ArrayBuildStateArr *astate, MemoryContext rcontext, bool release)
Definition: arrayfuncs.c:5318
Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, MemoryContext rcontext, bool release)
Definition: arrayfuncs.c:5060
#define PG_GETARG_INT32(n)
Definition: fmgr.h:225
#define ARR_OVERHEAD_NONULLS(ndims)
Definition: array.h:291
MemoryContext fn_mcxt
Definition: fmgr.h:62
#define ARR_SIZE(a)
Definition: array.h:270
ArrayBuildState * initArrayResult(Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:4922
#define MAXDIM
Definition: c.h:404
Datum array_agg_finalfn(PG_FUNCTION_ARGS)
Oid get_element_type(Oid typid)
Definition: lsyscache.c:2387
void get_typlenbyvalalign(Oid typid, int16 *typlen, bool *typbyval, char *typalign)
Definition: lsyscache.c:1924
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:224
Datum array_agg_transfn(PG_FUNCTION_ARGS)
#define TYPECACHE_EQ_OPR_FINFO
Definition: typcache.h:115
void array_bitmap_copy(bits8 *destbitmap, int destoffset, const bits8 *srcbitmap, int srcoffset, int nitems)
Definition: arrayfuncs.c:4595
int32 dataoffset
Definition: array.h:80
#define PG_GETARG_EXPANDED_ARRAYX(n, metacache)
Definition: array.h:250
int ArrayGetNItems(int ndim, const int *dims)
Definition: arrayutils.c:75
#define PG_RETURN_INT32(x)
Definition: fmgr.h:298
#define INT4OID
Definition: pg_type.h:316
int errcode(int sqlerrcode)
Definition: elog.c:569
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
Datum array_set_element(Datum arraydatum, int nSubscripts, int *indx, Datum dataValue, bool isNull, int arraytyplen, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:2179
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:232
Datum FunctionCall2Coll(FmgrInfo *flinfo, Oid collation, Datum arg1, Datum arg2)
Definition: fmgr.c:1307
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:519
#define ARR_OVERHEAD_WITHNULLS(ndims, nitems)
Definition: array.h:293
#define PG_GET_COLLATION()
Definition: fmgr.h:155
signed int int32
Definition: c.h:242
ArrayBuildStateArr * initArrayResultArr(Oid array_type, Oid element_type, MemoryContext rcontext, bool subcontext)
Definition: arrayfuncs.c:5112
#define ARR_DATA_OFFSET(a)
Definition: array.h:297
#define ARR_LBOUND(a)
Definition: array.h:277
Datum array_cat(PG_FUNCTION_ARGS)
bool typbyval
Definition: array.h:221
static ExpandedArrayHeader * fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
FmgrInfo * flinfo
Definition: fmgr.h:71
#define PG_GETARG_ARRAYTYPE_P(n)
Definition: array.h:244
bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull)
Definition: arrayfuncs.c:4305
#define ERROR
Definition: elog.h:41
ExpandedObjectHeader hdr
Definition: array.h:102
Oid elemtype
Definition: array.h:81
static struct @72 value
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:2313
#define ARR_DIMS(a)
Definition: array.h:275
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:160
static Datum array_position_common(FunctionCallInfo fcinfo)
#define ARR_DATA_PTR(a)
Definition: array.h:303
ArrayBuildStateArr * accumArrayResultArr(ArrayBuildStateArr *astate, Datum dvalue, bool disnull, Oid array_type, MemoryContext rcontext)
Definition: arrayfuncs.c:5161
static bool array_iterator(ArrayType *la, PGCALL2 callback, void *param, ltree **found)
Definition: _ltree_op.c:37
int16 typlen
Definition: array.h:220
int errdetail(const char *fmt,...)
Definition: elog.c:864
Datum array_position_start(PG_FUNCTION_ARGS)
#define DatumGetBool(X)
Definition: postgres.h:401
ArrayType * create_singleton_array(FunctionCallInfo fcinfo, Oid element_type, Datum element, bool isNull, int ndims)
#define ARR_HASNULL(a)
Definition: array.h:272
Datum array_position(PG_FUNCTION_ARGS)
MemoryContext CurrentMemoryContext
Definition: mcxt.c:37
#define PG_RETURN_ARRAYTYPE_P(x)
Definition: array.h:246
#define ereport(elevel, rest)
Definition: elog.h:132
Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext)
Definition: arrayfuncs.c:5028
Datum array_agg_array_transfn(PG_FUNCTION_ARGS)
ExpandedArrayHeader * construct_empty_expanded_array(Oid element_type, MemoryContext parentcontext, ArrayMetaState *metacache)
Definition: arrayfuncs.c:3412
uint8 bits8
Definition: c.h:261
void * palloc0(Size size)
Definition: mcxt.c:921
uintptr_t Datum
Definition: postgres.h:374
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:297
FmgrInfo eq_opr_finfo
Definition: typcache.h:67
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:182
#define InvalidOid
Definition: postgres_ext.h:36
Oid fn_oid
Definition: fmgr.h:56
void array_free_iterator(ArrayIterator iterator)
Definition: arrayfuncs.c:4388
#define PG_ARGISNULL(n)
Definition: fmgr.h:166
#define NULL
Definition: c.h:215
#define Assert(condition)
Definition: c.h:656
Definition: regguts.h:308
Datum array_positions(PG_FUNCTION_ARGS)
int AggCheckCallContext(FunctionCallInfo fcinfo, MemoryContext *aggcontext)
Definition: nodeAgg.c:3083
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:216
#define PG_NARGS()
Definition: fmgr.h:160
void * fn_extra
Definition: fmgr.h:61
#define ARR_NDIM(a)
Definition: array.h:271
char typalign
Definition: array.h:222
#define EOHPGetRWDatum(eohptr)
ArrayBuildState * accumArrayResult(ArrayBuildState *astate, Datum dvalue, bool disnull, Oid element_type, MemoryContext rcontext)
Definition: arrayfuncs.c:4964
#define Int32GetDatum(X)
Definition: postgres.h:487
void * palloc(Size size)
Definition: mcxt.c:892
int errmsg(const char *fmt,...)
Definition: elog.c:791
FmgrInfo proc
Definition: array.h:226
void * MemoryContextAlloc(MemoryContext context, Size size)
Definition: mcxt.c:750
int i
static celt element(struct vars *v, const chr *startp, const chr *endp)
Definition: regc_locale.c:367
Oid element_type
Definition: array.h:219
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define SET_VARSIZE(PTR, len)
Definition: postgres.h:330
#define elog
Definition: elog.h:228
Datum array_agg_array_finalfn(PG_FUNCTION_ARGS)
Datum array_append(PG_FUNCTION_ARGS)
Datum array_prepend(PG_FUNCTION_ARGS)
ArrayType * construct_md_array(Datum *elems, bool *nulls, int ndims, int *dims, int *lbs, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3311
bool array_contains_nulls(ArrayType *array)
Definition: arrayfuncs.c:3513
#define ARR_ELEMTYPE(a)
Definition: array.h:273
#define ARR_NULLBITMAP(a)
Definition: array.h:281
#define PG_RETURN_NULL()
Definition: fmgr.h:289
int ndim
Definition: array.h:79