1 /*-------------------------------------------------------------------------
4 * Misc user-visible array support functions
6 * Copyright (c) 2003-2015, PostgreSQL Global Development Group
9 * src/backend/utils/adt/array_userfuncs.c
11 *-------------------------------------------------------------------------
15 #include "utils/array.h"
16 #include "utils/builtins.h"
17 #include "utils/lsyscache.h"
21 * fetch_array_arg_replace_nulls
23 * Fetch an array-valued argument; if it's null, construct an empty array
24 * value of the proper data type. Also cache basic element type information
28 fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
31 ArrayMetaState *my_extra;
33 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
36 /* First time through, so look up the array type and element type */
37 Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
40 if (!OidIsValid(arr_typeid))
42 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
43 errmsg("could not determine input data type")));
44 element_type = get_element_type(arr_typeid);
45 if (!OidIsValid(element_type))
47 (errcode(ERRCODE_DATATYPE_MISMATCH),
48 errmsg("input data type is not an array")));
50 my_extra = (ArrayMetaState *)
51 MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
52 sizeof(ArrayMetaState));
53 my_extra->element_type = element_type;
55 /* Cache info about element type */
56 get_typlenbyvalalign(element_type,
61 fcinfo->flinfo->fn_extra = my_extra;
64 /* Now we can collect the array value */
65 if (PG_ARGISNULL(argno))
66 v = construct_empty_array(my_extra->element_type);
68 v = PG_GETARG_ARRAYTYPE_P(argno);
73 /*-----------------------------------------------------------------------------
75 * push an element onto the end of a one-dimensional array
76 *----------------------------------------------------------------------------
79 array_append(PG_FUNCTION_ARGS)
88 ArrayMetaState *my_extra;
90 v = fetch_array_arg_replace_nulls(fcinfo, 0);
91 isNull = PG_ARGISNULL(1);
95 newelem = PG_GETARG_DATUM(1);
104 ub = dimv[0] + lb[0] - 1;
110 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
111 errmsg("integer out of range")));
113 else if (ARR_NDIM(v) == 0)
117 (errcode(ERRCODE_DATA_EXCEPTION),
118 errmsg("argument must be empty or one-dimensional array")));
120 /* Perform element insertion */
121 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
123 result = array_set(v, 1, &indx, newelem, isNull,
124 -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
126 PG_RETURN_ARRAYTYPE_P(result);
129 /*-----------------------------------------------------------------------------
131 * push an element onto the front of a one-dimensional array
132 *----------------------------------------------------------------------------
135 array_prepend(PG_FUNCTION_ARGS)
143 ArrayMetaState *my_extra;
145 isNull = PG_ARGISNULL(0);
149 newelem = PG_GETARG_DATUM(0);
150 v = fetch_array_arg_replace_nulls(fcinfo, 1);
152 if (ARR_NDIM(v) == 1)
154 /* prepend newelem */
161 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
162 errmsg("integer out of range")));
164 else if (ARR_NDIM(v) == 0)
168 (errcode(ERRCODE_DATA_EXCEPTION),
169 errmsg("argument must be empty or one-dimensional array")));
171 /* Perform element insertion */
172 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
174 result = array_set(v, 1, &indx, newelem, isNull,
175 -1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
177 /* Readjust result's LB to match the input's, as expected for prepend */
178 if (ARR_NDIM(v) == 1)
179 ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
181 PG_RETURN_ARRAYTYPE_P(result);
184 /*-----------------------------------------------------------------------------
186 * concatenate two nD arrays to form an nD array, or
187 * push an (n-1)D array onto the end of an nD array
188 *----------------------------------------------------------------------------
191 array_cat(PG_FUNCTION_ARGS)
222 /* Concatenating a null array is a no-op, just return the other input */
227 result = PG_GETARG_ARRAYTYPE_P(1);
228 PG_RETURN_ARRAYTYPE_P(result);
232 result = PG_GETARG_ARRAYTYPE_P(0);
233 PG_RETURN_ARRAYTYPE_P(result);
236 v1 = PG_GETARG_ARRAYTYPE_P(0);
237 v2 = PG_GETARG_ARRAYTYPE_P(1);
239 element_type1 = ARR_ELEMTYPE(v1);
240 element_type2 = ARR_ELEMTYPE(v2);
242 /* Check we have matching element types */
243 if (element_type1 != element_type2)
245 (errcode(ERRCODE_DATATYPE_MISMATCH),
246 errmsg("cannot concatenate incompatible arrays"),
247 errdetail("Arrays with element types %s and %s are not "
248 "compatible for concatenation.",
249 format_type_be(element_type1),
250 format_type_be(element_type2))));
253 element_type = element_type1;
256 * We must have one of the following combinations of inputs:
257 * 1) one empty array, and one non-empty array
258 * 2) both arrays empty
259 * 3) two arrays with ndims1 == ndims2
260 * 4) ndims1 == ndims2 - 1
261 * 5) ndims1 == ndims2 + 1
264 ndims1 = ARR_NDIM(v1);
265 ndims2 = ARR_NDIM(v2);
268 * short circuit - if one input array is empty, and the other is not, we
269 * return the non-empty one as the result
271 * if both are empty, return the first one
273 if (ndims1 == 0 && ndims2 > 0)
274 PG_RETURN_ARRAYTYPE_P(v2);
277 PG_RETURN_ARRAYTYPE_P(v1);
279 /* the rest fall under rule 3, 4, or 5 */
280 if (ndims1 != ndims2 &&
281 ndims1 != ndims2 - 1 &&
282 ndims1 != ndims2 + 1)
284 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
285 errmsg("cannot concatenate incompatible arrays"),
286 errdetail("Arrays of %d and %d dimensions are not "
287 "compatible for concatenation.",
290 /* get argument array details */
291 lbs1 = ARR_LBOUND(v1);
292 lbs2 = ARR_LBOUND(v2);
293 dims1 = ARR_DIMS(v1);
294 dims2 = ARR_DIMS(v2);
295 dat1 = ARR_DATA_PTR(v1);
296 dat2 = ARR_DATA_PTR(v2);
297 bitmap1 = ARR_NULLBITMAP(v1);
298 bitmap2 = ARR_NULLBITMAP(v2);
299 nitems1 = ArrayGetNItems(ndims1, dims1);
300 nitems2 = ArrayGetNItems(ndims2, dims2);
301 ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
302 ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
304 if (ndims1 == ndims2)
307 * resulting array is made up of the elements (possibly arrays
308 * themselves) of the input argument arrays
311 dims = (int *) palloc(ndims * sizeof(int));
312 lbs = (int *) palloc(ndims * sizeof(int));
314 dims[0] = dims1[0] + dims2[0];
317 for (i = 1; i < ndims; i++)
319 if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
321 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
322 errmsg("cannot concatenate incompatible arrays"),
323 errdetail("Arrays with differing element dimensions are "
324 "not compatible for concatenation.")));
330 else if (ndims1 == ndims2 - 1)
333 * resulting array has the second argument as the outer array, with
334 * the first argument inserted at the front of the outer dimension
337 dims = (int *) palloc(ndims * sizeof(int));
338 lbs = (int *) palloc(ndims * sizeof(int));
339 memcpy(dims, dims2, ndims * sizeof(int));
340 memcpy(lbs, lbs2, ndims * sizeof(int));
342 /* increment number of elements in outer array */
345 /* make sure the added element matches our existing elements */
346 for (i = 0; i < ndims1; i++)
348 if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
350 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
351 errmsg("cannot concatenate incompatible arrays"),
352 errdetail("Arrays with differing dimensions are not "
353 "compatible for concatenation.")));
359 * (ndims1 == ndims2 + 1)
361 * resulting array has the first argument as the outer array, with the
362 * second argument appended to the end of the outer dimension
365 dims = (int *) palloc(ndims * sizeof(int));
366 lbs = (int *) palloc(ndims * sizeof(int));
367 memcpy(dims, dims1, ndims * sizeof(int));
368 memcpy(lbs, lbs1, ndims * sizeof(int));
370 /* increment number of elements in outer array */
373 /* make sure the added element matches our existing elements */
374 for (i = 0; i < ndims2; i++)
376 if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
378 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
379 errmsg("cannot concatenate incompatible arrays"),
380 errdetail("Arrays with differing dimensions are not "
381 "compatible for concatenation.")));
385 /* Do this mainly for overflow checking */
386 nitems = ArrayGetNItems(ndims, dims);
388 /* build the result array */
389 ndatabytes = ndatabytes1 + ndatabytes2;
390 if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
392 dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
393 nbytes = ndatabytes + dataoffset;
397 dataoffset = 0; /* marker for no null bitmap */
398 nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
400 result = (ArrayType *) palloc0(nbytes);
401 SET_VARSIZE(result, nbytes);
402 result->ndim = ndims;
403 result->dataoffset = dataoffset;
404 result->elemtype = element_type;
405 memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
406 memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
407 /* data area is arg1 then arg2 */
408 memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
409 memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
410 /* handle the null bitmap if needed */
411 if (ARR_HASNULL(result))
413 array_bitmap_copy(ARR_NULLBITMAP(result), 0,
416 array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
421 PG_RETURN_ARRAYTYPE_P(result);
426 * used by text_to_array() in varlena.c
429 create_singleton_array(FunctionCallInfo fcinfo,
443 ArrayMetaState *my_extra;
447 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
448 errmsg("invalid number of dimensions: %d", ndims)));
451 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
452 errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
455 dvalues[0] = element;
458 for (i = 0; i < ndims; i++)
465 * We arrange to look up info about element type only once per series of
466 * calls, assuming the element type doesn't change underneath us.
468 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
469 if (my_extra == NULL)
471 fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
472 sizeof(ArrayMetaState));
473 my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
474 my_extra->element_type = ~element_type;
477 if (my_extra->element_type != element_type)
479 /* Get info about element type */
480 get_typlenbyvalalign(element_type,
483 &my_extra->typalign);
484 my_extra->element_type = element_type;
486 typlen = my_extra->typlen;
487 typbyval = my_extra->typbyval;
488 typalign = my_extra->typalign;
490 return construct_md_array(dvalues, nulls, ndims, dims, lbs, element_type,
491 typlen, typbyval, typalign);
496 * ARRAY_AGG(anynonarray) aggregate function
499 array_agg_transfn(PG_FUNCTION_ARGS)
501 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
502 MemoryContext aggcontext;
503 ArrayBuildState *state;
506 if (arg1_typeid == InvalidOid)
508 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
509 errmsg("could not determine input data type")));
512 * Note: we do not need a run-time check about whether arg1_typeid is a
513 * valid array element type, because the parser would have verified that
514 * while resolving the input/result types of this polymorphic aggregate.
517 if (!AggCheckCallContext(fcinfo, &aggcontext))
519 /* cannot be called directly because of internal-type argument */
520 elog(ERROR, "array_agg_transfn called in non-aggregate context");
523 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
524 elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
525 state = accumArrayResult(state,
532 * The transition type for array_agg() is declared to be "internal", which
533 * is a pass-by-value type the same size as a pointer. So we can safely
534 * pass the ArrayBuildState pointer through nodeAgg.c's machinations.
536 PG_RETURN_POINTER(state);
540 array_agg_finalfn(PG_FUNCTION_ARGS)
543 ArrayBuildState *state;
547 /* cannot be called directly because of internal-type argument */
548 Assert(AggCheckCallContext(fcinfo, NULL));
550 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
553 PG_RETURN_NULL(); /* returns null iff no input values */
555 dims[0] = state->nelems;
559 * Make the result. We cannot release the ArrayBuildState because
560 * sometimes aggregate final functions are re-executed. Rather, it is
561 * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
564 result = makeMdArrayResult(state, 1, dims, lbs,
565 CurrentMemoryContext,
568 PG_RETURN_DATUM(result);
572 * ARRAY_AGG(anyarray) aggregate function
575 array_agg_array_transfn(PG_FUNCTION_ARGS)
577 Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
578 MemoryContext aggcontext;
579 ArrayBuildStateArr *state;
581 if (arg1_typeid == InvalidOid)
583 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
584 errmsg("could not determine input data type")));
587 * Note: we do not need a run-time check about whether arg1_typeid is a
588 * valid array type, because the parser would have verified that while
589 * resolving the input/result types of this polymorphic aggregate.
592 if (!AggCheckCallContext(fcinfo, &aggcontext))
594 /* cannot be called directly because of internal-type argument */
595 elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
598 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
599 state = accumArrayResultArr(state,
606 * The transition type for array_agg() is declared to be "internal", which
607 * is a pass-by-value type the same size as a pointer. So we can safely
608 * pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
610 PG_RETURN_POINTER(state);
614 array_agg_array_finalfn(PG_FUNCTION_ARGS)
617 ArrayBuildStateArr *state;
619 /* cannot be called directly because of internal-type argument */
620 Assert(AggCheckCallContext(fcinfo, NULL));
622 state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
625 PG_RETURN_NULL(); /* returns null iff no input values */
628 * Make the result. We cannot release the ArrayBuildStateArr because
629 * sometimes aggregate final functions are re-executed. Rather, it is
630 * nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
633 result = makeArrayResultArr(state, CurrentMemoryContext, false);
635 PG_RETURN_DATUM(result);