@@ -70,6 +70,17 @@ typedef enum/* required operations on state stack */
7070JSON_STACKOP_POP /* pop, or expect end of input if no stack */
7171}JsonStackOp ;
7272
73+ typedef enum /* type categories for datum_to_json */
74+ {
75+ JSONTYPE_NULL ,/* null, so we didn't bother to identify */
76+ JSONTYPE_BOOL ,/* boolean (built-in types only) */
77+ JSONTYPE_NUMERIC ,/* numeric (ditto) */
78+ JSONTYPE_JSON ,/* JSON itself */
79+ JSONTYPE_ARRAY ,/* array */
80+ JSONTYPE_COMPOSITE ,/* composite */
81+ JSONTYPE_OTHER /* all else */
82+ }JsonTypeCategory ;
83+
7384static void json_validate_cstring (char * input );
7485static void json_lex (JsonLexContext * lex );
7586static void json_lex_string (JsonLexContext * lex );
@@ -82,13 +93,16 @@ static void composite_to_json(Datum composite, StringInfo result,
8293bool use_line_feeds );
8394static void array_dim_to_json (StringInfo result ,int dim ,int ndims ,int * dims ,
8495Datum * vals ,bool * nulls ,int * valcount ,
85- TYPCATEGORY tcategory ,Oid typoutputfunc ,
96+ JsonTypeCategory tcategory ,Oid outfuncoid ,
8697bool use_line_feeds );
8798static void array_to_json_internal (Datum array ,StringInfo result ,
88- bool use_line_feeds );
99+ bool use_line_feeds );
100+ static void json_categorize_type (Oid typoid ,
101+ JsonTypeCategory * tcategory ,
102+ Oid * outfuncoid );
103+ static void datum_to_json (Datum val ,bool is_null ,StringInfo result ,
104+ JsonTypeCategory tcategory ,Oid outfuncoid );
89105
90- /* fake type category for JSON so we can distinguish it in datum_to_json */
91- #define TYPCATEGORY_JSON 'j'
92106/* chars to consider as part of an alphanumeric token */
93107#define JSON_ALPHANUMERIC_CHAR (c ) \
94108(((c) >= 'a' && (c) <= 'z') || \
@@ -816,14 +830,67 @@ extract_mb_char(char *s)
816830}
817831
818832/*
819- * Turn a scalar Datum into JSON, appending the string to "result".
833+ * Determine how we want to print values of a given type in datum_to_json.
834+ *
835+ * Given the datatype OID, return its JsonTypeCategory, as well as the type's
836+ * output function OID. If the returned category is JSONTYPE_CAST, we
837+ * return the OID of the type->JSON cast function instead.
838+ */
839+ static void
840+ json_categorize_type (Oid typoid ,
841+ JsonTypeCategory * tcategory ,
842+ Oid * outfuncoid )
843+ {
844+ bool typisvarlena ;
845+
846+ /*
847+ * We should look through domains here, but we'll wait till 9.4.
848+ */
849+
850+ /* We'll usually need to return the type output function */
851+ getTypeOutputInfo (typoid ,outfuncoid ,& typisvarlena );
852+
853+ /* Check for known types */
854+ switch (typoid )
855+ {
856+ case BOOLOID :
857+ * tcategory = JSONTYPE_BOOL ;
858+ break ;
859+
860+ case INT2OID :
861+ case INT4OID :
862+ case INT8OID :
863+ case FLOAT4OID :
864+ case FLOAT8OID :
865+ case NUMERICOID :
866+ * tcategory = JSONTYPE_NUMERIC ;
867+ break ;
868+
869+ case JSONOID :
870+ * tcategory = JSONTYPE_JSON ;
871+ break ;
872+
873+ default :
874+ /* Check for arrays and composites */
875+ if (OidIsValid (get_element_type (typoid )))
876+ * tcategory = JSONTYPE_ARRAY ;
877+ else if (type_is_rowtype (typoid ))
878+ * tcategory = JSONTYPE_COMPOSITE ;
879+ else
880+ * tcategory = JSONTYPE_OTHER ;
881+ break ;
882+ }
883+ }
884+
885+ /*
886+ * Turn a Datum into JSON text, appending the string to "result".
820887 *
821- *Hand off a non-scalar datum to composite_to_json or array_to_json_internal
822- *as appropriate .
888+ *tcategory and outfuncoid are from a previous call to json_categorize_type,
889+ *except that if is_null is true then they can be invalid .
823890 */
824891static void
825892datum_to_json (Datum val ,bool is_null ,StringInfo result ,
826- TYPCATEGORY tcategory ,Oid typoutputfunc )
893+ JsonTypeCategory tcategory ,Oid outfuncoid )
827894{
828895char * outputstr ;
829896bool numeric_error ;
@@ -837,20 +904,20 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
837904
838905switch (tcategory )
839906{
840- case TYPCATEGORY_ARRAY :
907+ case JSONTYPE_ARRAY :
841908array_to_json_internal (val ,result , false);
842909break ;
843- case TYPCATEGORY_COMPOSITE :
910+ case JSONTYPE_COMPOSITE :
844911composite_to_json (val ,result , false);
845912break ;
846- case TYPCATEGORY_BOOLEAN :
913+ case JSONTYPE_BOOL :
847914if (DatumGetBool (val ))
848915appendStringInfoString (result ,"true" );
849916else
850917appendStringInfoString (result ,"false" );
851918break ;
852- case TYPCATEGORY_NUMERIC :
853- outputstr = OidOutputFunctionCall (typoutputfunc ,val );
919+ case JSONTYPE_NUMERIC :
920+ outputstr = OidOutputFunctionCall (outfuncoid ,val );
854921
855922/*
856923 * Don't call escape_json here if it's a valid JSON number.
@@ -863,14 +930,14 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
863930escape_json (result ,outputstr );
864931pfree (outputstr );
865932break ;
866- case TYPCATEGORY_JSON :
933+ case JSONTYPE_JSON :
867934/* JSON will already be escaped */
868- outputstr = OidOutputFunctionCall (typoutputfunc ,val );
935+ outputstr = OidOutputFunctionCall (outfuncoid ,val );
869936appendStringInfoString (result ,outputstr );
870937pfree (outputstr );
871938break ;
872939default :
873- outputstr = OidOutputFunctionCall (typoutputfunc ,val );
940+ outputstr = OidOutputFunctionCall (outfuncoid ,val );
874941escape_json (result ,outputstr );
875942pfree (outputstr );
876943break ;
@@ -884,8 +951,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
884951 */
885952static void
886953array_dim_to_json (StringInfo result ,int dim ,int ndims ,int * dims ,Datum * vals ,
887- bool * nulls ,int * valcount ,TYPCATEGORY tcategory ,
888- Oid typoutputfunc ,bool use_line_feeds )
954+ bool * nulls ,int * valcount ,JsonTypeCategory tcategory ,
955+ Oid outfuncoid ,bool use_line_feeds )
889956{
890957int i ;
891958const char * sep ;
@@ -904,7 +971,7 @@ array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
904971if (dim + 1 == ndims )
905972{
906973datum_to_json (vals [* valcount ],nulls [* valcount ],result ,tcategory ,
907- typoutputfunc );
974+ outfuncoid );
908975(* valcount )++ ;
909976}
910977else
@@ -914,7 +981,7 @@ array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
914981 * we'll say no.
915982 */
916983array_dim_to_json (result ,dim + 1 ,ndims ,dims ,vals ,nulls ,
917- valcount ,tcategory ,typoutputfunc , false);
984+ valcount ,tcategory ,outfuncoid , false);
918985}
919986}
920987
@@ -937,11 +1004,9 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
9371004bool * nulls ;
9381005int16 typlen ;
9391006bool typbyval ;
940- char typalign ,
941- typdelim ;
942- Oid typioparam ;
943- Oid typoutputfunc ;
944- TYPCATEGORY tcategory ;
1007+ char typalign ;
1008+ JsonTypeCategory tcategory ;
1009+ Oid outfuncoid ;
9451010
9461011ndim = ARR_NDIM (v );
9471012dim = ARR_DIMS (v );
@@ -953,23 +1018,18 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
9531018return ;
9541019}
9551020
956- get_type_io_data (element_type ,IOFunc_output ,
957- & typlen ,& typbyval ,& typalign ,
958- & typdelim ,& typioparam ,& typoutputfunc );
1021+ get_typlenbyvalalign (element_type ,
1022+ & typlen ,& typbyval ,& typalign );
1023+
1024+ json_categorize_type (element_type ,
1025+ & tcategory ,& outfuncoid );
9591026
9601027deconstruct_array (v ,element_type ,typlen ,typbyval ,
9611028typalign ,& elements ,& nulls ,
9621029& nitems );
9631030
964- if (element_type == RECORDOID )
965- tcategory = TYPCATEGORY_COMPOSITE ;
966- else if (element_type == JSONOID )
967- tcategory = TYPCATEGORY_JSON ;
968- else
969- tcategory = TypeCategory (element_type );
970-
9711031array_dim_to_json (result ,0 ,ndim ,dim ,elements ,nulls ,& count ,tcategory ,
972- typoutputfunc ,use_line_feeds );
1032+ outfuncoid ,use_line_feeds );
9731033
9741034pfree (elements );
9751035pfree (nulls );
@@ -1009,13 +1069,11 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
10091069
10101070for (i = 0 ;i < tupdesc -> natts ;i ++ )
10111071{
1012- Datum val ,
1013- origval ;
1072+ Datum val ;
10141073bool isnull ;
10151074char * attname ;
1016- TYPCATEGORY tcategory ;
1017- Oid typoutput ;
1018- bool typisvarlena ;
1075+ JsonTypeCategory tcategory ;
1076+ Oid outfuncoid ;
10191077
10201078if (tupdesc -> attrs [i ]-> attisdropped )
10211079continue ;
@@ -1028,34 +1086,18 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
10281086escape_json (result ,attname );
10291087appendStringInfoChar (result ,':' );
10301088
1031- origval = heap_getattr (tuple ,i + 1 ,tupdesc ,& isnull );
1032-
1033- if (tupdesc -> attrs [i ]-> atttypid == RECORDARRAYOID )
1034- tcategory = TYPCATEGORY_ARRAY ;
1035- else if (tupdesc -> attrs [i ]-> atttypid == RECORDOID )
1036- tcategory = TYPCATEGORY_COMPOSITE ;
1037- else if (tupdesc -> attrs [i ]-> atttypid == JSONOID )
1038- tcategory = TYPCATEGORY_JSON ;
1039- else
1040- tcategory = TypeCategory (tupdesc -> attrs [i ]-> atttypid );
1089+ val = heap_getattr (tuple ,i + 1 ,tupdesc ,& isnull );
10411090
1042- getTypeOutputInfo (tupdesc -> attrs [i ]-> atttypid ,
1043- & typoutput ,& typisvarlena );
1044-
1045- /*
1046- * If we have a toasted datum, forcibly detoast it here to avoid
1047- * memory leakage inside the type's output routine.
1048- */
1049- if (typisvarlena && !isnull )
1050- val = PointerGetDatum (PG_DETOAST_DATUM (origval ));
1091+ if (isnull )
1092+ {
1093+ tcategory = JSONTYPE_NULL ;
1094+ outfuncoid = InvalidOid ;
1095+ }
10511096else
1052- val = origval ;
1053-
1054- datum_to_json (val ,isnull ,result ,tcategory ,typoutput );
1097+ json_categorize_type (tupdesc -> attrs [i ]-> atttypid ,
1098+ & tcategory ,& outfuncoid );
10551099
1056- /* Clean up detoasted copy, if any */
1057- if (val != origval )
1058- pfree (DatumGetPointer (val ));
1100+ datum_to_json (val ,isnull ,result ,tcategory ,outfuncoid );
10591101}
10601102
10611103appendStringInfoChar (result ,'}' );