Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c00c324

Browse files
committed
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.
1 parent d9c0c72 commit c00c324

File tree

2 files changed

+173
-133
lines changed

2 files changed

+173
-133
lines changed

src/backend/utils/adt/json.c

+74-51
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,15 @@ typedef enum /* type categories for datum_to_json */
6868
JSONTYPE_OTHER /* all else */
6969
} JsonTypeCategory;
7070

71+
typedef struct JsonAggState
72+
{
73+
StringInfo str;
74+
JsonTypeCategory key_category;
75+
Oid key_output_func;
76+
JsonTypeCategory val_category;
77+
Oid val_output_func;
78+
} JsonAggState;
79+
7180
static inline void json_lex(JsonLexContext *lex);
7281
static inline void json_lex_string(JsonLexContext *lex);
7382
static inline void json_lex_number(JsonLexContext *lex, char *s, bool *num_err);
@@ -1858,18 +1867,10 @@ to_json(PG_FUNCTION_ARGS)
18581867
Datum
18591868
json_agg_transfn(PG_FUNCTION_ARGS)
18601869
{
1861-
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
18621870
MemoryContext aggcontext,
18631871
oldcontext;
1864-
StringInfo state;
1872+
JsonAggState *state;
18651873
Datum val;
1866-
JsonTypeCategory tcategory;
1867-
Oid outfuncoid;
1868-
1869-
if (val_type == InvalidOid)
1870-
ereport(ERROR,
1871-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1872-
errmsg("could not determine input data type")));
18731874

18741875
if (!AggCheckCallContext(fcinfo, &aggcontext))
18751876
{
@@ -1879,50 +1880,59 @@ json_agg_transfn(PG_FUNCTION_ARGS)
18791880

18801881
if (PG_ARGISNULL(0))
18811882
{
1883+
Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1884+
1885+
if (arg_type == InvalidOid)
1886+
ereport(ERROR,
1887+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1888+
errmsg("could not determine input data type")));
1889+
18821890
/*
1883-
* Make this StringInfo in a context where it will persist for the
1891+
* Make this state object in a context where it will persist for the
18841892
* duration of the aggregate call. MemoryContextSwitchTo is only
18851893
* needed the first time, as the StringInfo routines make sure they
18861894
* use the right context to enlarge the object if necessary.
18871895
*/
18881896
oldcontext = MemoryContextSwitchTo(aggcontext);
1889-
state = makeStringInfo();
1897+
state = (JsonAggState *) palloc(sizeof(JsonAggState));
1898+
state->str = makeStringInfo();
18901899
MemoryContextSwitchTo(oldcontext);
18911900

1892-
appendStringInfoChar(state, '[');
1901+
appendStringInfoChar(state->str, '[');
1902+
json_categorize_type(arg_type,&state->val_category,
1903+
&state->val_output_func);
18931904
}
18941905
else
18951906
{
1896-
state = (StringInfo) PG_GETARG_POINTER(0);
1897-
appendStringInfoString(state, ", ");
1907+
state = (JsonAggState *) PG_GETARG_POINTER(0);
1908+
appendStringInfoString(state->str, ", ");
18981909
}
18991910

19001911
/* fast path for NULLs */
19011912
if (PG_ARGISNULL(1))
19021913
{
1903-
datum_to_json((Datum) 0, true, state, JSONTYPE_NULL, InvalidOid, false);
1914+
datum_to_json((Datum) 0, true, state->str, JSONTYPE_NULL,
1915+
InvalidOid, false);
19041916
PG_RETURN_POINTER(state);
19051917
}
19061918

19071919
val = PG_GETARG_DATUM(1);
19081920

1909-
/* XXX we do this every time?? */
1910-
json_categorize_type(val_type,
1911-
&tcategory, &outfuncoid);
1912-
19131921
/* add some whitespace if structured type and not first item */
19141922
if (!PG_ARGISNULL(0) &&
1915-
(tcategory == JSONTYPE_ARRAY || tcategory == JSONTYPE_COMPOSITE))
1923+
(state->val_category == JSONTYPE_ARRAY ||
1924+
state->val_category == JSONTYPE_COMPOSITE))
19161925
{
1917-
appendStringInfoString(state, "\n ");
1926+
appendStringInfoString(state->str, "\n ");
19181927
}
19191928

1920-
datum_to_json(val, false, state, tcategory, outfuncoid, false);
1929+
datum_to_json(val, false, state->str, state->val_category,
1930+
state->val_output_func, false);
19211931

19221932
/*
19231933
* The transition type for array_agg() is declared to be "internal", which
19241934
* is a pass-by-value type the same size as a pointer. So we can safely
1925-
* pass the ArrayBuildState pointer through nodeAgg.c's machinations.
1935+
* pass the JsonAggState pointer through nodeAgg.c's machinations.
19261936
*/
19271937
PG_RETURN_POINTER(state);
19281938
}
@@ -1933,19 +1943,21 @@ json_agg_transfn(PG_FUNCTION_ARGS)
19331943
Datum
19341944
json_agg_finalfn(PG_FUNCTION_ARGS)
19351945
{
1936-
StringInfo state;
1946+
JsonAggState *state;
19371947

19381948
/* cannot be called directly because of internal-type argument */
19391949
Assert(AggCheckCallContext(fcinfo, NULL));
19401950

1941-
state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
1951+
state = PG_ARGISNULL(0) ?
1952+
NULL :
1953+
(JsonAggState *) PG_GETARG_POINTER(0);
19421954

19431955
/* NULL result for no rows in, as is standard with aggregates */
19441956
if (state == NULL)
19451957
PG_RETURN_NULL();
19461958

19471959
/* Else return state with appropriate array terminator added */
1948-
PG_RETURN_TEXT_P(catenate_stringinfo_string(state, "]"));
1960+
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
19491961
}
19501962

