Commits

Andrew Dunstan committed 5ba8901

working use of type casts in *to_json functions.

Comments (0)

Files changed (1)

src/backend/utils/adt/json.c

 #include "postgres.h"
 
 #include "access/htup_details.h"
+#include "access/transam.h"
+#include "catalog/pg_cast.h"
 #include "catalog/pg_type.h"
 #include "executor/spi.h"
 #include "lib/stringinfo.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/json.h"
+#include "utils/syscache.h"
 #include "utils/typcache.h"
 
 typedef enum					/* types of JSON values */
 static void array_to_json_internal(Datum array, StringInfo result,
 								   bool use_line_feeds);
 
+/* 
+ * All the defined  type categories are upper case , so use lower case here
+ * so we avoid any possible clash.
+ */
 /* fake type category for JSON so we can distinguish it in datum_to_json */
 #define TYPCATEGORY_JSON 'j'
+/* fake category for types that have a cast to json */
+#define TYPCATEGORY_JSON_CAST 'c'
 /* letters appearing in numeric output that aren't valid in a JSON number */
 #define NON_NUMERIC_LETTER "NnAaIiFfTtYy"
 /* chars to consider as part of an alphanumeric token */
 			  TYPCATEGORY tcategory, Oid typoutputfunc)
 {
 	char	   *outputstr;
+	text       *jsontext;
 
 	if (is_null)
 	{
 			appendStringInfoString(result, outputstr);
 			pfree(outputstr);
 			break;
+		case TYPCATEGORY_JSON_CAST:
+			jsontext = DatumGetTextP(OidFunctionCall1(typoutputfunc, val));
+			outputstr = text_to_cstring(jsontext);
+			appendStringInfoString(result, outputstr);
+			pfree(outputstr);
+			pfree(jsontext);
+			break;
 		default:
 			outputstr = OidOutputFunctionCall(typoutputfunc, val);
 			escape_json(result, outputstr);
 	Oid			typioparam;
 	Oid			typoutputfunc;
 	TYPCATEGORY tcategory;
+	Oid         castfunc = InvalidOid;
 
 	ndim = ARR_NDIM(v);
 	dim = ARR_DIMS(v);
 					 &typlen, &typbyval, &typalign,
 					 &typdelim, &typioparam, &typoutputfunc);
 
+	if (element_type > FirstNormalObjectId)
+	{
+		    HeapTuple   tuple;
+			Form_pg_cast castForm;
+
+			tuple = SearchSysCache2(CASTSOURCETARGET,
+									ObjectIdGetDatum(element_type),
+									ObjectIdGetDatum(JSONOID));
+			if (HeapTupleIsValid(tuple))
+			{
+				castForm = (Form_pg_cast) GETSTRUCT(tuple);
+
+				if (castForm->castmethod == COERCION_METHOD_FUNCTION)
+					castfunc = typoutputfunc = castForm->castfunc;
+
+				ReleaseSysCache(tuple);
+			}
+	}
+
 	deconstruct_array(v, element_type, typlen, typbyval,
 					  typalign, &elements, &nulls,
 					  &nitems);
 
-	if (element_type == RECORDOID)
+	if (castfunc != InvalidOid)
+		tcategory = TYPCATEGORY_JSON_CAST;
+	else if	(element_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
 	else if (element_type == JSONOID)
 		tcategory = TYPCATEGORY_JSON;
 		TYPCATEGORY tcategory;
 		Oid			typoutput;
 		bool		typisvarlena;
+		Oid         castfunc = InvalidOid;
 
 		if (tupdesc->attrs[i]->attisdropped)
 			continue;
 
 		origval = heap_getattr(tuple, i + 1, tupdesc, &isnull);
 
-		if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
+		getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
+						  &typoutput, &typisvarlena);
+
+		if (tupdesc->attrs[i]->atttypid > FirstNormalObjectId)
+		{
+		    HeapTuple   cast_tuple;
+			Form_pg_cast castForm;
+			
+			cast_tuple = SearchSysCache2(CASTSOURCETARGET,
+										 ObjectIdGetDatum(tupdesc->attrs[i]->atttypid),
+										 ObjectIdGetDatum(JSONOID));
+			if (HeapTupleIsValid(cast_tuple))
+			{
+				castForm = (Form_pg_cast) GETSTRUCT(cast_tuple);
+				
+				if (castForm->castmethod == COERCION_METHOD_FUNCTION)
+					castfunc = typoutput = castForm->castfunc;
+				
+				ReleaseSysCache(cast_tuple);
+			}
+		}
+
+		if (castfunc != InvalidOid)
+			tcategory = TYPCATEGORY_JSON_CAST;
+		else if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
 			tcategory = TYPCATEGORY_ARRAY;
 		else if (tupdesc->attrs[i]->atttypid == RECORDOID)
 			tcategory = TYPCATEGORY_COMPOSITE;
 		else
 			tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
 
-		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.
 	TYPCATEGORY tcategory;
 	Oid			typoutput;
 	bool		typisvarlena;
+	Oid         castfunc = InvalidOid;
 
     if (val_type == InvalidOid)
         ereport(ERROR,
 
 	orig_val = PG_ARGISNULL(0) ? (Datum) 0 : PG_GETARG_DATUM(0);
 
-	if (val_type == RECORDARRAYOID)
+	getTypeOutputInfo(val_type, &typoutput, &typisvarlena);
+
+	if (val_type > FirstNormalObjectId)
+	{
+		    HeapTuple   tuple;
+			Form_pg_cast castForm;
+
+			tuple = SearchSysCache2(CASTSOURCETARGET,
+									ObjectIdGetDatum(val_type),
+									ObjectIdGetDatum(JSONOID));
+			if (HeapTupleIsValid(tuple))
+			{
+				castForm = (Form_pg_cast) GETSTRUCT(tuple);
+
+				if (castForm->castmethod == COERCION_METHOD_FUNCTION)
+					castfunc = typoutput = castForm->castfunc;
+
+				ReleaseSysCache(tuple);
+			}
+	}
+
+	if (castfunc != InvalidOid)
+		tcategory = TYPCATEGORY_JSON_CAST;
+	else if (val_type == RECORDARRAYOID)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
 	else
 		tcategory = TypeCategory(val_type);
 	
-	getTypeOutputInfo(val_type, &typoutput, &typisvarlena);
-
 	/*
 	 * If we have a toasted datum, forcibly detoast it here to avoid
 	 * memory leakage inside the type's output routine.
 	TYPCATEGORY tcategory;
 	Oid			typoutput;
 	bool		typisvarlena;
+	Oid         castfunc = InvalidOid;
 
     if (val_type == InvalidOid)
         ereport(ERROR,
 
 	orig_val = PG_GETARG_DATUM(1);
 
-	if (val_type == RECORDARRAYOID)
+	getTypeOutputInfo(val_type, &typoutput, &typisvarlena);
+
+	if (val_type > FirstNormalObjectId)
+	{
+		    HeapTuple   tuple;
+			Form_pg_cast castForm;
+
+			tuple = SearchSysCache2(CASTSOURCETARGET,
+									ObjectIdGetDatum(val_type),
+									ObjectIdGetDatum(JSONOID));
+			if (HeapTupleIsValid(tuple))
+			{
+				castForm = (Form_pg_cast) GETSTRUCT(tuple);
+
+				if (castForm->castmethod == COERCION_METHOD_FUNCTION)
+					castfunc = typoutput = castForm->castfunc;
+
+				ReleaseSysCache(tuple);
+			}
+	}
+
+	if (castfunc != InvalidOid)
+		tcategory = TYPCATEGORY_JSON_CAST;
+	else if (val_type == RECORDARRAYOID)
 		tcategory = TYPCATEGORY_ARRAY;
 	else if (val_type == RECORDOID)
 		tcategory = TYPCATEGORY_COMPOSITE;
 	else
 		tcategory = TypeCategory(val_type);
 	
-	getTypeOutputInfo(val_type, &typoutput, &typisvarlena);
-
 	/*
 	 * If we have a toasted datum, forcibly detoast it here to avoid
 	 * memory leakage inside the type's output routine.