2014-10-27 9:11 GMT+07:00 Ali Akbar <[email protected]>:
>
> 2014-10-27 1:38 GMT+07:00 Pavel Stehule <[email protected]>:
>
>> Hi
>>
>> My idea is using new ArrayBuilder optimized for building multidimensional
>> arrays with own State type. I think so casting to ArrayBuildState is base
>> of our problems, so I don't would to do. Code in array_agg_* is simple,
>> little bit more complex code is in nodeSubplan.c. Some schematic changes
>> are in attachments.
>>
>
> Thanks! The structure looks clear, and thanks for the example on
> nodeSubplan.c. I will restructure the v10 of the patch to this structure.
>
Patch attached.
Regards,
--
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/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 401bad4..eb4de3b 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -231,7 +231,10 @@ ExecScanSubPlan(SubPlanState *node,
bool found = false; /* TRUE if got at least one subplan tuple */
ListCell *pvar;
ListCell *l;
+ bool use_md_array_builder;
+ Oid md_array_element_type;
ArrayBuildState *astate = NULL;
+ MdArrayBuildState *mdastate = NULL;
/*
* MULTIEXPR subplans, when "executed", just return NULL; but first we
@@ -366,8 +369,25 @@ ExecScanSubPlan(SubPlanState *node,
/* stash away current value */
Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
dvalue = slot_getattr(slot, 1, &disnull);
- astate = accumArrayResult(astate, dvalue, disnull,
- subplan->firstColType, oldcontext);
+ /*
+ * use a fast array multidimensional builder when input is a array
+ * only check on first iteration. On subsequent, use the cached values
+ */
+ if (astate == NULL && mdastate == NULL)
+ {
+ md_array_element_type = get_element_type(subplan->firstColType);
+ use_md_array_builder = OidIsValid(md_array_element_type);
+ }
+
+ if (use_md_array_builder)
+ mdastate = accumMdArray(mdastate,
+ disnull? NULL :
+ DatumGetArrayTypeP(dvalue),
+ disnull, md_array_element_type,
+ oldcontext);
+ else
+ astate = accumArrayResult(astate, dvalue, disnull,
+ subplan->firstColType, oldcontext);
/* keep scanning subplan to collect all values */
continue;
}
@@ -439,6 +459,8 @@ ExecScanSubPlan(SubPlanState *node,
/* We return the result in the caller's context */
if (astate != NULL)
result = makeArrayResult(astate, oldcontext);
+ else if (mdastate != NULL)
+ result = makeMdArray(mdastate, oldcontext);
else
result = PointerGetDatum(construct_empty_array(subplan->firstColType));
}
@@ -951,7 +973,10 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
ListCell *pvar;
ListCell *l;
bool found = false;
+ bool use_md_array_builder;
+ Oid md_array_element_type;
ArrayBuildState *astate = NULL;
+ MdArrayBuildState *mdastate = NULL;
if (subLinkType == ANY_SUBLINK ||
subLinkType == ALL_SUBLINK)
@@ -1018,8 +1043,25 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
/* stash away current value */
Assert(subplan->firstColType == tdesc->attrs[0]->atttypid);
dvalue = slot_getattr(slot, 1, &disnull);
- astate = accumArrayResult(astate, dvalue, disnull,
- subplan->firstColType, oldcontext);
+ /*
+ * use a fast array multidimensional builder when input is a array
+ * only check on first iteration. On subsequent, use the cached values
+ */
+ if (astate == NULL && mdastate == NULL)
+ {
+ md_array_element_type = get_element_type(subplan->firstColType);
+ use_md_array_builder = OidIsValid(md_array_element_type);
+ }
+
+ if (use_md_array_builder)
+ mdastate = accumMdArray(mdastate,
+ disnull? NULL :
+ DatumGetArrayTypeP(dvalue),
+ disnull, md_array_element_type,
+ oldcontext);
+ else
+ astate = accumArrayResult(astate, dvalue, disnull,
+ subplan->firstColType, oldcontext);
/* keep scanning subplan to collect all values */
continue;
}
@@ -1075,6 +1117,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
if (astate != NULL)
node->curArray = makeArrayResult(astate,
econtext->ecxt_per_query_memory);
+ else if (mdastate != NULL)
+ node->curArray = makeMdArray(mdastate,
+ econtext->ecxt_per_query_memory);
else
{
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
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..dcb624c 100644
--- a/src/backend/utils/adt/array_userfuncs.c
+++ b/src/backend/utils/adt/array_userfuncs.c
@@ -16,6 +16,8 @@
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+static Datum array_agg_transfn_internal(PG_FUNCTION_ARGS,
+ const char* fname);
/*-----------------------------------------------------------------------------
* array_push :
@@ -513,8 +515,8 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
{
Datum result;
ArrayBuildState *state;
- int dims[1];
- int lbs[1];
+ int dims[0];
+ int lbs[0];
/*
* Test for null before Asserting we are in right context. This is to
@@ -529,8 +531,76 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
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.
+ */
dims[0] = state->nelems;
lbs[0] = 1;
+ result = makeMdArrayResult(state, 1, dims, lbs, CurrentMemoryContext, false);
+
+ PG_RETURN_DATUM(result);
+}
+
+/*
+ * ARRAY_AGG(anyarray) aggregate function
+ */
+Datum
+array_agg_anyarray_transfn(PG_FUNCTION_ARGS)
+{
+ Oid arg1_typeid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ Oid element_type = get_element_type(arg1_typeid);
+ MemoryContext aggcontext;
+ MdArrayBuildState *state;
+ Datum elem;
+
+ if (arg1_typeid == InvalidOid)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("could not determine input data type")));
+
+ if (!AggCheckCallContext(fcinfo, &aggcontext))
+ {
+ /* cannot be called directly because of internal-type argument */
+ elog(ERROR, "array_agg_anyarray_transfn called in non-aggregate context");
+ }
+
+ state = PG_ARGISNULL(0) ? NULL : (MdArrayBuildState *) PG_GETARG_POINTER(0);
+ elem = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_ARRAYTYPE_P(1);
+ state = accumMdArray(state,
+ elem,
+ PG_ARGISNULL(1),
+ element_type,
+ 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 ArrayBuildState pointer through nodeAgg.c's machinations.
+ */
+ PG_RETURN_POINTER(state);
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+ Datum result;
+ MdArrayBuildState *state;
+
+ /*
+ * 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));
+
+ state = (MdArrayBuildState *) PG_GETARG_POINTER(0);
/*
* Make the result. We cannot release the ArrayBuildState because
@@ -538,9 +608,7 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
* nodeAgg.c's responsibility to reset the aggcontext when it's safe to do
* so.
*/
- result = makeMdArrayResult(state, 1, dims, lbs,
- CurrentMemoryContext,
- false);
+ result = makeMdCustomArray(state, -1, NULL, NULL, CurrentMemoryContext, false);
PG_RETURN_DATUM(result);
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 6c8b41d..1c922d5 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4576,6 +4576,9 @@ array_insert_slice(ArrayType *destArray,
/*
* accumArrayResult - accumulate one (more) Datum for an array result
*
+ * Beware: use this only for scalar datums. For Array datums, use
+ * accumMdArray and friends.
+ *
* astate is working state (NULL on first call)
* rcontext is where to keep working state
*/
@@ -4588,6 +4591,9 @@ accumArrayResult(ArrayBuildState *astate,
MemoryContext arr_context,
oldcontext;
+ /* element_type must not be an array! */
+ Assert(!OidIsValid(get_element_type(element_type)));
+
if (astate == NULL)
{
/* First time through --- initialize */
@@ -4713,6 +4719,269 @@ makeMdArrayResult(ArrayBuildState *astate,
return PointerGetDatum(result);
}
+/*
+ * accumMdArray - accumulate one (more) array Datum for accumulation
+ *
+ * astate is working state (NULL on first call)
+ * element_type is the type that array hold
+ * rcontext is where to keep working state
+ */
+MdArrayBuildState *
+accumMdArray(MdArrayBuildState *astate,
+ ArrayType *arg, bool disnull,
+ Oid element_type,
+ MemoryContext rcontext)
+{
+ return accumMdCustomArray(astate, arg, disnull, element_type,
+ true, rcontext);
+}
+
+/*
+ * accumMdCustomArray - accumMdArray with option to not check
+ * dims and lbs
+ *
+ * astate is working state (NULL on first call)
+ * element_type is the type that array hold
+ * check_dims_and_lbs is the option to check dims and lbs
+ * rcontext is where to keep working state
+ */
+MdArrayBuildState *
+accumMdCustomArray(MdArrayBuildState *astate,
+ ArrayType *arg, bool disnull,
+ Oid element_type,
+ bool check_dims_and_lbs,
+ MemoryContext rcontext)
+{
+ MemoryContext arr_context,
+ oldcontext;
+
+ int *dims,
+ *lbs,
+ ndims,
+ nitems,
+ ndatabytes;
+ char *data;
+ int i;
+
+ /* element_type must not be an array! */
+ Assert(!OidIsValid(get_element_type(element_type)));
+
+ if (disnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("cannot accumulate null arrays")));
+
+ 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 (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)));
+
+ if (astate == NULL)
+ {
+ /* Make a temporary context to hold all the junk */
+ arr_context = AllocSetContextCreate(rcontext,
+ "accumArrayResult",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ oldcontext = MemoryContextSwitchTo(arr_context);
+
+ /* array accumulate */
+ astate = (MdArrayBuildState *) palloc(sizeof(MdArrayBuildState));
+ astate->mcontext = arr_context;
+
+ /*
+ * the resulting dimensions is n + 1, with first dimension is the number of
+ * array accumulated
+ */
+ astate->ndims = ndims + 1;
+ astate->dims = (int *) palloc((astate->ndims) * sizeof(int));
+ astate->lbs = (int *) palloc((astate->ndims) * sizeof(int));
+ astate->dims[0] = 0;
+ memcpy(&astate->dims[1], dims, ndims * sizeof(int));
+ astate->lbs[0] = 1;
+ memcpy(&astate->lbs[1], lbs, ndims * sizeof(int));
+
+ astate->aitems = 0;
+ astate->nbytes = 0;
+ astate->hasnull = false;
+ astate->nullbitmap = NULL;
+ astate->nitems = 0;
+
+ astate->abytes = ndatabytes >= 512 ? 4 * ndatabytes : 1024;
+ astate->data = (char *) palloc(astate->abytes);
+
+ 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);
+
+ if (check_dims_and_lbs)
+ {
+ if (astate->ndims != ndims + 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays of %d and %d dimensions are not "
+ "compatible for accumulation.",
+ astate->ndims - 1, ndims)));
+
+ for (i = 0; i < ndims; i++)
+ if (astate->dims[i+1] != dims[i] || astate->lbs[i+1] != lbs[i])
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("cannot accumulate incompatible arrays"),
+ errdetail("Arrays with differing element dimensions are "
+ "not compatible for concatenation.")));
+ }
+
+ /* grow array if needed */
+ if (astate->nbytes + ndatabytes >= astate->abytes)
+ {
+ astate->abytes *= 2;
+ astate->data = (char *)
+ repalloc(astate->data, astate->abytes);
+ }
+ if (astate->hasnull && 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 (astate->hasnull || ARR_HASNULL(arg))
+ {
+ if (!astate->hasnull)
+ {
+ /* arbitrary number if nitems below 32 */
+ astate->aitems = astate->nitems < 32 ? 64 : astate->nitems * 8;
+ 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->dims[0] += 1;
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return astate;
+}
+
+/*
+ * makeMdArray - produce N+1-D final result of accumMdArray
+ *
+ * astate is working state (not NULL)
+ * rcontext is where to construct result
+ */
+Datum
+makeMdArray(MdArrayBuildState *astate,
+ MemoryContext rcontext)
+{
+ return makeMdCustomArray(astate, -1, NULL, NULL, rcontext, true);
+}
+
+/*
+ * makeMdCustomArray - produce custom-D final result of accumMdArray
+ *
+ * beware: no check that specified dimensions match the number of values
+ * accumulated.
+ *
+ * astate is working state (not NULL)
+ * rcontext is where to construct result
+ * release is true if okay to release working state
+ * ndims = -1 will produce N+1-D array, with dims and lbs value ignored
+ */
+Datum
+makeMdCustomArray(MdArrayBuildState *astate,
+ int ndims,
+ int *dims,
+ int *lbs,
+ MemoryContext rcontext,
+ bool release)
+{
+ ArrayType *result;
+ MemoryContext oldcontext;
+
+ int dataoffset,
+ nbytes;
+
+ /* Build the final array result in rcontext */
+ oldcontext = MemoryContextSwitchTo(rcontext);
+
+ if (ndims == -1)
+ {
+ ndims = astate->ndims;
+ dims = astate->dims;
+ lbs = astate->lbs;
+ }
+
+ 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 = ndims;
+ result->dataoffset = dataoffset;
+ result->elemtype = astate->element_type;
+
+ memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+ memcpy(ARR_LBOUND(result), lbs, ndims * 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->mcontext);
+
+ return 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..b8f0fe1 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -75,7 +75,8 @@ typedef struct
} ArrayType;
/*
- * working state for accumArrayResult() and friends
+ * working state for array accumulation of scalar datums
+ * with accumArrayResult() and friends
*/
typedef struct ArrayBuildState
{
@@ -91,6 +92,34 @@ typedef struct ArrayBuildState
} ArrayBuildState;
/*
+ * array build state for array accumulation of array datums
+ * with accumMdArray() and friends
+ */
+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 ndims; /* element dimensions */
+ int *dims;
+ int *lbs;
+
+ bool hasnull; /* any element has null */
+
+ Oid element_type; /* data type of the Datums */
+ int16 typlen; /* needed info about datatype */
+ bool typbyval;
+ char typalign;
+} MdArrayBuildState;
+
+/*
* structure to cache type metadata needed for array manipulation
*/
typedef struct ArrayMetaState
@@ -260,6 +289,19 @@ extern Datum makeArrayResult(ArrayBuildState *astate,
MemoryContext rcontext);
extern Datum makeMdArrayResult(ArrayBuildState *astate, int ndims,
int *dims, int *lbs, MemoryContext rcontext, bool release);
+extern MdArrayBuildState *accumMdArray(MdArrayBuildState *astate,
+ ArrayType *arg, bool disnull,
+ Oid element_type,
+ MemoryContext rcontext);
+extern MdArrayBuildState *accumMdCustomArray(MdArrayBuildState *astate,
+ ArrayType *arg, bool disnull,
+ Oid element_type,
+ bool check_dims_and_lbs,
+ MemoryContext rcontext);
+extern Datum makeMdArray(MdArrayBuildState *astate,
+ MemoryContext rcontext);
+extern Datum makeMdCustomArray(MdArrayBuildState *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);
@@ -292,6 +334,8 @@ 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..607aeea 100644
--- a/src/test/regress/expected/aggregates.out
+++ b/src/test/regress/expected/aggregates.out
@@ -914,6 +914,76 @@ 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)
+
+-- 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)
+
+-- errors
+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
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+ERROR: cannot accumulate incompatible arrays
+DETAIL: Arrays with differing element dimensions are not compatible for concatenation.
-- 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..a70419f 100644
--- a/src/test/regress/sql/aggregates.sql
+++ b/src/test/regress/sql/aggregates.sql
@@ -322,6 +322,32 @@ 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);
+
+-- 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);
+
+-- errors
+select array_agg('{}'::int[]) from generate_series(1,2);
+select array_agg(null::int[]) from generate_series(1,2);
+select array_agg(ar)
+ from (values ('{1,2}'::int[]), ('{3}'::int[])) v(ar);
+
-- 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 ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers