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

Reply via email to