aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/json.c
diff options
context:
space:
mode:
authorAndrew Dunstan <andrew@dunslane.net>2015-09-18 14:39:39 -0400
committerAndrew Dunstan <andrew@dunslane.net>2015-09-18 14:39:39 -0400
commitc00c3249e3247d24751d97ff6f26603810593414 (patch)
treea07b7ec5ee0b483305ac232a6f16c129ae88447c /src/backend/utils/adt/json.c
parentd9c0c728afe734b7087dbb9a4bc16d5b682d139d (diff)
downloadpostgresql-c00c3249e3247d24751d97ff6f26603810593414.tar.gz
postgresql-c00c3249e3247d24751d97ff6f26603810593414.zip
Cache argument type information in json(b) aggregate functions.
These functions have been looking up type info for every row they process. Instead of doing that we only look them up the first time through and stash the information in the aggregate state object. Affects json_agg, json_object_agg, jsonb_agg and jsonb_object_agg. There is plenty more work to do in making these more efficient, especially the jsonb functions, but this is a virtually cost free improvement that can be done right away. Backpatch to 9.5 where the jsonb variants were introduced.
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, " }"));
}
/*