diff options
Diffstat (limited to 'src/backend/utils/adt/json.c')
-rw-r--r-- | src/backend/utils/adt/json.c | 125 |
1 files changed, 74 insertions, 51 deletions
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 8d0434767aa..eefe93bc8ab 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -68,6 +68,15 @@ typedef enum /* type categories for datum_to_json */ JSONTYPE_OTHER /* all else */ } JsonTypeCategory; +typedef struct JsonAggState +{ + StringInfo str; + JsonTypeCategory key_category; + Oid key_output_func; + JsonTypeCategory val_category; + Oid val_output_func; +} JsonAggState; + static inline void json_lex(JsonLexContext *lex); static inline void json_lex_string(JsonLexContext *lex); static inline void json_lex_number(JsonLexContext *lex, char *s, bool *num_err); @@ -1858,18 +1867,10 @@ to_json(PG_FUNCTION_ARGS) Datum json_agg_transfn(PG_FUNCTION_ARGS) { - Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); MemoryContext aggcontext, oldcontext; - StringInfo state; + JsonAggState *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)) { @@ -1879,50 +1880,59 @@ json_agg_transfn(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { + Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); + + if (arg_type == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not determine input data type"))); + /* - * Make this StringInfo in a context where it will persist for the + * Make this state object 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(); + state = (JsonAggState *) palloc(sizeof(JsonAggState)); + state->str = makeStringInfo(); MemoryContextSwitchTo(oldcontext); - appendStringInfoChar(state, '['); + appendStringInfoChar(state->str, '['); + json_categorize_type(arg_type,&state->val_category, + &state->val_output_func); } else { - state = (StringInfo) PG_GETARG_POINTER(0); - appendStringInfoString(state, ", "); + state = (JsonAggState *) PG_GETARG_POINTER(0); + appendStringInfoString(state->str, ", "); } /* fast path for NULLs */ if (PG_ARGISNULL(1)) { - datum_to_json((Datum) 0, true, state, JSONTYPE_NULL, InvalidOid, false); + datum_to_json((Datum) 0, true, state->str, JSONTYPE_NULL, + InvalidOid, false); 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)) + (state->val_category == JSONTYPE_ARRAY || + state->val_category == JSONTYPE_COMPOSITE)) { - appendStringInfoString(state, "\n "); + appendStringInfoString(state->str, "\n "); } - datum_to_json(val, false, state, tcategory, outfuncoid, false); + datum_to_json(val, false, state->str, state->val_category, + state->val_output_func, false); /* * 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. + * pass the JsonAggState pointer through nodeAgg.c's machinations. */ PG_RETURN_POINTER(state); } @@ -1933,19 +1943,21 @@ json_agg_transfn(PG_FUNCTION_ARGS) Datum json_agg_finalfn(PG_FUNCTION_ARGS) { - StringInfo state; + JsonAggState *state; /* cannot be called directly because of internal-type argument */ Assert(AggCheckCallContext(fcinfo, NULL)); - state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); + state = PG_ARGISNULL(0) ? + NULL : + (JsonAggState *) 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, "]")); + PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]")); } /* @@ -1956,10 +1968,9 @@ json_agg_finalfn(PG_FUNCTION_ARGS) Datum json_object_agg_transfn(PG_FUNCTION_ARGS) { - Oid val_type; MemoryContext aggcontext, oldcontext; - StringInfo state; + JsonAggState *state; Datum arg; if (!AggCheckCallContext(fcinfo, &aggcontext)) @@ -1970,6 +1981,8 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) if (PG_ARGISNULL(0)) { + Oid arg_type; + /* * Make the StringInfo in a context where it will persist for the * duration of the aggregate call. Switching context is only needed @@ -1977,15 +1990,36 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) * use the right context to enlarge the object if necessary. */ oldcontext = MemoryContextSwitchTo(aggcontext); - state = makeStringInfo(); + state = (JsonAggState *) palloc(sizeof(JsonAggState)); + state->str = makeStringInfo(); MemoryContextSwitchTo(oldcontext); - appendStringInfoString(state, "{ "); + arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1); + + if (arg_type == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not determine data type for argument 1"))); + + json_categorize_type(arg_type,&state->key_category, + &state->key_output_func); + + arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2); + + if (arg_type == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not determine data type for argument 2"))); + + json_categorize_type(arg_type,&state->val_category, + &state->val_output_func); + + appendStringInfoString(state->str, "{ "); } else { - state = (StringInfo) PG_GETARG_POINTER(0); - appendStringInfoString(state, ", "); + state = (JsonAggState *) PG_GETARG_POINTER(0); + appendStringInfoString(state->str, ", "); } /* @@ -1995,12 +2029,6 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) * type UNKNOWN, which fortunately does not matter to us, since * unknownout() works fine. */ - val_type = get_fn_expr_argtype(fcinfo->flinfo, 1); - - if (val_type == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not determine data type for argument %d", 1))); if (PG_ARGISNULL(1)) ereport(ERROR, @@ -2009,23 +2037,18 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) arg = PG_GETARG_DATUM(1); - add_json(arg, false, state, val_type, true); - - appendStringInfoString(state, " : "); + datum_to_json(arg, false, state->str, state->key_category, + state->key_output_func, true); - val_type = get_fn_expr_argtype(fcinfo->flinfo, 2); - - if (val_type == InvalidOid) - ereport(ERROR, - (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("could not determine data type for argument %d", 2))); + appendStringInfoString(state->str, " : "); if (PG_ARGISNULL(2)) arg = (Datum) 0; else arg = PG_GETARG_DATUM(2); - add_json(arg, PG_ARGISNULL(2), state, val_type, false); + datum_to_json(arg, PG_ARGISNULL(2), state->str, state->val_category, + state->val_output_func, false); PG_RETURN_POINTER(state); } @@ -2036,19 +2059,19 @@ json_object_agg_transfn(PG_FUNCTION_ARGS) Datum json_object_agg_finalfn(PG_FUNCTION_ARGS) { - StringInfo state; + JsonAggState *state; /* cannot be called directly because of internal-type argument */ Assert(AggCheckCallContext(fcinfo, NULL)); - state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0); + state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) 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 object terminator added */ - PG_RETURN_TEXT_P(catenate_stringinfo_string(state, " }")); + PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }")); } /* |