19511963
/*
@@ -1956,10 +1968,9 @@ json_agg_finalfn(PG_FUNCTION_ARGS)
19561968
Datum
19571969
json_object_agg_transfn(PG_FUNCTION_ARGS)
19581970
{
1959-
Oid val_type;
19601971
MemoryContext aggcontext,
19611972
oldcontext;
1962-
StringInfo state;
1973+
JsonAggState *state;
19631974
Datum arg;
19641975

19651976
if (!AggCheckCallContext(fcinfo, &aggcontext))
@@ -1970,22 +1981,45 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
19701981

19711982
if (PG_ARGISNULL(0))
19721983
{
1984+
Oid arg_type;
1985+
19731986
/*
19741987
* Make the StringInfo in a context where it will persist for the
19751988
* duration of the aggregate call. Switching context is only needed
19761989
* for this initial step, as the StringInfo routines make sure they
19771990
* use the right context to enlarge the object if necessary.
19781991
*/
19791992
oldcontext = MemoryContextSwitchTo(aggcontext);
1980-
state = makeStringInfo();
1993+
state = (JsonAggState *) palloc(sizeof(JsonAggState));
1994+
state->str = makeStringInfo();
19811995
MemoryContextSwitchTo(oldcontext);
19821996

1983-
appendStringInfoString(state, "{ ");
1997+
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1998+
1999+
if (arg_type == InvalidOid)
2000+
ereport(ERROR,
2001+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2002+
errmsg("could not determine data type for argument 1")));
2003+
2004+
json_categorize_type(arg_type,&state->key_category,
2005+
&state->key_output_func);
2006+
2007+
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
2008+
2009+
if (arg_type == InvalidOid)
2010+
ereport(ERROR,
2011+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2012+
errmsg("could not determine data type for argument 2")));
2013+
2014+
json_categorize_type(arg_type,&state->val_category,
2015+
&state->val_output_func);
2016+
2017+
appendStringInfoString(state->str, "{ ");
19842018
}
19852019
else
19862020
{
1987-
state = (StringInfo) PG_GETARG_POINTER(0);
1988-
appendStringInfoString(state, ", ");
2021+
state = (JsonAggState *) PG_GETARG_POINTER(0);
2022+
appendStringInfoString(state->str, ", ");
19892023
}
19902024

19912025
/*
@@ -1995,12 +2029,6 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
19952029
* type UNKNOWN, which fortunately does not matter to us, since
19962030
* unknownout() works fine.
19972031
*/
1998-
val_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1999-
2000-
if (val_type == InvalidOid)
2001-
ereport(ERROR,
2002-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2003-
errmsg("could not determine data type for argument %d", 1)));
20042032

20052033
if (PG_ARGISNULL(1))
20062034
ereport(ERROR,
@@ -2009,23 +2037,18 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
20092037

20102038
arg = PG_GETARG_DATUM(1);
20112039

2012-
add_json(arg, false, state, val_type, true);
2013-
2014-
appendStringInfoString(state, " : ");
2040+
datum_to_json(arg, false, state->str, state->key_category,
2041+
state->key_output_func, true);
20152042

2016-
val_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
2017-
2018-
if (val_type == InvalidOid)
2019-
ereport(ERROR,
2020-
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2021-
errmsg("could not determine data type for argument %d", 2)));
2043+
appendStringInfoString(state->str, " : ");
20222044

20232045
if (PG_ARGISNULL(2))
20242046
arg = (Datum) 0;
20252047
else
20262048
arg = PG_GETARG_DATUM(2);
20272049

2028-
add_json(arg, PG_ARGISNULL(2), state, val_type, false);
2050+
datum_to_json(arg, PG_ARGISNULL(2), state->str, state->val_category,
2051+
state->val_output_func, false);
20292052

20302053
PG_RETURN_POINTER(state);
20312054
}
@@ -2036,19 +2059,19 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
20362059
Datum
20372060
json_object_agg_finalfn(PG_FUNCTION_ARGS)
20382061
{
2039-
StringInfo state;
2062+
JsonAggState *state;
20402063

20412064
/* cannot be called directly because of internal-type argument */
20422065
Assert(AggCheckCallContext(fcinfo, NULL));
20432066

2044-
state = PG_ARGISNULL(0) ? NULL : (StringInfo) PG_GETARG_POINTER(0);
2067+
state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
20452068

20462069
/* NULL result for no rows in, as is standard with aggregates */
20472070
if (state == NULL)
20482071
PG_RETURN_NULL();
20492072

20502073
/* Else return state with appropriate object terminator added */
2051-
PG_RETURN_TEXT_P(catenate_stringinfo_string(state, " }"));
2074+
PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
20522075
}
20532076

20542077
/*

0 commit comments

Comments
 (0)