pg-custom-aggregate-grt
pg-custom-aggregate-grt copied to clipboard
is it possible to create a json_agg () function in pgsql in postgresql 9.2
is it possible to create a json_agg () function in pgsql in postgresql 9.2
I would like to implement this function in pgsql, because in the current database I do not have access to install extensions or even update the database
/*
* json_agg transition function
*
* aggregate input column as a json array value.
*/
PG_FUNCTION_INFO_V1(json_agg_transfn);
Datum
json_agg_transfn(PG_FUNCTION_ARGS)
{
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
MemoryContext aggcontext, oldcontext;
StringInfo state;
Datum val;
JsonTypeCategory tcategory;
Oid outfuncoid;
if (val_type == 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, "json_agg_transfn called in non-aggregate context");
}
if (PG_ARGISNULL(0))
{
/*
* Make this StringInfo in a context where it will persist for the
* duration of the aggregate call. MemoryContextSwitchTo is only
* needed the first time, as the StringInfo routines make sure they
* use the right context to enlarge the object if necessary.
*/
oldcontext = MemoryContextSwitchTo(aggcontext);
state = makeStringInfo();
MemoryContextSwitchTo(oldcontext);
appendStringInfoChar(state,'[');
}
else
{
state = (StringInfo) PG_GETARG_POINTER(0);
appendStringInfoString(state, ", ");
}
/* fast path for NULLs */
if (PG_ARGISNULL(1))
{
datum_to_json((Datum) 0, true, state, JSONTYPE_NULL, InvalidOid);
PG_RETURN_POINTER(state);
}
val = PG_GETARG_DATUM(1);
/* XXX we do this every time?? */
json_categorize_type(val_type,
&tcategory, &outfuncoid);
/* add some whitespace if structured type and not first item */
if (! PG_ARGISNULL(0) &&
(tcategory == JSONTYPE_ARRAY || tcategory == JSONTYPE_COMPOSITE))
{
appendStringInfoString(state,"\n ");
}
datum_to_json(val, false, state, tcategory, outfuncoid);
/*
* 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);
}
/*
* json_agg final function
*/
PG_FUNCTION_INFO_V1(json_agg_finalfn);
Datum
json_agg_finalfn(PG_FUNCTION_ARGS)
{
StringInfo state;
/* cannot be called directly because of internal-type argument */
Assert(AggCheckCallContext(fcinfo, NULL));
state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
/* NULL result for no rows in, as is standard with aggregates */
if (state == NULL)
PG_RETURN_NULL();
/* Else return state with appropriate array terminator added */
PG_RETURN_TEXT_P(catenate_stringinfo_string(state, "]"));
}
/*
* Helper function for aggregates: return given StringInfo's contents plus
* specified trailing string, as a text datum. We need this because aggregate
* final functions are not allowed to modify the aggregate state.
*/
static text *
catenate_stringinfo_string(StringInfo buffer, const char *addon)
{
/* custom version of cstring_to_text_with_len */
int buflen = buffer->len;
int addlen = strlen(addon);
text *result = (text *) palloc(buflen + addlen + VARHDRSZ);
SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
memcpy(VARDATA(result), buffer->data, buflen);
memcpy(VARDATA(result) + buflen, addon, addlen);
return result;
}
I'm sure it's possible with a bit of effort. But there is a much simpler solution.
Instead of using json_agg(foo) use array_to_json(array_agg(foo)).