JSON_STACKOP_POP /* pop, or expect end of input if no stack */
} JsonStackOp;
+typedef enum /* type categories for datum_to_json */
+{
+ JSONTYPE_NULL, /* null, so we didn't bother to identify */
+ JSONTYPE_BOOL, /* boolean (built-in types only) */
+ JSONTYPE_NUMERIC, /* numeric (ditto) */
+ JSONTYPE_JSON, /* JSON itself */
+ JSONTYPE_ARRAY, /* array */
+ JSONTYPE_COMPOSITE, /* composite */
+ JSONTYPE_OTHER /* all else */
+} JsonTypeCategory;
+
static void json_validate_cstring(char *input);
static void json_lex(JsonLexContext *lex);
static void json_lex_string(JsonLexContext *lex);
bool use_line_feeds);
static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
Datum *vals, bool *nulls, int *valcount,
- TYPCATEGORY tcategory, Oid typoutputfunc,
+ JsonTypeCategory tcategory, Oid outfuncoid,
bool use_line_feeds);
static void array_to_json_internal(Datum array, StringInfo result,
- bool use_line_feeds);
+ bool use_line_feeds);
+static void json_categorize_type(Oid typoid,
+ JsonTypeCategory *tcategory,
+ Oid *outfuncoid);
+static void datum_to_json(Datum val, bool is_null, StringInfo result,
+ JsonTypeCategory tcategory, Oid outfuncoid);
-/* fake type category for JSON so we can distinguish it in datum_to_json */
-#define TYPCATEGORY_JSON 'j'
/* chars to consider as part of an alphanumeric token */
#define JSON_ALPHANUMERIC_CHAR(c) \
(((c) >= 'a' && (c) <= 'z') || \
}
/*
- * Turn a scalar Datum into JSON, appending the string to "result".
+ * Determine how we want to print values of a given type in datum_to_json.
+ *
+ * Given the datatype OID, return its JsonTypeCategory, as well as the type's
+ * output function OID. If the returned category is JSONTYPE_CAST, we
+ * return the OID of the type->JSON cast function instead.
+ */
+static void
+json_categorize_type(Oid typoid,
+ JsonTypeCategory *tcategory,
+ Oid *outfuncoid)
+{
+ bool typisvarlena;
+
+ /*
+ * We should look through domains here, but we'll wait till 9.4.
+ */
+
+ /* We'll usually need to return the type output function */
+ getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+
+ /* Check for known types */
+ switch (typoid)
+ {
+ case BOOLOID:
+ *tcategory = JSONTYPE_BOOL;
+ break;
+
+ case INT2OID:
+ case INT4OID:
+ case INT8OID:
+ case FLOAT4OID:
+ case FLOAT8OID:
+ case NUMERICOID:
+ *tcategory = JSONTYPE_NUMERIC;
+ break;
+
+ case JSONOID:
+ *tcategory = JSONTYPE_JSON;
+ break;
+
+ default:
+ /* Check for arrays and composites */
+ if (OidIsValid(get_element_type(typoid)))
+ *tcategory = JSONTYPE_ARRAY;
+ else if (type_is_rowtype(typoid))
+ *tcategory = JSONTYPE_COMPOSITE;
+ else
+ *tcategory = JSONTYPE_OTHER;
+ break;
+ }
+}
+
+/*
+ * Turn a Datum into JSON text, appending the string to "result".
*
- * Hand off a non-scalar datum to composite_to_json or array_to_json_internal
- * as appropriate.
+ * tcategory and outfuncoid are from a previous call to json_categorize_type,
+ * except that if is_null is true then they can be invalid.
*/
static void
datum_to_json(Datum val, bool is_null, StringInfo result,
- TYPCATEGORY tcategory, Oid typoutputfunc)
+ JsonTypeCategory tcategory, Oid outfuncoid)
{
char *outputstr;
bool numeric_error;
switch (tcategory)
{
- case TYPCATEGORY_ARRAY:
+ case JSONTYPE_ARRAY:
array_to_json_internal(val, result, false);
break;
- case TYPCATEGORY_COMPOSITE:
+ case JSONTYPE_COMPOSITE:
composite_to_json(val, result, false);
break;
- case TYPCATEGORY_BOOLEAN:
+ case JSONTYPE_BOOL:
if (DatumGetBool(val))
appendStringInfoString(result, "true");
else
appendStringInfoString(result, "false");
break;
- case TYPCATEGORY_NUMERIC:
- outputstr = OidOutputFunctionCall(typoutputfunc, val);
+ case JSONTYPE_NUMERIC:
+ outputstr = OidOutputFunctionCall(outfuncoid, val);
/*
* Don't call escape_json here if it's a valid JSON number.
escape_json(result, outputstr);
pfree(outputstr);
break;
- case TYPCATEGORY_JSON:
+ case JSONTYPE_JSON:
/* JSON will already be escaped */
- outputstr = OidOutputFunctionCall(typoutputfunc, val);
+ outputstr = OidOutputFunctionCall(outfuncoid, val);
appendStringInfoString(result, outputstr);
pfree(outputstr);
break;
default:
- outputstr = OidOutputFunctionCall(typoutputfunc, val);
+ outputstr = OidOutputFunctionCall(outfuncoid, val);
escape_json(result, outputstr);
pfree(outputstr);
break;
*/
static void
array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
- bool *nulls, int *valcount, TYPCATEGORY tcategory,
- Oid typoutputfunc, bool use_line_feeds)
+ bool *nulls, int *valcount, JsonTypeCategory tcategory,
+ Oid outfuncoid, bool use_line_feeds)
{
int i;
const char *sep;
if (dim + 1 == ndims)
{
datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
- typoutputfunc);
+ outfuncoid);
(*valcount)++;
}
else
* we'll say no.
*/
array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
- valcount, tcategory, typoutputfunc, false);
+ valcount, tcategory, outfuncoid, false);
}
}
bool *nulls;
int16 typlen;
bool typbyval;
- char typalign,
- typdelim;
- Oid typioparam;
- Oid typoutputfunc;
- TYPCATEGORY tcategory;
+ char typalign;
+ JsonTypeCategory tcategory;
+ Oid outfuncoid;
ndim = ARR_NDIM(v);
dim = ARR_DIMS(v);
return;
}
- get_type_io_data(element_type, IOFunc_output,
- &typlen, &typbyval, &typalign,
- &typdelim, &typioparam, &typoutputfunc);
+ get_typlenbyvalalign(element_type,
+ &typlen, &typbyval, &typalign);
+
+ json_categorize_type(element_type,
+ &tcategory, &outfuncoid);
deconstruct_array(v, element_type, typlen, typbyval,
typalign, &elements, &nulls,
&nitems);
- if (element_type == RECORDOID)
- tcategory = TYPCATEGORY_COMPOSITE;
- else if (element_type == JSONOID)
- tcategory = TYPCATEGORY_JSON;
- else
- tcategory = TypeCategory(element_type);
-
array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
- typoutputfunc, use_line_feeds);
+ outfuncoid, use_line_feeds);
pfree(elements);
pfree(nulls);
for (i = 0; i < tupdesc->natts; i++)
{
- Datum val,
- origval;
+ Datum val;
bool isnull;
char *attname;
- TYPCATEGORY tcategory;
- Oid typoutput;
- bool typisvarlena;
+ JsonTypeCategory tcategory;
+ Oid outfuncoid;
if (tupdesc->attrs[i]->attisdropped)
continue;
escape_json(result, attname);
appendStringInfoChar(result, ':');
- origval = heap_getattr(tuple, i + 1, tupdesc, &isnull);
-
- if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
- tcategory = TYPCATEGORY_ARRAY;
- else if (tupdesc->attrs[i]->atttypid == RECORDOID)
- tcategory = TYPCATEGORY_COMPOSITE;
- else if (tupdesc->attrs[i]->atttypid == JSONOID)
- tcategory = TYPCATEGORY_JSON;
- else
- tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
+ val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
- getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
- &typoutput, &typisvarlena);
-
- /*
- * If we have a toasted datum, forcibly detoast it here to avoid
- * memory leakage inside the type's output routine.
- */
- if (typisvarlena && !isnull)
- val = PointerGetDatum(PG_DETOAST_DATUM(origval));
+ if (isnull)
+ {
+ tcategory = JSONTYPE_NULL;
+ outfuncoid = InvalidOid;
+ }
else
- val = origval;
-
- datum_to_json(val, isnull, result, tcategory, typoutput);
+ json_categorize_type(tupdesc->attrs[i]->atttypid,
+ &tcategory, &outfuncoid);
- /* Clean up detoasted copy, if any */
- if (val != origval)
- pfree(DatumGetPointer(val));
+ datum_to_json(val, isnull, result, tcategory, outfuncoid);
}
appendStringInfoChar(result, '}');