2014-10-24 16:26 GMT+07:00 Pavel Stehule <pavel.steh...@gmail.com>: > Hi > > some in last patch is wrong, I cannot to compile it: > > arrayfuncs.c: In function ‘accumArrayResult’: > arrayfuncs.c:4603:9: error: ‘ArrayBuildState’ has no member named ‘alen’ > astate->alen = 64; /* arbitrary starting array size */ > ^ > arrayfuncs.c:4604:9: error: ‘ArrayBuildState’ has no member named ‘dvalues’ > astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum)); > ^ > arrayfuncs.c:4604:44: error: ‘ArrayBuildState’ has no member named ‘alen’ > astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum)); > ^ > arrayfuncs.c:4605:9: error: ‘ArrayBuildState’ has no member named ‘dnulls’ > astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool)); > ^ > arrayfuncs.c:4605:42: error: ‘ArrayBuildState’ has no member named ‘alen’ > astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool)); > ^ > arrayfuncs.c:4606:9: error: ‘ArrayBuildState’ has no member named ‘nelems’ > astate->nelems = 0; > ^ > arrayfuncs.c:4618:13: error: ‘ArrayBuildState’ has no member named ‘nelems’ > if (astate->nelems >= astate->alen) > ^ > arrayfuncs.c:4618:31: error: ‘ArrayBuildState’ has no member named ‘alen’ > if (astate->nelems >= astate->alen) > ^ > arrayfuncs.c:4620:10: error: ‘ArrayBuildState’ has no member named ‘alen’ > astate->alen *= 2; >
Sorry, correct patch attached. This patch is in patience format (git --patience ..). In previous patches, i use context format (git --patience ... | filterdiff --format=context), but it turns out that some modification is lost. -- Ali Akbar
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 7e5bcd9..f59738a 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -12046,6 +12046,22 @@ NULL baz</literallayout>(3 rows)</entry> <row> <entry> <indexterm> + <primary>array_agg</primary> + </indexterm> + <function>array_agg(<replaceable class="parameter">anyarray</replaceable>)</function> + </entry> + <entry> + any + </entry> + <entry> + the same array type as input type + </entry> + <entry>input arrays, aggregated into higher-order multidimesional array. Rejects NULL and empty array as input.</entry> + </row> + + <row> + <entry> + <indexterm> <primary>average</primary> </indexterm> <indexterm> diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml index 2f0680f..8c182a4 100644 --- a/doc/src/sgml/syntax.sgml +++ b/doc/src/sgml/syntax.sgml @@ -2238,6 +2238,11 @@ SELECT ARRAY(SELECT oid FROM pg_proc WHERE proname LIKE 'bytea%'); array ----------------------------------------------------------------------- {2011,1954,1948,1952,1951,1244,1950,2005,1949,1953,2006,31,2412,2413} + +SELECT ARRAY(SELECT array(select i) FROM generate_series(1,5) a(i)); + array +----------------------- + {{1},{2},{3},{4},{5}} (1 row) </programlisting> The subquery must return a single column. The resulting diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 41e973b..0261fcb 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -108,12 +108,16 @@ exprType(const Node *expr) type = exprType((Node *) tent->expr); if (sublink->subLinkType == ARRAY_SUBLINK) { - type = get_array_type(type); - if (!OidIsValid(type)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("could not find array type for data type %s", - format_type_be(exprType((Node *) tent->expr))))); + if (!OidIsValid(get_element_type(type))) + { + /* not array, so check for its array type */ + type = get_array_type(type); + if (!OidIsValid(type)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(exprType((Node *) tent->expr))))); + } } } else if (sublink->subLinkType == MULTIEXPR_SUBLINK) @@ -139,12 +143,16 @@ exprType(const Node *expr) type = subplan->firstColType; if (subplan->subLinkType == ARRAY_SUBLINK) { - type = get_array_type(type); - if (!OidIsValid(type)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("could not find array type for data type %s", - format_type_be(subplan->firstColType)))); + if (!OidIsValid(get_element_type(type))) + { + /* not array, so check for its array type */ + type = get_array_type(type); + if (!OidIsValid(type)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(subplan->firstColType)))); + } } } else if (subplan->subLinkType == MULTIEXPR_SUBLINK) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 3e7dc85..8fc8b49 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -668,10 +668,16 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, Assert(!te->resjunk); Assert(testexpr == NULL); - arraytype = get_array_type(exprType((Node *) te->expr)); - if (!OidIsValid(arraytype)) - elog(ERROR, "could not find array type for datatype %s", - format_type_be(exprType((Node *) te->expr))); + + arraytype = exprType((Node *) te->expr); + if (!OidIsValid(get_element_type(arraytype))) + { + /* not array, so get the array type */ + arraytype = get_array_type(exprType((Node *) te->expr)); + if (!OidIsValid(arraytype)) + elog(ERROR, "could not find array type for datatype %s", + format_type_be(exprType((Node *) te->expr))); + } prm = generate_new_param(root, arraytype, exprTypmod((Node *) te->expr), diff --git a/src/backend/utils/adt/array_userfuncs.c b/src/backend/utils/adt/array_userfuncs.c index 831466d..9fdb25b 100644 --- a/src/backend/utils/adt/array_userfuncs.c +++ b/src/backend/utils/adt/array_userfuncs.c @@ -16,7 +16,6 @@ #include "utils/builtins.h" #include "utils/lsyscache.h" - /*----------------------------------------------------------------------------- * array_push : * push an element onto either end of a one-dimensional array @@ -513,8 +512,6 @@ array_agg_finalfn(PG_FUNCTION_ARGS) { Datum result; ArrayBuildState *state; - int dims[1]; - int lbs[1]; /* * Test for null before Asserting we are in right context. This is to @@ -529,18 +526,43 @@ array_agg_finalfn(PG_FUNCTION_ARGS) state = (ArrayBuildState *) PG_GETARG_POINTER(0); - dims[0] = state->nelems; - lbs[0] = 1; - /* * Make the result. We cannot release the ArrayBuildState 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 = makeMdArrayResult(state, 1, dims, lbs, - CurrentMemoryContext, - false); + if (!state->is_array_accum) + { + int dims[1]; + int lbs[1]; + + dims[0] = ((ArrayBuildStateScalar *) state)->nelems; + lbs[0] = 1; + + result = makeMdArrayResult(state, 1, dims, lbs, + CurrentMemoryContext, + false); + } + else + result = makeArrayResultArray((ArrayBuildStateArray *) state, + CurrentMemoryContext, + false); PG_RETURN_DATUM(result); } + +/* + * ARRAY_AGG(anyarray) aggregate function + */ +Datum +array_agg_anyarray_transfn(PG_FUNCTION_ARGS) +{ + return array_agg_transfn(fcinfo); +} + +Datum +array_agg_anyarray_finalfn(PG_FUNCTION_ARGS) +{ + return array_agg_finalfn(fcinfo); +} diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 6c8b41d..582912e 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -145,7 +145,6 @@ static int width_bucket_array_variable(Datum operand, Oid collation, TypeCacheEntry *typentry); - /* * array_in : * converts an array from the external format in "string" to @@ -4588,9 +4587,13 @@ accumArrayResult(ArrayBuildState *astate, MemoryContext arr_context, oldcontext; + ArrayBuildStateScalar *astate_scalar = NULL; /* for scalar datum accumulation */ + ArrayBuildStateArray *astate_array = NULL; /* for array datum accumulation */ + if (astate == NULL) { /* First time through --- initialize */ + Oid subelement_type = get_element_type(element_type); /* Make a temporary context to hold all the junk */ arr_context = AllocSetContextCreate(rcontext, @@ -4599,60 +4602,224 @@ accumArrayResult(ArrayBuildState *astate, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcontext = MemoryContextSwitchTo(arr_context); - astate = (ArrayBuildState *) palloc(sizeof(ArrayBuildState)); - astate->mcontext = arr_context; - astate->alen = 64; /* arbitrary starting array size */ - astate->dvalues = (Datum *) palloc(astate->alen * sizeof(Datum)); - astate->dnulls = (bool *) palloc(astate->alen * sizeof(bool)); - astate->nelems = 0; - astate->element_type = element_type; - get_typlenbyvalalign(element_type, - &astate->typlen, - &astate->typbyval, - &astate->typalign); + + if (subelement_type == InvalidOid) + { + /* scalar accumulate */ + astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar)); + astate = (ArrayBuildState *) astate_scalar; + astate->is_array_accum = false; + astate->mcontext = arr_context; + + astate_scalar->alen = 64; /* arbitrary starting array size */ + astate_scalar->dvalues = (Datum *) palloc(astate_scalar->alen * sizeof(Datum)); + astate_scalar->dnulls = (bool *) palloc(astate_scalar->alen * sizeof(bool)); + astate_scalar->nelems = 0; + + astate->element_type = element_type; + get_typlenbyvalalign(element_type, + &astate->typlen, + &astate->typbyval, + &astate->typalign); + } + else + { + /* array accumulate */ + astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray)); + astate = (ArrayBuildState *) astate_array; + astate->is_array_accum = true; + astate->mcontext = arr_context; + + astate_array->abytes = 0; + astate_array->aitems = 0; + astate_array->data = NULL; + astate_array->nullbitmap = NULL; + astate_array->nitems = 0; + astate_array->narray = 0; + + astate->element_type = subelement_type; + get_typlenbyvalalign(subelement_type, + &astate->typlen, + &astate->typbyval, + &astate->typalign); + } } else { oldcontext = MemoryContextSwitchTo(astate->mcontext); - Assert(astate->element_type == element_type); - /* enlarge dvalues[]/dnulls[] if needed */ - if (astate->nelems >= astate->alen) + if (!astate->is_array_accum) { - astate->alen *= 2; - astate->dvalues = (Datum *) - repalloc(astate->dvalues, astate->alen * sizeof(Datum)); - astate->dnulls = (bool *) - repalloc(astate->dnulls, astate->alen * sizeof(bool)); + Assert(astate->element_type == element_type); + astate_scalar = (ArrayBuildStateScalar *) astate; + + /* enlarge dvalues[]/dnulls[] if needed */ + if (astate_scalar->nelems >= astate_scalar->alen) + { + astate_scalar->alen *= 2; + astate_scalar->dvalues = (Datum *) + repalloc(astate_scalar->dvalues, astate_scalar->alen * sizeof(Datum)); + astate_scalar->dnulls = (bool *) + repalloc(astate_scalar->dnulls, astate_scalar->alen * sizeof(bool)); + } + } + else + { + Assert(astate->element_type == get_element_type(element_type)); + astate_array = (ArrayBuildStateArray *) astate; + /* + * array accumulate alloc & realloc placed below because + * we need to know its length first + */ } } - /* - * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if - * it's varlena. (You might think that detoasting is not needed here - * because construct_md_array can detoast the array elements later. - * However, we must not let construct_md_array modify the ArrayBuildState - * because that would mean array_agg_finalfn damages its input, which is - * verboten. Also, this way frequently saves one copying step.) - */ - if (!disnull && !astate->typbyval) + if (!astate->is_array_accum) { - if (astate->typlen == -1) - dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue)); - else - dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen); + /* + * Ensure pass-by-ref stuff is copied into mcontext; and detoast it too if + * it's varlena. (You might think that detoasting is not needed here + * because construct_md_array can detoast the array elements later. + * However, we must not let construct_md_array modify the ArrayBuildState + * because that would mean array_agg_finalfn damages its input, which is + * verboten. Also, this way frequently saves one copying step.) + */ + if (!disnull && !astate->typbyval) + { + if (astate->typlen == -1) + dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue)); + else + dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen); + } + + astate_scalar->dvalues[astate_scalar->nelems] = dvalue; + astate_scalar->dnulls[astate_scalar->nelems] = disnull; + astate_scalar->nelems++; } + else + { + ArrayType *arg; + int *dims, + *lbs, + ndims, + nitems, + ndatabytes; + char *data; + int i; - astate->dvalues[astate->nelems] = dvalue; - astate->dnulls[astate->nelems] = disnull; - astate->nelems++; + if (disnull) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot accumulate null arrays"))); + + arg = DatumGetArrayTypeP(dvalue); + + 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_array->data == NULL) + { + /* first allocation */ + if (ndims == 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot accumulate 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_array->ndims = ndims; + astate_array->dims = (int *) palloc(ndims * sizeof(int)); + astate_array->lbs = (int *) palloc(ndims * sizeof(int)); + memcpy(astate_array->dims, dims, ndims * sizeof(int)); + memcpy(astate_array->lbs, lbs, ndims * sizeof(int)); + + astate_array->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024; + astate_array->aitems = 4 * nitems; + astate_array->data = (char *) palloc(astate_array->abytes); + + memcpy(astate_array->data, data, ndatabytes); + astate_array->nbytes = ndatabytes; + astate_array->nitems = nitems; + if (ARR_HASNULL(arg)) + { + astate_array->hasnull = true; + astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8); + array_bitmap_copy(astate_array->nullbitmap, 0, + ARR_NULLBITMAP(arg), 0, + nitems); + } + else + astate_array->hasnull = false; + } + else + { + if (astate_array->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_array->ndims, ndims))); + + for (i = 0; i < ndims; i++) + if (astate_array->dims[i] != dims[i] || astate_array->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_array->nbytes + ndatabytes >= astate_array->abytes) + { + astate_array->nbytes *= 2; + astate_array->data = (char *) + repalloc(astate_array->data, astate_array->nbytes); + } + if (astate_array->nitems + nitems >= astate_array->aitems) + { + astate_array->aitems *= 2; + astate_array->nullbitmap = (bits8 *) + repalloc(astate_array->nullbitmap, (astate_array->aitems + 7) / 8); + } + + memcpy(astate_array->data + astate_array->nbytes, data, ndatabytes); + astate_array->nbytes += ndatabytes; + + if (ARR_HASNULL(arg) || astate_array->hasnull) + { + if (!astate_array->hasnull) + { + astate_array->hasnull = true; + astate_array->nullbitmap = (bits8 *) palloc((astate_array->aitems + 7) / 8); + array_bitmap_copy(astate_array->nullbitmap, 0, + NULL, 0, + astate_array->nitems); + } + array_bitmap_copy(astate_array->nullbitmap, astate_array->nitems, + ARR_NULLBITMAP(arg), 0, + nitems); + astate_array->nitems += nitems; + } + } + astate_array->narray += 1; + } MemoryContextSwitchTo(oldcontext); return astate; } /* - * makeArrayResult - produce 1-D final result of accumArrayResult + * makeArrayResult - produce 1-D final result of scalar accumArrayResult + * - produce N+1-D final result of array accumArrayResult * * astate is working state (not NULL) * rcontext is where to construct result @@ -4661,17 +4828,22 @@ Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext) { - int dims[1]; - int lbs[1]; + if (!astate->is_array_accum) + { + int dims[1]; + int lbs[1]; - dims[0] = astate->nelems; - lbs[0] = 1; + dims[0] = ((ArrayBuildStateScalar *) astate)->nelems; + lbs[0] = 1; - return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true); + return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true); + } + else + return makeArrayResultArray((ArrayBuildStateArray *) astate, rcontext, true); } /* - * makeMdArrayResult - produce multi-D final result of accumArrayResult + * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult * * beware: no check that specified dimensions match the number of values * accumulated. @@ -4690,12 +4862,17 @@ makeMdArrayResult(ArrayBuildState *astate, { ArrayType *result; MemoryContext oldcontext; + ArrayBuildStateScalar *astate_scalar; + + Assert(!astate->is_array_accum); + + astate_scalar = (ArrayBuildStateScalar *) astate; /* Build the final array result in rcontext */ oldcontext = MemoryContextSwitchTo(rcontext); - result = construct_md_array(astate->dvalues, - astate->dnulls, + result = construct_md_array(astate_scalar->dvalues, + astate_scalar->dnulls, ndims, dims, lbs, @@ -4713,6 +4890,68 @@ makeMdArrayResult(ArrayBuildState *astate, return PointerGetDatum(result); } +/* + * makeMdArrayResultArray - produce N+1-D final result of array accumArrayResult + * + * astate is working state (not NULL) + * rcontext is where to construct result + * release is true if okay to release working state + */ +Datum +makeArrayResultArray(ArrayBuildStateArray *astate, + MemoryContext rcontext, + bool release) +{ + MemoryContext oldcontext; + + ArrayType *result; + + int dataoffset, + nbytes, + ndims; + + oldcontext = MemoryContextSwitchTo(rcontext); + + 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->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); + + /* Clean up all the junk */ + if (release) + MemoryContextDelete(astate->astate.mcontext); + + PG_RETURN_DATUM(PointerGetDatum(result)); +} + Datum array_larger(PG_FUNCTION_ARGS) { diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 3ba9e5e..2005199 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -275,6 +275,7 @@ DATA(insert ( 2901 n 0 xmlconcat2 - - - - f f 0 142 0 0 0 _null_ /* 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_ )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index b6dc1b8..9273c1f 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -879,11 +879,17 @@ 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_ )); +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 2283" _null_ _null_ _null_ _null_ array_agg_finalfn _null_ _null_ _null_ )); +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 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); +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"); diff --git a/src/include/utils/array.h b/src/include/utils/array.h index e744314..c6fbc7f 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -76,14 +76,15 @@ typedef struct /* * working state for accumArrayResult() and friends + * + * is_array_accum: whether accumulating array values. + * (if true must be casted to ArrayBuildStateArray, else + * cast to ArrayBuildStateScalar) */ typedef struct ArrayBuildState { + bool is_array_accum; MemoryContext mcontext; /* where all the temp stuff is kept */ - Datum *dvalues; /* array of accumulated Datums */ - bool *dnulls; /* array of is-null flags for Datums */ - int alen; /* allocated length of above arrays */ - int nelems; /* number of valid entries in above arrays */ Oid element_type; /* data type of the Datums */ int16 typlen; /* needed info about datatype */ bool typbyval; @@ -91,6 +92,43 @@ typedef struct ArrayBuildState } ArrayBuildState; /* + * array build state for array accumulation of scalar datums + */ +typedef struct ArrayBuildStateScalar +{ + ArrayBuildState astate; + + Datum *dvalues; /* array of accumulated Datums */ + bool *dnulls; /* array of is-null flags for Datums */ + int alen; /* allocated length of above arrays */ + int nelems; /* number of valid entries in above arrays */ +} ArrayBuildStateScalar; + + +/* + * array build state for array accumulation of array datums + */ +typedef struct +{ + ArrayBuildState astate; + + 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 */ + + int ndims; /* element dimensions */ + int *dims; + int *lbs; + + bool hasnull; /* any element has null */ +} ArrayBuildStateArray; + +/* * structure to cache type metadata needed for array manipulation */ typedef struct ArrayMetaState @@ -260,7 +298,8 @@ extern Datum makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext); extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, MemoryContext rcontext, bool release); - +extern Datum makeArrayResultArray(ArrayBuildStateArray *astate, + MemoryContext rcontext, bool release); extern ArrayIterator array_create_iterator(ArrayType *arr, int slice_ndim); extern bool array_iterate(ArrayIterator iterator, Datum *value, bool *isnull); extern void array_free_iterator(ArrayIterator iterator); @@ -293,6 +332,9 @@ extern ArrayType *create_singleton_array(FunctionCallInfo fcinfo, 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 */ diff --git a/src/test/regress/expected/aggregates.out b/src/test/regress/expected/aggregates.out index 58df854..eed1d5b 100644 --- a/src/test/regress/expected/aggregates.out +++ b/src/test/regress/expected/aggregates.out @@ -914,6 +914,33 @@ select array_agg(distinct a order by a desc nulls last) {3,2,1,NULL} (1 row) +-- array_agg(anyarray) +select array_agg(ar) + from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar); + array_agg +--------------- + {{1,2},{3,4}} +(1 row) + +select array_agg(distinct ar order by ar desc) + from (select array[i / 2] from generate_series(1,10) a(i)) b(ar); + array_agg +--------------------------- + {{5},{4},{3},{2},{1},{0}} +(1 row) + +select array_agg(ar) + from (select array_agg(array[i, i+1, i-1]) + from generate_series(1,2) a(i)) b(ar); + array_agg +--------------------- + {{{1,2,0},{2,3,1}}} +(1 row) + +select array_agg('{}'::int[]) from generate_series(1,2); +ERROR: cannot accumulate empty arrays +select array_agg(null::int[]) from generate_series(1,2); +ERROR: cannot accumulate null arrays -- multi-arg aggs, strict/nonstrict, distinct/order by select aggfstr(a,b,c) from (values (1,3,'foo'),(0,null,null),(2,2,'bar'),(3,1,'baz')) v(a,b,c); diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 46eff67..e80ebec 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -1521,6 +1521,23 @@ select array_agg(unique1) from tenk1 where unique1 < -15; (1 row) +select array(select unique1 from tenk1 where unique1 < 15 order by unique1); + array +-------------------------------------- + {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14} +(1 row) + +select array(select array[i,i/2] from generate_series(1,5) a(i)); + array +--------------------------------- + {{1,0},{2,1},{3,1},{4,2},{5,2}} +(1 row) + +-- cannot accumulate null arrays and empty arrays +select array(select null::int[]); +ERROR: cannot accumulate null arrays +select array(select '{}'::int[]); +ERROR: cannot accumulate empty arrays select unnest(array[1,2,3]); unnest -------- diff --git a/src/test/regress/sql/aggregates.sql b/src/test/regress/sql/aggregates.sql index 8096a6f..2296337 100644 --- a/src/test/regress/sql/aggregates.sql +++ b/src/test/regress/sql/aggregates.sql @@ -322,6 +322,18 @@ select array_agg(distinct a order by a desc) select array_agg(distinct a order by a desc nulls last) from (values (1),(2),(1),(3),(null),(2)) v(a); +-- array_agg(anyarray) +select array_agg(ar) + from (values ('{1,2}'::int[]), ('{3,4}'::int[])) v(ar); +select array_agg(distinct ar order by ar desc) + from (select array[i / 2] from generate_series(1,10) a(i)) b(ar); +select array_agg(ar) + from (select array_agg(array[i, i+1, i-1]) + from generate_series(1,2) a(i)) b(ar); + +select array_agg('{}'::int[]) from generate_series(1,2); +select array_agg(null::int[]) from generate_series(1,2); + -- multi-arg aggs, strict/nonstrict, distinct/order by select aggfstr(a,b,c) diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index fa8a20a..cb00f5f 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -432,6 +432,12 @@ select array_agg(ten) from (select ten from tenk1 where unique1 < 15 order by un select array_agg(nullif(ten, 4)) from (select ten from tenk1 where unique1 < 15 order by unique1) ss; select array_agg(unique1) from tenk1 where unique1 < -15; +select array(select unique1 from tenk1 where unique1 < 15 order by unique1); +select array(select array[i,i/2] from generate_series(1,5) a(i)); +-- cannot accumulate null arrays and empty arrays +select array(select null::int[]); +select array(select '{}'::int[]); + select unnest(array[1,2,3]); select * from unnest(array[1,2,3]); select unnest(array[1,2,3,4.5]::float8[]);
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers