>
> I fixed small issue in regress tests and I enhanced tests for varlena
> types and null values.

Thanks.

it is about 15% faster than original implementation.

15% faster than array_agg(scalar)? I haven't verify the performance, but
because the internal array data and null bitmap is copied as-is, that will
be faster.

2014-10-25 1:51 GMT+07:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hi Ali
>

> I checked a code. I am thinking so code organization is not good.
> accumArrayResult is too long now. makeMdArrayResult will not work, when
> arrays was joined (it is not consistent now). I don't like a usage of
> state->is_array_accum in array_userfunc.c -- it is signal of wrong
> wrapping.
>
Yes, i was thinking the same. Attached WIP patch to reorganizate the code.
makeMdArrayResult works now, with supplied arguments act as override from
default values calculated from ArrayBuildStateArray.

In array_userfunc.c, because we don't want to override ndims, dims and lbs,
i copied array_agg_finalfn and only change the call to makeMdArrayResult
(we don't uses makeArrayResult because we want to set release to false).
Another alternative is to create new makeArrayResult-like function that has
parameter bool release.


> next question: there is function array_append(anyarray, anyelement). Isn't
> time to define array_append(anyarray, anyarray) now?
>

There is array_cat(anyarray, anyarray):
/*-----------------------------------------------------------------------------
 * array_cat :
 * concatenate two nD arrays to form an nD array, or
 * push an (n-1)D array onto the end of an nD array
 *----------------------------------------------------------------------------
 */

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/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..8ad623c 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
@@ -529,16 +528,57 @@ array_agg_finalfn(PG_FUNCTION_ARGS)
 
 	state = (ArrayBuildState *) PG_GETARG_POINTER(0);
 
-	dims[0] = state->nelems;
+	/*
+	 * 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] = ((ArrayBuildStateScalar *) 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)
+{
+	return array_agg_transfn(fcinfo);
+}
+
+Datum
+array_agg_anyarray_finalfn(PG_FUNCTION_ARGS)
+{
+	Datum		result;
+	ArrayBuildState *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 = (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.
 	 */
-	result = makeMdArrayResult(state, 1, dims, lbs,
+	result = makeMdArrayResult(state, -1, NULL, NULL,
 							   CurrentMemoryContext,
 							   false);
 
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 6c8b41d..ba51792 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -145,6 +145,19 @@ static int width_bucket_array_variable(Datum operand,
 							Oid collation,
 							TypeCacheEntry *typentry);
 
+/* internal functions of accumArrayResult */
+static ArrayBuildStateScalar *initializeArrayBuildStateScalar(Oid element_type,
+								MemoryContext arr_context);
+static ArrayBuildStateArray *initializeArrayBuildStateArray(Oid subelement_type,
+							   MemoryContext arr_context);
+static void appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+				  Datum dvalue, bool disnull);
+static void appendArrayDatum(ArrayBuildStateArray *astate_array,
+				 Datum dvalue, bool disnull);
+
+/* internal function of makeMdArrayResult */
+static ArrayType *makeMdArrayResultArray(ArrayBuildStateArray *astate,
+					 int ndims, int *dims, int *lbs);
 
 /*
  * array_in :
@@ -4588,9 +4601,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,33 +4616,127 @@ 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 (!OidIsValid(subelement_type))
+		{
+			astate_scalar = initializeArrayBuildStateScalar(element_type, arr_context);
+			astate = (ArrayBuildState *) astate_scalar;
+
+			appendScalarDatum(astate_scalar, dvalue, disnull);
+		}
+		else
+		{
+			astate_array = initializeArrayBuildStateArray(subelement_type, arr_context);
+			astate = (ArrayBuildState *) astate_array;
+
+			appendArrayDatum(astate_array, dvalue, disnull);
+		}
 	}
 	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));
+			}
+
+			appendScalarDatum(astate_scalar, dvalue, disnull);
+		}
+		else
+		{
+			Assert(astate->element_type == get_element_type(element_type));
+			astate_array = (ArrayBuildStateArray *) astate;
+			/*
+			 * alloc & realloc placed in appendArrayDatum because
+			 * we need to know the dvalue length first
+			 */
+			appendArrayDatum(astate_array, dvalue, disnull);
 		}
 	}
 
