@@ -68,6 +68,15 @@ typedef enum/* type categories for datum_to_json */
6868JSONTYPE_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 );
18621870MemoryContext aggcontext ,
18631871oldcontext ;
1864- StringInfo state ;
1872+ JsonAggState * state ;
18651873Datum 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
18741875if (!AggCheckCallContext (fcinfo ,& aggcontext ))
18751876{
@@ -1879,50 +1880,59 @@ json_agg_transfn(PG_FUNCTION_ARGS)
18791880
18801881if (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 thisStringInfo in a context where it will persist for the
1891+ * Make thisstate 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 */
18881896oldcontext = MemoryContextSwitchTo (aggcontext );
1889- state = makeStringInfo ();
1897+ state = (JsonAggState * )palloc (sizeof (JsonAggState ));
1898+ state -> str = makeStringInfo ();
18901899MemoryContextSwitchTo (oldcontext );
18911900
1892- appendStringInfoChar (state ,'[' );
1901+ appendStringInfoChar (state -> str ,'[' );
1902+ json_categorize_type (arg_type ,& state -> val_category ,
1903+ & state -> val_output_func );
18931904}
18941905else
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 */
19011912if (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);
19041916PG_RETURN_POINTER (state );
19051917}
19061918
19071919val = 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 */
19141922if (!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 theArrayBuildState pointer through nodeAgg.c's machinations.
1935+ * pass theJsonAggState pointer through nodeAgg.c's machinations.
19261936 */
19271937PG_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 */
19391949Assert (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 */
19441956if (state == NULL )
19451957PG_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 ;
19601971MemoryContext aggcontext ,
19611972oldcontext ;
1962- StringInfo state ;
1973+ JsonAggState * state ;
19631974Datum arg ;
19641975
19651976if (!AggCheckCallContext (fcinfo ,& aggcontext ))
@@ -1970,22 +1981,45 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
19701981
19711982if (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 */
19791992oldcontext = MemoryContextSwitchTo (aggcontext );
1980- state = makeStringInfo ();
1993+ state = (JsonAggState * )palloc (sizeof (JsonAggState ));
1994+ state -> str = makeStringInfo ();
19811995MemoryContextSwitchTo (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}
19852019else
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
20052033if (PG_ARGISNULL (1 ))
20062034ereport (ERROR ,
@@ -2009,23 +2037,18 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
20092037
20102038arg = 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
20232045if (PG_ARGISNULL (2 ))
20242046arg = (Datum )0 ;
20252047else
20262048arg = 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
20302053PG_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 */
20422065Assert (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 */
20472070if (state == NULL )
20482071PG_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/*