pg-custom-aggregate-grt icon indicating copy to clipboard operation
pg-custom-aggregate-grt copied to clipboard

is it possible to create a json_agg () function in pgsql in postgresql 9.2

Open insinfo opened this issue 7 years ago • 1 comments

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;
}

insinfo avatar Oct 02 '18 20:10 insinfo

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)).

jackc avatar Oct 02 '18 21:10 jackc