@@ -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+
7180static inline void json_lex (JsonLexContext * lex );
7281static inline void json_lex_string (JsonLexContext * lex );
7382static inline void json_lex_number (JsonLexContext * lex , char * s , bool * num_err );
@@ -1858,18 +1867,10 @@ to_json(PG_FUNCTION_ARGS)
18581867Datum
18591868json_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)
19331943Datum
19341944json_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)
19561968Datum
19571969json_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)
20362059Datum
20372060json_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