2014-10-24 13:58 GMT+02:00 Pavel Stehule <pavel.steh...@gmail.com>: > > > 2014-10-24 11:43 GMT+02:00 Ali Akbar <the.ap...@gmail.com>: > >> >> 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. >> > > last version is compileable, but some is still broken > > postgres=# select array_agg(array[i, i+1, i-1]) > from generate_series(1,2) a(i); > ERROR: could not find array type for data type integer[] >
I am sorry, it works - I had a problem with broken database I fixed small issue in regress tests and I enhanced tests for varlena types and null values. Regards Pavel > > but array(subselect) works > > postgres=# select array(select a from xx); > array > ------------------- > {{1,2,3},{1,2,3}} > (1 row) > > Regards > > Pavel > > > >> >> -- >> Ali Akbar >> > >
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml new file mode 100644 index 7e5bcd9..f59738a *** a/doc/src/sgml/func.sgml --- b/doc/src/sgml/func.sgml *************** NULL baz</literallayout>(3 rows)</entry> *** 12046,12051 **** --- 12046,12067 ---- <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 new file mode 100644 index 2f0680f..8c182a4 *** a/doc/src/sgml/syntax.sgml --- b/doc/src/sgml/syntax.sgml *************** SELECT ARRAY(SELECT oid FROM pg_proc WHE *** 2238,2243 **** --- 2238,2248 ---- 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 new file mode 100644 index 41e973b..0261fcb *** a/src/backend/nodes/nodeFuncs.c --- b/src/backend/nodes/nodeFuncs.c *************** exprType(const Node *expr) *** 108,119 **** 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))))); } } else if (sublink->subLinkType == MULTIEXPR_SUBLINK) --- 108,123 ---- type = exprType((Node *) tent->expr); if (sublink->subLinkType == ARRAY_SUBLINK) { ! 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) *************** exprType(const Node *expr) *** 139,150 **** 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)))); } } else if (subplan->subLinkType == MULTIEXPR_SUBLINK) --- 143,158 ---- type = subplan->firstColType; if (subplan->subLinkType == ARRAY_SUBLINK) { ! 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 new file mode 100644 index 3e7dc85..8fc8b49 *** a/src/backend/optimizer/plan/subselect.c --- b/src/backend/optimizer/plan/subselect.c *************** build_subplan(PlannerInfo *root, Plan *p *** 668,677 **** 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))); prm = generate_new_param(root, arraytype, exprTypmod((Node *) te->expr), --- 668,683 ---- Assert(!te->resjunk); Assert(testexpr == NULL); ! ! 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 new file mode 100644 index 831466d..9fdb25b *** a/src/backend/utils/adt/array_userfuncs.c --- b/src/backend/utils/adt/array_userfuncs.c *************** *** 16,22 **** #include "utils/builtins.h" #include "utils/lsyscache.h" - /*----------------------------------------------------------------------------- * array_push : * push an element onto either end of a one-dimensional array --- 16,21 ---- *************** array_agg_finalfn(PG_FUNCTION_ARGS) *** 513,520 **** { Datum result; ArrayBuildState *state; - int dims[1]; - int lbs[1]; /* * Test for null before Asserting we are in right context. This is to --- 512,517 ---- *************** array_agg_finalfn(PG_FUNCTION_ARGS) *** 529,546 **** 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); PG_RETURN_DATUM(result); } --- 526,568 ---- state = (ArrayBuildState *) PG_GETARG_POINTER(0); /* * 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. */ ! 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 new file mode 100644 index 6c8b41d..582912e *** a/src/backend/utils/adt/arrayfuncs.c --- b/src/backend/utils/adt/arrayfuncs.c *************** static int width_bucket_array_variable(D *** 145,151 **** Oid collation, TypeCacheEntry *typentry); - /* * array_in : * converts an array from the external format in "string" to --- 145,150 ---- *************** accumArrayResult(ArrayBuildState *astate *** 4588,4596 **** --- 4587,4599 ---- 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, *************** accumArrayResult(ArrayBuildState *astate *** 4599,4658 **** 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); } else { oldcontext = MemoryContextSwitchTo(astate->mcontext); ! Assert(astate->element_type == element_type); ! /* enlarge dvalues[]/dnulls[] if needed */ ! if (astate->nelems >= astate->alen) { ! astate->alen *= 2; ! astate->dvalues = (Datum *) ! repalloc(astate->dvalues, astate->alen * sizeof(Datum)); ! astate->dnulls = (bool *) ! repalloc(astate->dnulls, astate->alen * sizeof(bool)); } } ! /* ! * 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->dvalues[astate->nelems] = dvalue; ! astate->dnulls[astate->nelems] = disnull; ! astate->nelems++; MemoryContextSwitchTo(oldcontext); return astate; } /* ! * makeArrayResult - produce 1-D final result of accumArrayResult * * astate is working state (not NULL) * rcontext is where to construct result --- 4602,4825 ---- ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcontext = MemoryContextSwitchTo(arr_context); ! ! 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); ! if (!astate->is_array_accum) { ! 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 ! */ } } ! if (!astate->is_array_accum) { ! /* ! * 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; ! 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 scalar accumArrayResult ! * - produce N+1-D final result of array accumArrayResult * * astate is working state (not NULL) * rcontext is where to construct result *************** Datum *** 4661,4677 **** makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext) { ! int dims[1]; ! int lbs[1]; ! dims[0] = astate->nelems; ! lbs[0] = 1; ! return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true); } /* ! * makeMdArrayResult - produce multi-D final result of accumArrayResult * * beware: no check that specified dimensions match the number of values * accumulated. --- 4828,4849 ---- makeArrayResult(ArrayBuildState *astate, MemoryContext rcontext) { ! if (!astate->is_array_accum) ! { ! int dims[1]; ! int lbs[1]; ! dims[0] = ((ArrayBuildStateScalar *) astate)->nelems; ! lbs[0] = 1; ! return makeMdArrayResult(astate, 1, dims, lbs, rcontext, true); ! } ! else ! return makeArrayResultArray((ArrayBuildStateArray *) astate, rcontext, true); } /* ! * makeMdArrayResult - produce multi-D final result of scalar accumArrayResult * * beware: no check that specified dimensions match the number of values * accumulated. *************** makeMdArrayResult(ArrayBuildState *astat *** 4690,4701 **** { ArrayType *result; MemoryContext oldcontext; /* Build the final array result in rcontext */ oldcontext = MemoryContextSwitchTo(rcontext); ! result = construct_md_array(astate->dvalues, ! astate->dnulls, ndims, dims, lbs, --- 4862,4878 ---- { 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_scalar->dvalues, ! astate_scalar->dnulls, ndims, dims, lbs, *************** makeMdArrayResult(ArrayBuildState *astat *** 4713,4718 **** --- 4890,4957 ---- 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 new file mode 100644 index 3ba9e5e..2005199 *** a/src/include/catalog/pg_aggregate.h --- b/src/include/catalog/pg_aggregate.h *************** DATA(insert ( 2901 n 0 xmlconcat2 - *** 275,280 **** --- 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_ )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h new file mode 100644 index b6dc1b8..9273c1f *** a/src/include/catalog/pg_proc.h --- b/src/include/catalog/pg_proc.h *************** DATA(insert OID = 3167 ( array_remove *** 879,889 **** 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"); diff --git a/src/include/utils/array.h b/src/include/utils/array.h new file mode 100644 index e744314..c6fbc7f *** a/src/include/utils/array.h --- b/src/include/utils/array.h *************** typedef struct *** 76,89 **** /* * working state for accumArrayResult() and friends */ typedef struct ArrayBuildState { 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; --- 76,90 ---- /* * 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 */ Oid element_type; /* data type of the Datums */ int16 typlen; /* needed info about datatype */ bool typbyval; *************** typedef struct ArrayBuildState *** 91,96 **** --- 92,134 ---- } 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 *************** extern Datum makeArrayResult(ArrayBuildS *** 260,266 **** MemoryContext rcontext); extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims, int *dims, int *lbs, 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); --- 298,305 ---- 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); *************** extern ArrayType *create_singleton_array *** 293,298 **** --- 332,340 ---- 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 new file mode 100644 index 58df854..7568724 *** a/src/test/regress/expected/aggregates.out --- b/src/test/regress/expected/aggregates.out *************** select array_agg(distinct a order by a d *** 914,919 **** --- 914,984 ---- {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) + + -- array_agg(anyarray), varlena types + select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3); + array_agg + --------------------------------------------- + {{1.2,1.3,1.4},{1.2,1.3,1.4},{1.2,1.3,1.4}} + (1 row) + + select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3); + array_agg + --------------------------------------------------------- + {{Hello,Hohoho,Hi},{Hello,Hohoho,Hi},{Hello,Hohoho,Hi}} + (1 row) + + -- array_agg(anyarray), arrays with nulls + select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i); + array_agg + --------------------------------------------------------- + {{1,NULL,2,NULL,3},{2,NULL,3,NULL,4},{3,NULL,4,NULL,5}} + (1 row) + + select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i); + array_agg + --------------------------------------------------------------------------- + {{2.1,NULL,3.1,NULL,4.1},{3.1,NULL,4.1,NULL,5.1},{4.1,NULL,5.1,NULL,6.1}} + (1 row) + + select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3); + array_agg + --------------------------------------------------------------------------------------- + {{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi},{NULL,Hello,Hohoho,NULL,Hi}} + (1 row) + + select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2); + array_agg + ------------------------------------------------------------------------------------------- + {{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}},{{NULL,Hello,NULL,Hi},{Hello,NULL,Hi,NULL}}} + (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 new file mode 100644 index 46eff67..e80ebec *** a/src/test/regress/expected/arrays.out --- b/src/test/regress/expected/arrays.out *************** select array_agg(unique1) from tenk1 whe *** 1521,1526 **** --- 1521,1543 ---- (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 new file mode 100644 index 8096a6f..165f248 *** a/src/test/regress/sql/aggregates.sql --- b/src/test/regress/sql/aggregates.sql *************** select array_agg(distinct a order by a d *** 322,327 **** --- 322,350 ---- 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); + + -- array_agg(anyarray), varlena types + select array_agg(array[1.2,1.3,1.4]) from generate_series(1,3); + select array_agg(array['Hello','Hohoho','Hi']) from generate_series(1,3); + + -- array_agg(anyarray), arrays with nulls + select array_agg(array[i, null, i+1, null, i+2]) from generate_series(1,3) g(i); + select array_agg(array[1.1+ i, null, 1.1+i+1, null, 1.1+i+2]) from generate_series(1,3) g(i); + select array_agg(array[null, 'Hello','Hohoho', null,'Hi']) from generate_series(1,3); + + select array_agg(array[[null, 'Hello', null, 'Hi'],['Hello', null, 'Hi', null]]) from generate_series(1,2); + + 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 new file mode 100644 index fa8a20a..cb00f5f *** a/src/test/regress/sql/arrays.sql --- b/src/test/regress/sql/arrays.sql *************** select array_agg(ten) from (select ten f *** 432,437 **** --- 432,443 ---- 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