8000 Get rid of bogus dependency on typcategory in to_json() and friends. · larkly/postgres-docker@25c933c · GitHub
[go: up one dir, main page]

Skip to content

Commit 25c933c

Browse files
committed
Get rid of bogus dependency on typcategory in to_json() and friends.
These functions were relying on typcategory to identify arrays and composites, which is not reliable and not the normal way to do it. Using typcategory to identify boolean, numeric types, and json itself is also pretty questionable, though the code in those cases didn't seem to be at risk of anything worse than wrong output. Instead, use the standard lsyscache functions to identify arrays and composites, and rely on a direct check of the type OID for the other cases. In HEAD, also be sure to look through domains so that a domain is treated the same as its base type for conversions to JSON. However, this is a small behavioral change; given the lack of field complaints, we won't back-patch it. In passing, refactor so that there's only one copy of the code that decides which conversion strategy to apply, not multiple copies that could (and have) gotten out of sync.
1 parent 9012022 commit 25c933c

File tree

1 file changed

+108
-66
lines changed

1 file changed

+108
-66
lines changed

src/backend/utils/adt/json.c

Lines changed: 108 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ typedef enum /* required operations on state stack */
7070
JSON_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+
7384
static void json_validate_cstring(char *input);
7485
static void json_lex(JsonLexContext *lex);
7586
static void json_lex_string(JsonLexContext *lex);
@@ -82,13 +93,16 @@ static void composite_to_json(Datum composite, StringInfo result,
8293
bool use_line_feeds);
8394
static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
8495
Datum *vals, bool *nulls, int *valcount,
85-
TYPCATEGORY tcategory, Oid typoutputfunc,
96+
JsonTypeCategory tcategory, Oid outfuncoid,
8697
bool use_line_feeds);
8798
static 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
*/
824891
static void
825892
datum_to_json(Datum val, bool is_null, StringInfo result,
826-
TYPCATEGORY tcategory, Oid typoutputfunc)
893+
JsonTypeCategory tcategory, Oid outfuncoid)
827894
{
828895
char *outputstr;
829896
bool numeric_error;
@@ -837,20 +904,20 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
837904

838905
switch (tcategory)
839906
{
840-
case TYPCATEGORY_ARRAY:
907+
case JSONTYPE_ARRAY:
841908
array_to_json_internal(val, result, false);
842909
break;
843-
case TYPCATEGORY_COMPOSITE:
910+
case JSONTYPE_COMPOSITE:
844911
composite_to_json(val, result, false);
845912
break;
846-
case TYPCATEGORY_BOOLEAN:
913+
case JSONTYPE_BOOL:
847914
if (DatumGetBool(val))
848915
appendStringInfoString(result, "true");
849916
else
850917
appendStringInfoString(result, "false");
851918
break;
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,
863930
escape_json(result, outputstr);
864931
pfree(outputstr);
865932
break;
866-
case TYPCATEGORY_JSON:
933+
case JSONTYPE_JSON:
867934
/* JSON will already be escaped */
868-
outputstr = OidOutputFunctionCall(typoutputfunc, val);
935+
outputstr = OidOutputFunctionCall(outfuncoid, val);
869936
appendStringInfoString(result, outputstr);
870937
pfree(outputstr);
871938
break;
872939
default:
873-
outputstr = OidOutputFunctionCall(typoutputfunc, val);
940+
outputstr = OidOutputFunctionCall(outfuncoid, val);
874941
escape_json(result, outputstr);
875942
pfree(outputstr);
876943
break;
@@ -884,8 +951,8 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
884951
*/
885952
static void
886953
array_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
{
890957
int i;
891958
const char *sep;
@@ -904,7 +971,7 @@ array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
904971
if (dim + 1 == ndims)
905972
{
906973
datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
907-
typoutputfunc);
974+
outfuncoid);
908975
(*valcount)++;
909976
}
910977
else
@@ -914,7 +981,7 @@ array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
914981
* we'll say no.
915982
*/
916983
array_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)
9371004
bool *nulls;
9381005
int16 typlen;
9391006
bool 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

9461011
ndim = ARR_NDIM(v);
9471012
dim = ARR_DIMS(v);
@@ -953,23 +1018,18 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
9531018
return;
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

9601027
deconstruct_array(v, element_type, typlen, typbyval,
9611028
typalign, &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-
9711031
array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
972-
typoutputfunc, use_line_feeds);
1032+
outfuncoid, use_line_feeds);
9731033

9741034
pfree(elements);
9751035
pfree(nulls);
@@ -1009,13 +1069,11 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
10091069

10101070
for (i = 0; i < tupdesc->natts; i++)
10111071
{
1012-
Datum val,
1013-
origval;
1072+
Datum val;
10141073
bool isnull;
10151074
char *attname;
1016-
TYPCATEGORY tcategory;
1017-
Oid typoutput;
1018-
bool typisvarlena;
1075+
JsonTypeCategory tcategory;
1076+
Oid outfuncoid;
10191077

10201078
if (tupdesc->attrs[i]->attisdropped)
10211079
continue;
@@ -1028,34 +1086,18 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
10281086
escape_json(result, attname);
10291087
appendStringInfoChar(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+
}
10511096
else
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

10611103
appendStringInfoChar(result, '}');

0 commit comments

Comments
 (0)
0