
#include "postgres.h"
#include "fmgr.h"
#include "utils/array.h"
#include "nodes/execnodes.h"

PG_MODULE_MAGIC;
PG_FUNCTION_INFO_V1(aaccum_sfunc);

Datum
aaccum_sfunc(PG_FUNCTION_ARGS)
{
	int32			totlen;
	bytea			*storage;
	Datum	 		element;
	ArrayBuildState	*astate = NULL;
	AggState		*aggstate;

	/* Make sure we are in an aggregate. */
	if (!fcinfo->context || !IsA(fcinfo->context, AggState))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("Can not call aaccum_sfunc as a non-aggregate")));

	aggstate = (AggState*) fcinfo->context;

	/* Initial call just passes in NULLs, so just allocate memory
	 * and get set up. */
	if (PG_ARGISNULL(0)) {
		storage = (bytea*) palloc(VARHDRSZ+sizeof(ArrayBuildState*));
		storage->vl_len = VARHDRSZ+sizeof(ArrayBuildState*);
		astate = NULL;
		memcpy(storage->vl_dat,&astate,sizeof(astate));
	} else {
		storage = PG_GETARG_BYTEA_P(0);
	}

	memcpy(&astate,storage->vl_dat,sizeof(astate));

	element = PG_GETARG_DATUM(1);

	astate = accumArrayResult(astate, element, PG_ARGISNULL(1), 
					          get_fn_expr_argtype(fcinfo->flinfo, 1),
							  aggstate->aggcontext);

	memcpy(storage->vl_dat,&astate,sizeof(astate));

	PG_RETURN_BYTEA_P(storage);
}

PG_FUNCTION_INFO_V1(aaccum_ffunc);

Datum
aaccum_ffunc(PG_FUNCTION_ARGS)
{
	int			dims[1];
	int			lbs[1];
	bytea		*storage;
	ArrayBuildState	*astate;
	AggState		*aggstate;
	ArrayType	*result;

	/* Make sure we are in an aggregate. */
	if (!fcinfo->context || !IsA(fcinfo->context, AggState))
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("Can not call aaccum_sfunc as a non-aggregate")));

	aggstate = (AggState*) fcinfo->context;

	if (PG_ARGISNULL(0)) PG_RETURN_ARRAYTYPE_P(NULL);

	storage = (bytea*) PG_GETARG_BYTEA_P(0);
	memcpy(&astate,storage->vl_dat,sizeof(astate));

	dims[0] = astate->nelems;
	lbs[0] = 1;

	result = construct_md_array(astate->dvalues,
								astate->dnulls,
								1,
								dims,
								lbs,
								astate->element_type,
								astate->typlen,
								astate->typbyval,
								astate->typalign);

	PG_RETURN_ARRAYTYPE_P(PointerGetDatum(result));
}
