@@ -68,6 +68,15 @@ typedef enum /* type categories for datum_to_json */
68
68
JSONTYPE_OTHER /* all else */
69
69
} JsonTypeCategory ;
70
70
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
+
71
80
static inline void json_lex (JsonLexContext * lex );
72
81
static inline void json_lex_string (JsonLexContext * lex );
73
82
static inline void json_lex_number (JsonLexContext * lex , char * s , bool * num_err );
@@ -1858,18 +1867,10 @@ to_json(PG_FUNCTION_ARGS)
1858
1867
Datum
1859
1868
json_agg_transfn (PG_FUNCTION_ARGS )
1860
1869
{
1861
- Oid val_type = get_fn_expr_argtype (fcinfo -> flinfo , 1 );
1862
1870
MemoryContext aggcontext ,
1863
1871
oldcontext ;
1864
- StringInfo state ;
1872
+ JsonAggState * state ;
1865
1873
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" )));
1873
1874
1874
1875
if (!AggCheckCallContext (fcinfo , & aggcontext ))
1875
1876
{
@@ -1879,50 +1880,59 @@ json_agg_transfn(PG_FUNCTION_ARGS)
1879
1880
1880
1881
if (PG_ARGISNULL (0 ))
1881
1882
{
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
+
1882
1890
/*
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
1884
1892
* duration of the aggregate call. MemoryContextSwitchTo is only
1885
1893
* needed the first time, as the StringInfo routines make sure they
1886
1894
* use the right context to enlarge the object if necessary.
1887
1895
*/
1888
1896
oldcontext = MemoryContextSwitchTo (aggcontext );
1889
- state = makeStringInfo ();
1897
+ state = (JsonAggState * ) palloc (sizeof (JsonAggState ));
1898
+ state -> str = makeStringInfo ();
1890
1899
MemoryContextSwitchTo (oldcontext );
1891
1900
1892
- appendStringInfoChar (state , '[' );
1901
+ appendStringInfoChar (state -> str , '[' );
1902
+ json_categorize_type (arg_type ,& state -> val_category ,
1903
+ & state -> val_output_func );
1893
1904
}
1894
1905
else
1895
1906
{
1896
- state = (StringInfo ) PG_GETARG_POINTER (0 );
1897
- appendStringInfoString (state , ", " );
1907
+ state = (JsonAggState * ) PG_GETARG_POINTER (0 );
1908
+ appendStringInfoString (state -> str , ", " );
1898
1909
}
1899
1910
1900
1911
/* fast path for NULLs */
1901
1912
if (PG_ARGISNULL (1 ))
1902
1913
{
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);
1904
1916
PG_RETURN_POINTER (state );
1905
1917
}
1906
1918
1907
1919
val = PG_GETARG_DATUM (1 );
1908
1920
1909
- /* XXX we do this every time?? */
1910
- json_categorize_type (val_type ,
1911
- & tcategory , & outfuncoid );
1912
-
1913
1921
/* add some whitespace if structured type and not first item */
1914
1922
if (!PG_ARGISNULL (0 ) &&
1915
- (tcategory == JSONTYPE_ARRAY || tcategory == JSONTYPE_COMPOSITE ))
1923
+ (state -> val_category == JSONTYPE_ARRAY ||
1924
+ state -> val_category == JSONTYPE_COMPOSITE ))
1916
1925
{
1917
- appendStringInfoString (state , "\n " );
1926
+ appendStringInfoString (state -> str , "\n " );
1918
1927
}
1919
1928
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);
1921
1931
1922
1932
/*
1923
1933
* The transition type for array_agg() is declared to be "internal", which
1924
1934
* 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.
1926
1936
*/
1927
1937
PG_RETURN_POINTER (state );
1928
1938
}
@@ -1933,19 +1943,21 @@ json_agg_transfn(PG_FUNCTION_ARGS)
1933
1943
Datum
1934
1944
json_agg_finalfn (PG_FUNCTION_ARGS )
1935
1945
{
1936
- StringInfo state ;
1946
+ JsonAggState * state ;
1937
1947
1938
1948
/* cannot be called directly because of internal-type argument */
1939
1949
Assert (AggCheckCallContext (fcinfo , NULL ));
1940
1950
1941
- state = PG_ARGISNULL (0 ) ? NULL : (StringInfo ) PG_GETARG_POINTER (0 );
1951
+ state = PG_ARGISNULL (0 ) ?
1952
+ NULL :
1953
+ (JsonAggState * ) PG_GETARG_POINTER (0 );
1942
1954
1943
1955
/* NULL result for no rows in, as is standard with aggregates */
1944
1956
if (state == NULL )
1945
1957
PG_RETURN_NULL ();
1946
1958
1947
1959
/* 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 , "]" ));
1949
1961
}
1950
1962
1951
1963
/*
@@ -1956,10 +1968,9 @@ json_agg_finalfn(PG_FUNCTION_ARGS)
1956
1968
Datum
1957
1969
json_object_agg_transfn (PG_FUNCTION_ARGS )
1958
1970
{
1959
- Oid val_type ;
1960
1971
MemoryContext aggcontext ,
1961
1972
oldcontext ;
1962
- StringInfo state ;
1973
+ JsonAggState * state ;
1963
1974
Datum arg ;
1964
1975
1965
1976
if (!AggCheckCallContext (fcinfo , & aggcontext ))
@@ -1970,22 +1981,45 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
1970
1981
1971
1982
if (PG_ARGISNULL (0 ))
1972
1983
{
1984
+ Oid arg_type ;
1985
+
1973
1986
/*
1974
1987
* Make the StringInfo in a context where it will persist for the
1975
1988
* duration of the aggregate call. Switching context is only needed
1976
1989
* for this initial step, as the StringInfo routines make sure they
1977
1990
* use the right context to enlarge the object if necessary.
1978
1991
*/
1979
1992
oldcontext = MemoryContextSwitchTo (aggcontext );
1980
- state = makeStringInfo ();
1993
+ state = (JsonAggState * ) palloc (sizeof (JsonAggState ));
1994
+ state -> str = makeStringInfo ();
1981
1995
MemoryContextSwitchTo (oldcontext );
1982
1996
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 , "{ " );
1984
2018
}
1985
2019
else
1986
2020
{
1987
- state = (StringInfo ) PG_GETARG_POINTER (0 );
1988
- appendStringInfoString (state , ", " );
2021
+ state = (JsonAggState * ) PG_GETARG_POINTER (0 );
2022
+ appendStringInfoString (state -> str , ", " );
1989
2023
}
1990
2024
1991
2025
/*
@@ -1995,12 +2029,6 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
1995
2029
* type UNKNOWN, which fortunately does not matter to us, since
1996
2030
* unknownout() works fine.
1997
2031
*/
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 )));
2004
2032
2005
2033
if (PG_ARGISNULL (1 ))
2006
2034
ereport (ERROR ,
@@ -2009,23 +2037,18 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
2009
2037
2010
2038
arg = PG_GETARG_DATUM (1 );
2011
2039
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);
2015
2042
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 , " : " );
2022
2044
2023
2045
if (PG_ARGISNULL (2 ))
2024
2046
arg = (Datum ) 0 ;
2025
2047
else
2026
2048
arg = PG_GETARG_DATUM (2 );
2027
2049
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);
2029
2052
2030
2053
PG_RETURN_POINTER (state );
2031
2054
}
@@ -2036,19 +2059,19 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
2036
2059
Datum
2037
2060
json_object_agg_finalfn (PG_FUNCTION_ARGS )
2038
2061
{
2039
- StringInfo state ;
2062
+ JsonAggState * state ;
2040
2063
2041
2064
/* cannot be called directly because of internal-type argument */
2042
2065
Assert (AggCheckCallContext (fcinfo , NULL ));
2043
2066
2044
- state = PG_ARGISNULL (0 ) ? NULL : (StringInfo ) PG_GETARG_POINTER (0 );
2067
+ state = PG_ARGISNULL (0 ) ? NULL : (JsonAggState * ) PG_GETARG_POINTER (0 );
2045
2068
2046
2069
/* NULL result for no rows in, as is standard with aggregates */
2047
2070
if (state == NULL )
2048
2071
PG_RETURN_NULL ();
2049
2072
2050
2073
/* 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 , " }" ));
2052
2075
}
2053
2076
2054
2077
/*
0 commit comments