+	MemoryContextSwitchTo(oldcontext);
+
+	return astate;
+}
+
+/*
+ * Initialize ArrayBuildState for scalar accumulation (internal function of
+ * accumArrayResult)
+ */
+ArrayBuildStateScalar *
+initializeArrayBuildStateScalar(Oid element_type,
+								MemoryContext arr_context)
+{
+	ArrayBuildStateScalar *astate_scalar;
+
+	/* scalar accumulate */
+	astate_scalar = (ArrayBuildStateScalar *) palloc(sizeof(ArrayBuildStateScalar));
+	astate_scalar->astate.is_array_accum = false;
+	astate_scalar->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_scalar->astate.element_type = element_type;
+	get_typlenbyvalalign(element_type,
+						 &astate_scalar->astate.typlen,
+						 &astate_scalar->astate.typbyval,
+						 &astate_scalar->astate.typalign);
+
+	return astate_scalar;
+}
+
+/*
+ * Initialize ArrayBuildState for array accum (internal of accumArrayResult)
+ */
+ArrayBuildStateArray *
+initializeArrayBuildStateArray(Oid subelement_type,
+							   MemoryContext arr_context)
+{
+	ArrayBuildStateArray *astate_array;
+
+	/* array accumulate */
+	astate_array = (ArrayBuildStateArray *) palloc(sizeof(ArrayBuildStateArray));
+	astate_array->astate.is_array_accum = true;
+	astate_array->astate.mcontext = arr_context;
+
+	astate_array->abytes = 0;
+	astate_array->aitems = 0;
+	astate_array->data = NULL;
+	astate_array->nbytes = 0;
+	astate_array->hasnull = false;
+	astate_array->nullbitmap = NULL;
+	astate_array->nitems = 0;
+	astate_array->narray = 0;
+
+	astate_array->astate.element_type = subelement_type;
+	get_typlenbyvalalign(subelement_type,
+						 &astate_array->astate.typlen,
+						 &astate_array->astate.typbyval,
+						 &astate_array->astate.typalign);
+
+	return astate_array;
+}
+
+/*
+ * Append a datum for scalar accumulation (internal of accumArrayResult)
+ */
+void
+appendScalarDatum(ArrayBuildStateScalar *astate_scalar,
+				  Datum dvalue, bool disnull)
+{
 	/*
 	 * 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
@@ -4634,25 +4745,129 @@ accumArrayResult(ArrayBuildState *astate,
 	 * 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 (!disnull && !astate_scalar->astate.typbyval)
 	{
-		if (astate->typlen == -1)
+		if (astate_scalar->astate.typlen == -1)
 			dvalue = PointerGetDatum(PG_DETOAST_DATUM_COPY(dvalue));
 		else
-			dvalue = datumCopy(dvalue, astate->typbyval, astate->typlen);
+			dvalue = datumCopy(dvalue, astate_scalar->astate.typbyval,
+							   astate_scalar->astate.typlen);
 	}
 
-	astate->dvalues[astate->nelems] = dvalue;
-	astate->dnulls[astate->nelems] = disnull;
-	astate->nelems++;
+	astate_scalar->dvalues[astate_scalar->nelems] = dvalue;
+	astate_scalar->dnulls[astate_scalar->nelems] = disnull;
+	astate_scalar->nelems++;
+}
 
-	MemoryContextSwitchTo(oldcontext);
+/*
+ * Append a datum for array accum (internal of accumArrayResult)
+ */
+void
+appendArrayDatum(ArrayBuildStateArray *astate_array,
+				 Datum dvalue, bool disnull)
+{
+	ArrayType  *arg;
+	int		   *dims,
+			   *lbs,
+				ndims,
+				nitems,
+				ndatabytes;
+	char	   *data;
+	int			i;
 
-	return astate;
+	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->data = (char *) palloc(astate_array->abytes);
+		astate_array->aitems = 4 * nitems;
+	}
+	else
+	{
+		if (astate_array->ndims != ndims)
+			ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("cannot accumulate 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 accumulate incompatible arrays"),
+					errdetail("Arrays with differing element dimensions are "
+							  "not compatible for concatenation.")));
+
+		if (astate_array->nbytes + ndatabytes >= astate_array->abytes)
+		{
+			astate_array->abytes *= 2;
+			astate_array->data = (char *)
+				repalloc(astate_array->data, astate_array->abytes);
+		}
+		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;
 }
 
 /*
- * 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 +4876,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 makeMdArrayResult(astate, -1, NULL, NULL, 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.
@@ -4679,6 +4899,11 @@ makeArrayResult(ArrayBuildState *astate,
  *	astate is working state (not NULL)
  *	rcontext is where to construct result
  *	release is true if okay to release working state
+ *
+ * for array accumulation:
+ *	ndims == -1: default (calculated from astate)
+ *  dims == NULL: default dims (calculated from astate)
+ *  lbs == NULL: default (calculated from astate)
  */
 Datum
 makeMdArrayResult(ArrayBuildState *astate,
@@ -4694,8 +4919,13 @@ makeMdArrayResult(ArrayBuildState *astate,
 	/* Build the final array result in rcontext */
 	oldcontext = MemoryContextSwitchTo(rcontext);
 
-	result = construct_md_array(astate->dvalues,
-								astate->dnulls,
+	if (!astate->is_array_accum)
+	{
+		ArrayBuildStateScalar *astate_scalar;
+		astate_scalar = (ArrayBuildStateScalar *) astate;
+
+		result = construct_md_array(astate_scalar->dvalues,
+								astate_scalar->dnulls,
 								ndims,
 								dims,
 								lbs,
@@ -4703,6 +4933,16 @@ makeMdArrayResult(ArrayBuildState *astate,
 								astate->typlen,
 								astate->typbyval,
 								astate->typalign);
+	}
+	else
+	{
+		ArrayBuildStateArray *astate_array;
+		astate_array = (ArrayBuildStateArray *) astate;
+		result = makeMdArrayResultArray(astate_array,
+									  ndims,
+									  dims,
+									  lbs);
+	}
 
 	MemoryContextSwitchTo(oldcontext);
 
@@ -4713,6 +4953,75 @@ makeMdArrayResult(ArrayBuildState *astate,
 	return PointerGetDatum(result);
 }
 
+/*
+ * makeMdArrayResultArray - internal of makeArrayResult & makeMdArrayResult
+ *
+ * beware: no check that specified dimensions match the values
+ * accumulated.
+ *
+ *	astate is working state (not NULL)
+ *	ndims > -1 if wants to override default (calculated from astate)
+ *  dims not NULL if wants to override default
+ *  lbs not NULL if wants to override default
+ */
+ArrayType *
+makeMdArrayResultArray(ArrayBuildStateArray *astate,
+					 int ndims,
+					 int *dims,
+					 int *lbs)
+{
+	ArrayType  *result;
+
+	int			dataoffset,
+				nbytes;
+
+	if (ndims == -1)
+		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;
+
+	if (dims != NULL)
+		memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
+	else
+	{
+		ARR_DIMS(result)[0] = astate->narray;
+		memcpy(&(ARR_DIMS(result)[1]), astate->dims, (ndims - 1) * sizeof(int));
+	}
+	if (lbs != NULL)
+		memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
+	else
+	{
+		ARR_LBOUND(result)[0] = 1;
+		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);
+
+	return 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..8dddd02 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,6 @@ extern Datum makeArrayResult(ArrayBuildState *astate,
 				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);
@@ -293,6 +330,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..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 (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to