>
> So, is there any idea how we will handle NULL and empty array in
> array_agg(anyarray)?
> I propose we just reject those input because the output will make no sense:
> - array_agg(NULL::int[]) --> the result will be indistinguished from
> array_agg of NULL ints.
> - array_agg('{}'::int[]) --> how we determine the dimension of the result?
> is it 0? Or the result will be just an empty array {} ?
>
This updated patch rejects NULL and {} arrays as noted above.
Regards,
--
Ali Akbar
*** a/src/backend/utils/adt/array_userfuncs.c
--- b/src/backend/utils/adt/array_userfuncs.c
***************
*** 16,21 ****
--- 16,51 ----
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+ #include "utils/memutils.h"
+
+ /*-------------------------------------------------------------------------
+ * ArrayAggAnyArrayState:
+ * aggregate state for array_agg(anyarray)
+ *-------------------------------------------------------------------------
+ */
+ typedef struct
+ {
+ MemoryContext mcontext; /* where all the temp stuff is kept */
+ char *data; /* array of accumulated data */
+ bits8 *nullbitmap; /* bitmap of is-null flags for data */
+
+ int abytes; /* allocated length of above arrays */
+ int aitems; /* allocated length of above arrays */
+ int nbytes; /* number of used bytes in above arrays */
+ int nitems; /* number of elements in above arrays */
+ int narray; /* number of array accumulated */
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+
+ int ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+ } ArrayAggAnyArrayState;
+
/*-----------------------------------------------------------------------------
* array_push :
***************
*** 544,546 **** array_agg_finalfn(PG_FUNCTION_ARGS)
--- 574,821 ----
PG_RETURN_DATUM(result);
}
+
+ /*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+ Datum
+ array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+ {
+ MemoryContext aggcontext,
+ arr_context,
+ oldcontext;
+ ArrayAggAnyArrayState *astate;
+
+ Oid arg_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid arg_elemtype = get_element_type(arg_typeid);
+ ArrayType *arg;
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+
+ int i;
+
+ if (arg_elemtype == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ elog(ERROR, "array_agg_anyarray_transfn called in non-aggregate context");
+
+ if (PG_ARGISNULL(1))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot aggregate null arrays")));
+
+ astate = PG_ARGISNULL(0) ? NULL : (ArrayAggAnyArrayState *) PG_GETARG_POINTER(0);
+
+ if (astate == NULL)
+ {
+ arr_context = AllocSetContextCreate(aggcontext,
+ "array_agg_anyarray_transfn",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(arr_context);
+ astate = (ArrayAggAnyArrayState *) palloc(sizeof(ArrayAggAnyArrayState));
+
+ astate->mcontext = arr_context;
+ astate->abytes = 0;
+ astate->aitems = 0;
+ astate->data = NULL;
+ astate->nullbitmap = NULL;
+ astate->nitems = 0;
+ astate->narray = 0;
+ astate->element_type = arg_elemtype;
+ get_typlenbyvalalign(arg_elemtype,
+ &astate->typlen,
+ &astate->typbyval,
+ &astate->typalign);
+ }
+ else
+ {
+ oldcontext = MemoryContextSwitchTo(astate->mcontext);
+ Assert(astate->element_type == arg_elemtype);
+ }
+
+ arg = PG_GETARG_ARRAYTYPE_P(1);
+
+ ndims = ARR_NDIM(arg);
+ dims = ARR_DIMS(arg);
+ lbs = ARR_LBOUND(arg);
+ data = ARR_DATA_PTR(arg);
+ nitems = ArrayGetNItems(ndims, dims);
+
+ ndatabytes = ARR_SIZE(arg) - ARR_DATA_OFFSET(arg);
+
+ if (astate->data == NULL)
+ {
+ if (ndims == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot aggregate empty arrays")));
+
+ if (ndims + 1 > MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ ndims + 1, MAXDIM)));
+
+ astate->ndims = ndims;
+ astate->dims = (int *) palloc(ndims * sizeof(int));
+ astate->lbs = (int *) palloc(ndims * sizeof(int));
+ memcpy(astate->dims, dims, ndims * sizeof(int));
+ memcpy(astate->lbs, lbs, ndims * sizeof(int));
+
+ astate->abytes = 64 * (ndatabytes == 0 ? 1 : ndatabytes);
+ astate->aitems = 64 * nitems;
+ astate->data = (char *) palloc(astate->abytes);
+
+ memcpy(astate->data, data, ndatabytes);
+ astate->nbytes = ndatabytes;
+ astate->nitems = nitems;
+
+ if (ARR_HASNULL(arg))
+ {
+ astate->hasnull = true;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ }
+ else
+ astate->hasnull = false;
+ }
+ else
+ {
+ if (astate->ndims != ndims)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for concatenation.",
+ astate->ndims, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate->dims[i] != dims[i] || astate->lbs[i] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot aggregate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+
+ if (astate->nbytes + ndatabytes >= astate->abytes)
+ {
+ astate->nbytes *= 2;
+ astate->data = (char *)
+ repalloc(astate->data, astate->nbytes);
+ }
+ if (astate->nitems + nitems >= astate->aitems)
+ {
+ astate->aitems *= 2;
+ astate->nullbitmap = (bits8 *)
+ repalloc(astate->nullbitmap, (astate->aitems + 7) / 8);
+ }
+
+ memcpy(astate->data + astate->nbytes, data, ndatabytes);
+ astate->nbytes += ndatabytes;
+
+ if (ARR_HASNULL(arg) || astate->hasnull)
+ {
+ if (!astate->hasnull)
+ {
+ astate->hasnull = true;
+ astate->nullbitmap = (bits8 *) palloc((astate->aitems + 7) / 8);
+ array_bitmap_copy(astate->nullbitmap, 0,
+ NULL, 0,
+ astate->nitems);
+ }
+ array_bitmap_copy(astate->nullbitmap, astate->nitems,
+ ARR_NULLBITMAP(arg), 0,
+ nitems);
+ astate->nitems += nitems;
+ }
+ }
+ astate->narray += 1;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /*
+ * 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 pointer through nodeAgg.c's machinations.
+ */
+ PG_RETURN_POINTER(astate);
+ }
+
+ Datum
+ array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+ {
+ MemoryContext oldcontext;
+
+ ArrayType *result;
+ ArrayAggAnyArrayState *astate;
+
+ int dataoffset,
+ nbytes,
+ ndims;
+
+ /*
+ * Test for null before Asserting we are in right context. This is to
+ * avoid possible Assert failure in 8.4beta installations, where it is
+ * possible for users to create NULL constants of type internal.
+ */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL(); /* returns null iff no input values */
+
+ /* cannot be called directly because of internal-type argument */
+ Assert(AggCheckCallContext(fcinfo, NULL));
+
+ oldcontext = MemoryContextSwitchTo(CurrentMemoryContext);
+
+ astate = (ArrayAggAnyArrayState *) PG_GETARG_POINTER(0);
+
+ ndims = astate->ndims + 1;
+ nbytes = astate->nbytes;
+ /* compute required space */
+ if (astate->hasnull)
+ {
+ dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, astate->nitems);
+ nbytes += dataoffset;
+ }
+ else
+ {
+ dataoffset = 0;
+ nbytes += ARR_OVERHEAD_NONULLS(ndims);
+ }
+
+ result = (ArrayType *) palloc0(nbytes);
+ SET_VARSIZE(result, nbytes);
+ result->ndim = astate->ndims + 1;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->element_type;
+
+ ARR_DIMS(result)[0] = astate->narray;
+ ARR_LBOUND(result)[0] = 1;
+ memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+ memcpy(&(ARR_LBOUND(result)[1]), astate->lbs, (ndims - 1) * sizeof(int));
+
+ memcpy(ARR_DATA_PTR(result), astate->data, astate->nbytes);
+ if (astate->hasnull)
+ array_bitmap_copy(ARR_NULLBITMAP(result), 0,
+ astate->nullbitmap, 0,
+ astate->nitems);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ /* we cannot release ArrayAggAnyArrayState because sometimes
+ * aggregate final functions are re-executed. Rather, it is nodeAgg.c's
+ * responsibility to reset aggcontext when it's safe to do so
+ */
+
+ PG_RETURN_DATUM(PointerGetDatum(result));
+ }
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
***************
*** 275,280 **** DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_
--- 275,281 ----
/* array */
DATA(insert ( 2335 n 0 array_agg_transfn array_agg_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
+ DATA(insert ( 6005 n 0 array_agg_anyarray_transfn array_agg_anyarray_finalfn - - - t f 0 2281 0 0 0 _null_ _null_ ));
/* text */
DATA(insert ( 3538 n 0 string_agg_transfn string_agg_finalfn - - - f f 0 2281 0 0 0 _null_ _null_ ));
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
***************
*** 879,889 **** DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
--- 879,895 ----
DESCR("remove any occurrences of an element from an array");
DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ ));
DESCR("replace any occurrences of an element in an array");
! DATA(insert OID = 2333 ( array_agg_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2776" _null_ _null_ _null_ _null_ array_agg_transfn _null_ _null_ _null_ ));
DESCR("aggregate transition function");
! DATA(insert OID = 2334 ( array_agg_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2776" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ ));
DESCR("aggregate final function");
! DATA(insert OID = 2335 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2776" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
! DESCR("concatenate aggregate input into an array");
! DATA(insert OID = 6003 ( array_agg_anyarray_transfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_transfn _null_ _null_ _null_ ));
! DESCR("aggregate transition function");
! DATA(insert OID = 6004 ( array_agg_anyarray_finalfn PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2281 2277" _null_ _null_ _null_ _null_ array_agg_anyarray_finalfn _null_ _null_ _null_ ));
! DESCR("aggregate final function");
! DATA(insert OID = 6005 ( array_agg PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 2277 "2277" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
DESCR("concatenate aggregate input into an array");
DATA(insert OID = 3218 ( width_bucket PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 23 "2283 2277" _null_ _null_ _null_ _null_ width_bucket_array _null_ _null_ _null_ ));
DESCR("bucket number of operand given a sorted array of bucket lower bounds");
*** a/src/include/utils/array.h
--- b/src/include/utils/array.h
***************
*** 293,298 **** extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo,
--- 293,301 ----
extern Datum array_agg_transfn(PG_FUNCTION_ARGS);
extern Datum array_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_transfn(PG_FUNCTION_ARGS);
+ extern Datum array_agg_anyarray_finalfn(PG_FUNCTION_ARGS);
+
/*
* prototypes for functions defined in array_typanalyze.c
*/
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers