2003-04-08 23:20:04 +00:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* array_userfuncs.c
|
|
|
|
* Misc user-visible array support functions
|
|
|
|
*
|
2015-01-06 11:43:47 -05:00
|
|
|
* Copyright (c) 2003-2015, PostgreSQL Global Development Group
|
2003-04-08 23:20:04 +00:00
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/utils/adt/array_userfuncs.c
|
2003-04-08 23:20:04 +00:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
|
|
|
#include "utils/array.h"
|
2003-07-27 04:53:12 +00:00
|
|
|
#include "utils/builtins.h"
|
2003-04-08 23:20:04 +00:00
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
2005-11-17 22:14:56 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
/*
|
|
|
|
* fetch_array_arg_replace_nulls
|
|
|
|
*
|
|
|
|
* Fetch an array-valued argument; if it's null, construct an empty array
|
|
|
|
* value of the proper data type. Also cache basic element type information
|
|
|
|
* in fn_extra.
|
|
|
|
*/
|
|
|
|
static ArrayType *
|
|
|
|
fetch_array_arg_replace_nulls(FunctionCallInfo fcinfo, int argno)
|
|
|
|
{
|
|
|
|
ArrayType *v;
|
|
|
|
ArrayMetaState *my_extra;
|
|
|
|
|
|
|
|
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
|
|
|
|
if (my_extra == NULL)
|
|
|
|
{
|
|
|
|
/* First time through, so look up the array type and element type */
|
|
|
|
Oid arr_typeid = get_fn_expr_argtype(fcinfo->flinfo, argno);
|
|
|
|
Oid element_type;
|
|
|
|
|
|
|
|
if (!OidIsValid(arr_typeid))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("could not determine input data type")));
|
|
|
|
element_type = get_element_type(arr_typeid);
|
|
|
|
if (!OidIsValid(element_type))
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("input data type is not an array")));
|
|
|
|
|
|
|
|
my_extra = (ArrayMetaState *)
|
|
|
|
MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
|
|
|
|
sizeof(ArrayMetaState));
|
|
|
|
my_extra->element_type = element_type;
|
|
|
|
|
|
|
|
/* Cache info about element type */
|
|
|
|
get_typlenbyvalalign(element_type,
|
|
|
|
&my_extra->typlen,
|
|
|
|
&my_extra->typbyval,
|
|
|
|
&my_extra->typalign);
|
|
|
|
|
|
|
|
fcinfo->flinfo->fn_extra = my_extra;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now we can collect the array value */
|
|
|
|
if (PG_ARGISNULL(argno))
|
|
|
|
v = construct_empty_array(my_extra->element_type);
|
|
|
|
else
|
|
|
|
v = PG_GETARG_ARRAYTYPE_P(argno);
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
2003-04-08 23:20:04 +00:00
|
|
|
/*-----------------------------------------------------------------------------
|
2015-02-18 20:53:14 -05:00
|
|
|
* array_append :
|
|
|
|
* push an element onto the end of a one-dimensional array
|
2003-04-08 23:20:04 +00:00
|
|
|
*----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Datum
|
2015-02-18 20:53:14 -05:00
|
|
|
array_append(PG_FUNCTION_ARGS)
|
2003-04-08 23:20:04 +00:00
|
|
|
{
|
|
|
|
ArrayType *v;
|
|
|
|
Datum newelem;
|
2005-11-17 22:14:56 +00:00
|
|
|
bool isNull;
|
2015-02-18 20:53:14 -05:00
|
|
|
ArrayType *result;
|
2003-04-08 23:20:04 +00:00
|
|
|
int *dimv,
|
|
|
|
*lb;
|
|
|
|
int indx;
|
2003-06-27 00:33:26 +00:00
|
|
|
ArrayMetaState *my_extra;
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
v = fetch_array_arg_replace_nulls(fcinfo, 0);
|
|
|
|
isNull = PG_ARGISNULL(1);
|
|
|
|
if (isNull)
|
|
|
|
newelem = (Datum) 0;
|
|
|
|
else
|
|
|
|
newelem = PG_GETARG_DATUM(1);
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
if (ARR_NDIM(v) == 1)
|
2003-04-08 23:20:04 +00:00
|
|
|
{
|
2015-02-18 20:53:14 -05:00
|
|
|
/* append newelem */
|
|
|
|
int ub;
|
|
|
|
|
|
|
|
lb = ARR_LBOUND(v);
|
|
|
|
dimv = ARR_DIMS(v);
|
|
|
|
ub = dimv[0] + lb[0] - 1;
|
|
|
|
indx = ub + 1;
|
|
|
|
|
|
|
|
/* overflow? */
|
|
|
|
if (indx < ub)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("integer out of range")));
|
2003-04-08 23:20:04 +00:00
|
|
|
}
|
2015-02-18 20:53:14 -05:00
|
|
|
else if (ARR_NDIM(v) == 0)
|
|
|
|
indx = 1;
|
2003-04-08 23:20:04 +00:00
|
|
|
else
|
2003-07-27 04:53:12 +00:00
|
|
|
ereport(ERROR,
|
2015-02-18 20:53:14 -05:00
|
|
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
|
|
|
errmsg("argument must be empty or one-dimensional array")));
|
|
|
|
|
|
|
|
/* Perform element insertion */
|
|
|
|
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
|
|
|
|
|
|
|
|
result = array_set(v, 1, &indx, newelem, isNull,
|
|
|
|
-1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
|
|
|
|
|
|
|
|
PG_RETURN_ARRAYTYPE_P(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
* array_prepend :
|
|
|
|
* push an element onto the front of a one-dimensional array
|
|
|
|
*----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
array_prepend(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
ArrayType *v;
|
|
|
|
Datum newelem;
|
|
|
|
bool isNull;
|
|
|
|
ArrayType *result;
|
2015-02-20 00:47:28 -05:00
|
|
|
int *lb;
|
2015-02-18 20:53:14 -05:00
|
|
|
int indx;
|
|
|
|
ArrayMetaState *my_extra;
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
isNull = PG_ARGISNULL(0);
|
|
|
|
if (isNull)
|
|
|
|
newelem = (Datum) 0;
|
|
|
|
else
|
|
|
|
newelem = PG_GETARG_DATUM(0);
|
|
|
|
v = fetch_array_arg_replace_nulls(fcinfo, 1);
|
2005-11-17 22:14:56 +00:00
|
|
|
|
2003-06-27 00:33:26 +00:00
|
|
|
if (ARR_NDIM(v) == 1)
|
2003-06-24 23:14:49 +00:00
|
|
|
{
|
2015-02-18 20:53:14 -05:00
|
|
|
/* prepend newelem */
|
2003-06-27 00:33:26 +00:00
|
|
|
lb = ARR_LBOUND(v);
|
2015-02-18 20:53:14 -05:00
|
|
|
indx = lb[0] - 1;
|
2003-06-27 00:33:26 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
/* overflow? */
|
|
|
|
if (indx > lb[0])
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
|
|
|
|
errmsg("integer out of range")));
|
2003-06-24 23:14:49 +00:00
|
|
|
}
|
2003-06-27 00:33:26 +00:00
|
|
|
else if (ARR_NDIM(v) == 0)
|
|
|
|
indx = 1;
|
2003-06-24 23:14:49 +00:00
|
|
|
else
|
2003-07-27 04:53:12 +00:00
|
|
|
ereport(ERROR,
|
2003-09-15 20:03:37 +00:00
|
|
|
(errcode(ERRCODE_DATA_EXCEPTION),
|
2005-10-15 02:49:52 +00:00
|
|
|
errmsg("argument must be empty or one-dimensional array")));
|
2003-06-27 00:33:26 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
/* Perform element insertion */
|
2003-06-27 00:33:26 +00:00
|
|
|
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
|
2003-06-25 21:30:34 +00:00
|
|
|
|
2005-11-17 22:14:56 +00:00
|
|
|
result = array_set(v, 1, &indx, newelem, isNull,
|
2015-02-18 20:53:14 -05:00
|
|
|
-1, my_extra->typlen, my_extra->typbyval, my_extra->typalign);
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2015-02-18 20:53:14 -05:00
|
|
|
/* Readjust result's LB to match the input's, as expected for prepend */
|
2005-11-19 01:50:08 +00:00
|
|
|
if (ARR_NDIM(v) == 1)
|
|
|
|
ARR_LBOUND(result)[0] = ARR_LBOUND(v)[0];
|
|
|
|
|
2003-04-08 23:20:04 +00:00
|
|
|
PG_RETURN_ARRAYTYPE_P(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*-----------------------------------------------------------------------------
|
|
|
|
* array_cat :
|
2003-08-17 23:43:27 +00:00
|
|
|
* concatenate two nD arrays to form an nD array, or
|
2003-04-08 23:20:04 +00:00
|
|
|
* push an (n-1)D array onto the end of an nD array
|
|
|
|
*----------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
array_cat(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2003-08-04 00:43:34 +00:00
|
|
|
ArrayType *v1,
|
|
|
|
*v2;
|
2005-11-17 22:14:56 +00:00
|
|
|
ArrayType *result;
|
2003-08-04 00:43:34 +00:00
|
|
|
int *dims,
|
|
|
|
*lbs,
|
|
|
|
ndims,
|
2005-11-17 22:14:56 +00:00
|
|
|
nitems,
|
2003-08-04 00:43:34 +00:00
|
|
|
ndatabytes,
|
|
|
|
nbytes;
|
|
|
|
int *dims1,
|
|
|
|
*lbs1,
|
|
|
|
ndims1,
|
2005-11-17 22:14:56 +00:00
|
|
|
nitems1,
|
2003-08-04 00:43:34 +00:00
|
|
|
ndatabytes1;
|
|
|
|
int *dims2,
|
|
|
|
*lbs2,
|
|
|
|
ndims2,
|
2005-11-17 22:14:56 +00:00
|
|
|
nitems2,
|
2003-08-04 00:43:34 +00:00
|
|
|
ndatabytes2;
|
2003-08-17 23:43:27 +00:00
|
|
|
int i;
|
2003-08-04 00:43:34 +00:00
|
|
|
char *dat1,
|
|
|
|
*dat2;
|
2005-11-17 22:14:56 +00:00
|
|
|
bits8 *bitmap1,
|
|
|
|
*bitmap2;
|
2003-04-08 23:20:04 +00:00
|
|
|
Oid element_type;
|
|
|
|
Oid element_type1;
|
|
|
|
Oid element_type2;
|
2005-11-17 22:14:56 +00:00
|
|
|
int32 dataoffset;
|
|
|
|
|
|
|
|
/* Concatenating a null array is a no-op, just return the other input */
|
|
|
|
if (PG_ARGISNULL(0))
|
|
|
|
{
|
|
|
|
if (PG_ARGISNULL(1))
|
|
|
|
PG_RETURN_NULL();
|
|
|
|
result = PG_GETARG_ARRAYTYPE_P(1);
|
|
|
|
PG_RETURN_ARRAYTYPE_P(result);
|
|
|
|
}
|
|
|
|
if (PG_ARGISNULL(1))
|
|
|
|
{
|
|
|
|
result = PG_GETARG_ARRAYTYPE_P(0);
|
|
|
|
PG_RETURN_ARRAYTYPE_P(result);
|
|
|
|
}
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
v1 = PG_GETARG_ARRAYTYPE_P(0);
|
|
|
|
v2 = PG_GETARG_ARRAYTYPE_P(1);
|
|
|
|
|
2004-12-17 20:59:58 +00:00
|
|
|
element_type1 = ARR_ELEMTYPE(v1);
|
|
|
|
element_type2 = ARR_ELEMTYPE(v2);
|
|
|
|
|
|
|
|
/* Check we have matching element types */
|
|
|
|
if (element_type1 != element_type2)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_DATATYPE_MISMATCH),
|
|
|
|
errmsg("cannot concatenate incompatible arrays"),
|
|
|
|
errdetail("Arrays with element types %s and %s are not "
|
|
|
|
"compatible for concatenation.",
|
|
|
|
format_type_be(element_type1),
|
|
|
|
format_type_be(element_type2))));
|
|
|
|
|
|
|
|
/* OK, use it */
|
|
|
|
element_type = element_type1;
|
|
|
|
|
2003-08-17 23:43:27 +00:00
|
|
|
/*----------
|
|
|
|
* We must have one of the following combinations of inputs:
|
|
|
|
* 1) one empty array, and one non-empty array
|
|
|
|
* 2) both arrays empty
|
|
|
|
* 3) two arrays with ndims1 == ndims2
|
|
|
|
* 4) ndims1 == ndims2 - 1
|
|
|
|
* 5) ndims1 == ndims2 + 1
|
|
|
|
*----------
|
2003-04-08 23:20:04 +00:00
|
|
|
*/
|
|
|
|
ndims1 = ARR_NDIM(v1);
|
|
|
|
ndims2 = ARR_NDIM(v2);
|
|
|
|
|
2003-06-27 00:33:26 +00:00
|
|
|
/*
|
2005-10-15 02:49:52 +00:00
|
|
|
* short circuit - if one input array is empty, and the other is not, we
|
|
|
|
* return the non-empty one as the result
|
2003-06-27 00:33:26 +00:00
|
|
|
*
|
|
|
|
* if both are empty, return the first one
|
|
|
|
*/
|
|
|
|
if (ndims1 == 0 && ndims2 > 0)
|
|
|
|
PG_RETURN_ARRAYTYPE_P(v2);
|
|
|
|
|
|
|
|
if (ndims2 == 0)
|
|
|
|
PG_RETURN_ARRAYTYPE_P(v1);
|
|
|
|
|
2003-08-17 23:43:27 +00:00
|
|
|
/* the rest fall under rule 3, 4, or 5 */
|
|
|
|
if (ndims1 != ndims2 &&
|
|
|
|
ndims1 != ndims2 - 1 &&
|
|
|
|
ndims1 != ndims2 + 1)
|
2003-07-27 04:53:12 +00:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("cannot concatenate incompatible arrays"),
|
|
|
|
errdetail("Arrays of %d and %d dimensions are not "
|
2003-08-04 00:43:34 +00:00
|
|
|
"compatible for concatenation.",
|
|
|
|
ndims1, ndims2)));
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
/* get argument array details */
|
|
|
|
lbs1 = ARR_LBOUND(v1);
|
|
|
|
lbs2 = ARR_LBOUND(v2);
|
|
|
|
dims1 = ARR_DIMS(v1);
|
|
|
|
dims2 = ARR_DIMS(v2);
|
|
|
|
dat1 = ARR_DATA_PTR(v1);
|
|
|
|
dat2 = ARR_DATA_PTR(v2);
|
2005-11-17 22:14:56 +00:00
|
|
|
bitmap1 = ARR_NULLBITMAP(v1);
|
|
|
|
bitmap2 = ARR_NULLBITMAP(v2);
|
|
|
|
nitems1 = ArrayGetNItems(ndims1, dims1);
|
|
|
|
nitems2 = ArrayGetNItems(ndims2, dims2);
|
|
|
|
ndatabytes1 = ARR_SIZE(v1) - ARR_DATA_OFFSET(v1);
|
|
|
|
ndatabytes2 = ARR_SIZE(v2) - ARR_DATA_OFFSET(v2);
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
if (ndims1 == ndims2)
|
|
|
|
{
|
|
|
|
/*
|
2003-08-17 23:43:27 +00:00
|
|
|
* resulting array is made up of the elements (possibly arrays
|
|
|
|
* themselves) of the input argument arrays
|
2003-04-08 23:20:04 +00:00
|
|
|
*/
|
2003-08-17 23:43:27 +00:00
|
|
|
ndims = ndims1;
|
2003-04-08 23:20:04 +00:00
|
|
|
dims = (int *) palloc(ndims * sizeof(int));
|
|
|
|
lbs = (int *) palloc(ndims * sizeof(int));
|
|
|
|
|
2003-08-17 23:43:27 +00:00
|
|
|
dims[0] = dims1[0] + dims2[0];
|
|
|
|
lbs[0] = lbs1[0];
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2003-08-17 23:43:27 +00:00
|
|
|
for (i = 1; i < ndims; i++)
|
2003-04-08 23:20:04 +00:00
|
|
|
{
|
|
|
|
if (dims1[i] != dims2[i] || lbs1[i] != lbs2[i])
|
2003-07-27 04:53:12 +00:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("cannot concatenate incompatible arrays"),
|
2005-10-15 02:49:52 +00:00
|
|
|
errdetail("Arrays with differing element dimensions are "
|
|
|
|
"not compatible for concatenation.")));
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2003-08-17 23:43:27 +00:00
|
|
|
dims[i] = dims1[i];
|
|
|
|
lbs[i] = lbs1[i];
|
2003-04-08 23:20:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (ndims1 == ndims2 - 1)
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 02:49:52 +00:00
|
|
|
* resulting array has the second argument as the outer array, with
|
2005-11-19 01:50:08 +00:00
|
|
|
* the first argument inserted at the front of the outer dimension
|
2003-04-08 23:20:04 +00:00
|
|
|
*/
|
|
|
|
ndims = ndims2;
|
2003-08-17 23:43:27 +00:00
|
|
|
dims = (int *) palloc(ndims * sizeof(int));
|
|
|
|
lbs = (int *) palloc(ndims * sizeof(int));
|
|
|
|
memcpy(dims, dims2, ndims * sizeof(int));
|
|
|
|
memcpy(lbs, lbs2, ndims * sizeof(int));
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
/* increment number of elements in outer array */
|
|
|
|
dims[0] += 1;
|
|
|
|
|
|
|
|
/* make sure the added element matches our existing elements */
|
|
|
|
for (i = 0; i < ndims1; i++)
|
|
|
|
{
|
|
|
|
if (dims1[i] != dims[i + 1] || lbs1[i] != lbs[i + 1])
|
2003-07-27 04:53:12 +00:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("cannot concatenate incompatible arrays"),
|
2005-10-15 02:49:52 +00:00
|
|
|
errdetail("Arrays with differing dimensions are not "
|
|
|
|
"compatible for concatenation.")));
|
2003-04-08 23:20:04 +00:00
|
|
|
}
|
|
|
|
}
|
2003-08-04 00:43:34 +00:00
|
|
|
else
|
2003-04-08 23:20:04 +00:00
|
|
|
{
|
|
|
|
/*
|
2003-08-17 23:43:27 +00:00
|
|
|
* (ndims1 == ndims2 + 1)
|
|
|
|
*
|
2005-10-15 02:49:52 +00:00
|
|
|
* resulting array has the first argument as the outer array, with the
|
|
|
|
* second argument appended to the end of the outer dimension
|
2003-04-08 23:20:04 +00:00
|
|
|
*/
|
|
|
|
ndims = ndims1;
|
2003-08-17 23:43:27 +00:00
|
|
|
dims = (int *) palloc(ndims * sizeof(int));
|
|
|
|
lbs = (int *) palloc(ndims * sizeof(int));
|
|
|
|
memcpy(dims, dims1, ndims * sizeof(int));
|
|
|
|
memcpy(lbs, lbs1, ndims * sizeof(int));
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
/* increment number of elements in outer array */
|
|
|
|
dims[0] += 1;
|
|
|
|
|
|
|
|
/* make sure the added element matches our existing elements */
|
|
|
|
for (i = 0; i < ndims2; i++)
|
|
|
|
{
|
|
|
|
if (dims2[i] != dims[i + 1] || lbs2[i] != lbs[i + 1])
|
2003-07-27 04:53:12 +00:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
|
|
errmsg("cannot concatenate incompatible arrays"),
|
2005-10-15 02:49:52 +00:00
|
|
|
errdetail("Arrays with differing dimensions are not "
|
|
|
|
"compatible for concatenation.")));
|
2003-04-08 23:20:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-11-17 22:14:56 +00:00
|
|
|
/* Do this mainly for overflow checking */
|
|
|
|
nitems = ArrayGetNItems(ndims, dims);
|
|
|
|
|
2003-04-08 23:20:04 +00:00
|
|
|
/* build the result array */
|
|
|
|
ndatabytes = ndatabytes1 + ndatabytes2;
|
2005-11-17 22:14:56 +00:00
|
|
|
if (ARR_HASNULL(v1) || ARR_HASNULL(v2))
|
|
|
|
{
|
|
|
|
dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
|
|
|
|
nbytes = ndatabytes + dataoffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dataoffset = 0; /* marker for no null bitmap */
|
|
|
|
nbytes = ndatabytes + ARR_OVERHEAD_NONULLS(ndims);
|
|
|
|
}
|
Fix array- and path-creating functions to ensure padding bytes are zeroes.
Per recent discussion, it's important for all computed datums (not only the
results of input functions) to not contain any ill-defined (uninitialized)
bits. Failing to ensure that can result in equal() reporting that
semantically indistinguishable Consts are not equal, which in turn leads to
bizarre and undesirable planner behavior, such as in a recent example from
David Johnston. We might eventually try to fix this in a general manner by
allowing datatypes to define identity-testing functions, but for now the
path of least resistance is to expect datatypes to force all unused bits
into consistent states.
Per some testing by Noah Misch, array and path functions seem to be the
only ones presenting risks at the moment, so I looked through all the
functions in adt/array*.c and geo_ops.c and fixed them as necessary. In
the array functions, the easiest/safest fix is to allocate result arrays
with palloc0 instead of palloc. Possibly in future someone will want to
look into whether we can just zero the padding bytes, but that looks too
complex for a back-patchable fix. In the path functions, we already had a
precedent in path_in for just zeroing the one known pad field, so duplicate
that code as needed.
Back-patch to all supported branches.
2011-04-27 13:58:36 -04:00
|
|
|
result = (ArrayType *) palloc0(nbytes);
|
2007-02-27 23:48:10 +00:00
|
|
|
SET_VARSIZE(result, nbytes);
|
2003-04-08 23:20:04 +00:00
|
|
|
result->ndim = ndims;
|
2005-11-17 22:14:56 +00:00
|
|
|
result->dataoffset = dataoffset;
|
2003-04-08 23:20:04 +00:00
|
|
|
result->elemtype = element_type;
|
|
|
|
memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
|
|
|
|
memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
|
|
|
|
/* data area is arg1 then arg2 */
|
|
|
|
memcpy(ARR_DATA_PTR(result), dat1, ndatabytes1);
|
|
|
|
memcpy(ARR_DATA_PTR(result) + ndatabytes1, dat2, ndatabytes2);
|
2005-11-17 22:14:56 +00:00
|
|
|
/* handle the null bitmap if needed */
|
|
|
|
if (ARR_HASNULL(result))
|
|
|
|
{
|
|
|
|
array_bitmap_copy(ARR_NULLBITMAP(result), 0,
|
|
|
|
bitmap1, 0,
|
|
|
|
nitems1);
|
|
|
|
array_bitmap_copy(ARR_NULLBITMAP(result), nitems1,
|
|
|
|
bitmap2, 0,
|
|
|
|
nitems2);
|
|
|
|
}
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
PG_RETURN_ARRAYTYPE_P(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2003-06-27 00:33:26 +00:00
|
|
|
* used by text_to_array() in varlena.c
|
2003-04-08 23:20:04 +00:00
|
|
|
*/
|
|
|
|
ArrayType *
|
2003-06-27 00:33:26 +00:00
|
|
|
create_singleton_array(FunctionCallInfo fcinfo,
|
|
|
|
Oid element_type,
|
|
|
|
Datum element,
|
2010-08-10 21:51:00 +00:00
|
|
|
bool isNull,
|
2003-06-27 00:33:26 +00:00
|
|
|
int ndims)
|
2003-04-08 23:20:04 +00:00
|
|
|
{
|
2003-08-04 00:43:34 +00:00
|
|
|
Datum dvalues[1];
|
2010-08-10 21:51:00 +00:00
|
|
|
bool nulls[1];
|
2003-08-04 00:43:34 +00:00
|
|
|
int16 typlen;
|
|
|
|
bool typbyval;
|
|
|
|
char typalign;
|
|
|
|
int dims[MAXDIM];
|
|
|
|
int lbs[MAXDIM];
|
|
|
|
int i;
|
2003-06-27 00:33:26 +00:00
|
|
|
ArrayMetaState *my_extra;
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2003-07-27 04:53:12 +00:00
|
|
|
if (ndims < 1)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("invalid number of dimensions: %d", ndims)));
|
|
|
|
if (ndims > MAXDIM)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
|
2003-09-25 06:58:07 +00:00
|
|
|
errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
|
|
|
|
ndims, MAXDIM)));
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
dvalues[0] = element;
|
2010-08-10 21:51:00 +00:00
|
|
|
nulls[0] = isNull;
|
2003-04-08 23:20:04 +00:00
|
|
|
|
|
|
|
for (i = 0; i < ndims; i++)
|
|
|
|
{
|
|
|
|
dims[i] = 1;
|
|
|
|
lbs[i] = 1;
|
|
|
|
}
|
|
|
|
|
2003-06-27 00:33:26 +00:00
|
|
|
/*
|
2005-10-15 02:49:52 +00:00
|
|
|
* We arrange to look up info about element type only once per series of
|
|
|
|
* calls, assuming the element type doesn't change underneath us.
|
2003-06-27 00:33:26 +00:00
|
|
|
*/
|
|
|
|
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
|
|
|
|
if (my_extra == NULL)
|
|
|
|
{
|
|
|
|
fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
|
2005-10-15 02:49:52 +00:00
|
|
|
sizeof(ArrayMetaState));
|
2003-06-27 00:33:26 +00:00
|
|
|
my_extra = (ArrayMetaState *) fcinfo->flinfo->fn_extra;
|
2005-11-17 22:14:56 +00:00
|
|
|
my_extra->element_type = ~element_type;
|
2003-06-27 00:33:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (my_extra->element_type != element_type)
|
|
|
|
{
|
|
|
|
/* Get info about element type */
|
|
|
|
get_typlenbyvalalign(element_type,
|
|
|
|
&my_extra->typlen,
|
|
|
|
&my_extra->typbyval,
|
|
|
|
&my_extra->typalign);
|
|
|
|
my_extra->element_type = element_type;
|
|
|
|
}
|
|
|
|
typlen = my_extra->typlen;
|
|
|
|
typbyval = my_extra->typbyval;
|
|
|
|
typalign = my_extra->typalign;
|
2003-04-08 23:20:04 +00:00
|
|
|
|
2010-08-10 21:51:00 +00:00
|
|
|
return construct_md_array(dvalues, nulls, ndims, dims, lbs, element_type,
|
2003-04-08 23:20:04 +00:00
|
|
|
typlen, typbyval, typalign);
|
|
|
|
}
|
2008-11-13 15:59:51 +00:00
|
|
|
|
2008-11-14 00:12:08 +00:00
|
|
|
|
|
|
|
/*
|
2014-11-25 12:21:22 -05:00
|
|
|
* ARRAY_AGG(anynonarray) aggregate function
|
2008-11-14 00:12:08 +00:00
|
|
|
*/
|
2008-11-13 15:59:51 +00:00
|
|
|
Datum
|
|
|
|
array_agg_transfn(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2009-06-11 14:49:15 +00:00
|
|
|
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
2008-12-28 18:54:01 +00:00
|
|
|
MemoryContext aggcontext;
|
2008-11-14 00:12:08 +00:00
|
|
|
ArrayBuildState *state;
|
|
|
|
Datum elem;
|
2008-11-13 15:59:51 +00:00
|
|
|
|
|
|
|
if (arg1_typeid == InvalidOid)
|
2008-11-14 00:12:08 +00:00
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("could not determine input data type")));
|
2008-11-13 15:59:51 +00:00
|
|
|
|
2014-11-25 12:21:22 -05:00
|
|
|
/*
|
|
|
|
* Note: we do not need a run-time check about whether arg1_typeid is a
|
|
|
|
* valid array element type, because the parser would have verified that
|
|
|
|
* while resolving the input/result types of this polymorphic aggregate.
|
|
|
|
*/
|
|
|
|
|
2010-02-08 20:39:52 +00:00
|
|
|
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
2008-12-28 18:54:01 +00:00
|
|
|
{
|
|
|
|
/* cannot be called directly because of internal-type argument */
|
|
|
|
elog(ERROR, "array_agg_transfn called in non-aggregate context");
|
|
|
|
}
|
2008-11-13 15:59:51 +00:00
|
|
|
|
2008-11-14 00:12:08 +00:00
|
|
|
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
|
|
|
|
elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
|
|
|
|
state = accumArrayResult(state,
|
|
|
|
elem,
|
|
|
|
PG_ARGISNULL(1),
|
|
|
|
arg1_typeid,
|
2008-12-28 18:54:01 +00:00
|
|
|
aggcontext);
|
2008-11-14 00:12:08 +00:00
|
|
|
|
|
|
|
/*
|
2009-06-11 14:49:15 +00:00
|
|
|
* The transition type for array_agg() is declared to be "internal", which
|
2014-05-06 12:12:18 -04:00
|
|
|
* is a pass-by-value type the same size as a pointer. So we can safely
|
2009-06-11 14:49:15 +00:00
|
|
|
* pass the ArrayBuildState pointer through nodeAgg.c's machinations.
|
2008-11-14 00:12:08 +00:00
|
|
|
*/
|
|
|
|
PG_RETURN_POINTER(state);
|
2008-11-13 15:59:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
array_agg_finalfn(PG_FUNCTION_ARGS)
|
|
|
|
{
|
2008-12-28 18:54:01 +00:00
|
|
|
Datum result;
|
2008-11-14 00:12:08 +00:00
|
|
|
ArrayBuildState *state;
|
2008-12-28 18:54:01 +00:00
|
|
|
int dims[1];
|
|
|
|
int lbs[1];
|
2008-11-14 00:12:08 +00:00
|
|
|
|
2008-11-13 15:59:51 +00:00
|
|
|
/* cannot be called directly because of internal-type argument */
|
2010-02-08 20:39:52 +00:00
|
|
|
Assert(AggCheckCallContext(fcinfo, NULL));
|
2008-11-13 15:59:51 +00:00
|
|
|
|
2014-11-25 12:21:22 -05:00
|
|
|
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
if (state == NULL)
|
|
|
|
PG_RETURN_NULL(); /* returns null iff no input values */
|
2008-12-28 18:54:01 +00:00
|
|
|
|
|
|
|
dims[0] = state->nelems;
|
|
|
|
lbs[0] = 1;
|
|
|
|
|
2009-06-20 18:45:28 +00:00
|
|
|
/*
|
|
|
|
* Make the result. We cannot release the ArrayBuildState because
|
2010-02-26 02:01:40 +00:00
|
|
|
* sometimes aggregate final functions are re-executed. Rather, it is
|
|
|
|
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
|
|
|
|
* so.
|
2009-06-20 18:45:28 +00:00
|
|
|
*/
|
2008-12-28 18:54:01 +00:00
|
|
|
result = makeMdArrayResult(state, 1, dims, lbs,
|
|
|
|
CurrentMemoryContext,
|
2009-06-20 18:45:28 +00:00
|
|
|
false);
|
2008-12-28 18:54:01 +00:00
|
|
|
|
|
|
|
PG_RETURN_DATUM(result);
|
2008-11-13 15:59:51 +00:00
|
|
|
}
|
2014-11-25 12:21:22 -05:00
|
|
|
|
|
|
|
/*
|
|
|
|
* ARRAY_AGG(anyarray) aggregate function
|
|
|
|
*/
|
|
|
|
Datum
|
|
|
|
array_agg_array_transfn(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
|
|
|
|
MemoryContext aggcontext;
|
|
|
|
ArrayBuildStateArr *state;
|
|
|
|
|
|
|
|
if (arg1_typeid == InvalidOid)
|
|
|
|
ereport(ERROR,
|
|
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
|
|
errmsg("could not determine input data type")));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Note: we do not need a run-time check about whether arg1_typeid is a
|
|
|
|
* valid array type, because the parser would have verified that while
|
|
|
|
* resolving the input/result types of this polymorphic aggregate.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!AggCheckCallContext(fcinfo, &aggcontext))
|
|
|
|
{
|
|
|
|
/* cannot be called directly because of internal-type argument */
|
|
|
|
elog(ERROR, "array_agg_array_transfn called in non-aggregate context");
|
|
|
|
}
|
|
|
|
|
|
|
|
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
|
|
|
|
state = accumArrayResultArr(state,
|
|
|
|
PG_GETARG_DATUM(1),
|
|
|
|
PG_ARGISNULL(1),
|
|
|
|
arg1_typeid,
|
|
|
|
aggcontext);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The transition type for array_agg() is declared to be "internal", which
|
|
|
|
* is a pass-by-value type the same size as a pointer. So we can safely
|
|
|
|
* pass the ArrayBuildStateArr pointer through nodeAgg.c's machinations.
|
|
|
|
*/
|
|
|
|
PG_RETURN_POINTER(state);
|
|
|
|
}
|
|
|
|
|
|
|
|
Datum
|
|
|
|
array_agg_array_finalfn(PG_FUNCTION_ARGS)
|
|
|
|
{
|
|
|
|
Datum result;
|
|
|
|
ArrayBuildStateArr *state;
|
|
|
|
|
|
|
|
/* cannot be called directly because of internal-type argument */
|
|
|
|
Assert(AggCheckCallContext(fcinfo, NULL));
|
|
|
|
|
|
|
|
state = PG_ARGISNULL(0) ? NULL : (ArrayBuildStateArr *) PG_GETARG_POINTER(0);
|
|
|
|
|
|
|
|
if (state == NULL)
|
|
|
|
PG_RETURN_NULL(); /* returns null iff no input values */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Make the result. We cannot release the ArrayBuildStateArr because
|
|
|
|
* sometimes aggregate final functions are re-executed. Rather, it is
|
|
|
|
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
|
|
|
|
* so.
|
|
|
|
*/
|
|
|
|
result = makeArrayResultArr(state, CurrentMemoryContext, false);
|
|
|
|
|
|
|
|
PG_RETURN_DATUM(result);
|
|
|
|
}
|