aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/json.c')
-rw-r--r--src/backend/utils/adt/json.c125
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, " }"));
}
/*