Unify JSON categorize type API and export for external use
authorAmit Langote <amitlan@postgresql.org>
Thu, 20 Jul 2023 07:19:56 +0000 (16:19 +0900)
committerAmit Langote <amitlan@postgresql.org>
Thu, 20 Jul 2023 07:19:56 +0000 (16:19 +0900)
This essentially removes the JsonbTypeCategory enum and
jsonb_categorize_type() and integrates any jsonb-specific logic that
was in jsonb_categorize_type() into json_categorize_type(), now
moved to jsonfuncs.c.  The remaining JsonTypeCategory enum and
json_categorize_type() cover the needs of the callers in both json.c
and jsonb.c.  json_categorize_type() has grown a new parameter named
is_jsonb for callers to engage the jsonb-specific behavior of
json_categorize_type().

One notable change in the now exported API of json_categorize_type()
is that it now always returns *outfuncoid even though a caller may
have no need currently to see one.

This is in preparation of later commits to implement additional
SQL/JSON functions.

Co-authored-by: Álvaro Herrera <alvherre@alvh.no-ip.org>
Reviewed-by: Álvaro Herrera <alvherre@alvh.no-ip.org>
Discussion: https://postgr.es/m/CA+HiwqE4XTdfb1nW=Ojoy_tQSRhYt-q_kb6i5d4xcKyrLC1Nbg@mail.gmail.com

src/backend/utils/adt/json.c
src/backend/utils/adt/jsonb.c
src/backend/utils/adt/jsonfuncs.c
src/include/utils/jsonfuncs.h
src/tools/pgindent/typedefs.list

index 49080e5fbfffd71cebdca654c4e8a0cd78c25053..f6bef9c1484be731f4206ccda002c8304d7f275c 100644 (file)
@@ -19,7 +19,6 @@
 #include "funcapi.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
-#include "parser/parse_coerce.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/lsyscache.h"
 #include "utils/typcache.h"
 
-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_DATE,                          /* we use special formatting for datetimes */
-       JSONTYPE_TIMESTAMP,
-       JSONTYPE_TIMESTAMPTZ,
-       JSONTYPE_JSON,                          /* JSON itself (and JSONB) */
-       JSONTYPE_ARRAY,                         /* array */
-       JSONTYPE_COMPOSITE,                     /* composite */
-       JSONTYPE_CAST,                          /* something with an explicit cast to JSON */
-       JSONTYPE_OTHER                          /* all else */
-} JsonTypeCategory;
-
 
 /*
  * Support for fast key uniqueness checking.
@@ -107,9 +91,6 @@ static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
                                                          bool use_line_feeds);
 static void array_to_json_internal(Datum array, StringInfo result,
                                                                   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,
                                                  bool key_scalar);
@@ -182,106 +163,6 @@ json_recv(PG_FUNCTION_ARGS)
        PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
 }
 
-/*
- * 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;
-
-       /* Look through any domain */
-       typoid = getBaseType(typoid);
-
-       *outfuncoid = InvalidOid;
-
-       /*
-        * We need to get the output function for everything except date and
-        * timestamp types, array and composite types, booleans, and non-builtin
-        * types where there's a cast to json.
-        */
-
-       switch (typoid)
-       {
-               case BOOLOID:
-                       *tcategory = JSONTYPE_BOOL;
-                       break;
-
-               case INT2OID:
-               case INT4OID:
-               case INT8OID:
-               case FLOAT4OID:
-               case FLOAT8OID:
-               case NUMERICOID:
-                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                       *tcategory = JSONTYPE_NUMERIC;
-                       break;
-
-               case DATEOID:
-                       *tcategory = JSONTYPE_DATE;
-                       break;
-
-               case TIMESTAMPOID:
-                       *tcategory = JSONTYPE_TIMESTAMP;
-                       break;
-
-               case TIMESTAMPTZOID:
-                       *tcategory = JSONTYPE_TIMESTAMPTZ;
-                       break;
-
-               case JSONOID:
-               case JSONBOID:
-                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                       *tcategory = JSONTYPE_JSON;
-                       break;
-
-               default:
-                       /* Check for arrays and composites */
-                       if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
-                               || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
-                               *tcategory = JSONTYPE_ARRAY;
-                       else if (type_is_rowtype(typoid))       /* includes RECORDOID */
-                               *tcategory = JSONTYPE_COMPOSITE;
-                       else
-                       {
-                               /* It's probably the general case ... */
-                               *tcategory = JSONTYPE_OTHER;
-                               /* but let's look for a cast to json, if it's not built-in */
-                               if (typoid >= FirstNormalObjectId)
-                               {
-                                       Oid                     castfunc;
-                                       CoercionPathType ctype;
-
-                                       ctype = find_coercion_pathway(JSONOID, typoid,
-                                                                                                 COERCION_EXPLICIT,
-                                                                                                 &castfunc);
-                                       if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
-                                       {
-                                               *tcategory = JSONTYPE_CAST;
-                                               *outfuncoid = castfunc;
-                                       }
-                                       else
-                                       {
-                                               /* non builtin type with no cast */
-                                               getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                                       }
-                               }
-                               else
-                               {
-                                       /* any other builtin type */
-                                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                               }
-                       }
-                       break;
-       }
-}
-
 /*
  * Turn a Datum into JSON text, appending the string to "result".
  *
@@ -591,7 +472,7 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
        get_typlenbyvalalign(element_type,
                                                 &typlen, &typbyval, &typalign);
 
-       json_categorize_type(element_type,
+       json_categorize_type(element_type, false,
                                                 &tcategory, &outfuncoid);
 
        deconstruct_array(v, element_type, typlen, typbyval,
@@ -665,7 +546,8 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
                        outfuncoid = InvalidOid;
                }
                else
-                       json_categorize_type(att->atttypid, &tcategory, &outfuncoid);
+                       json_categorize_type(att->atttypid, false, &tcategory,
+                                                                &outfuncoid);
 
                datum_to_json(val, isnull, result, tcategory, outfuncoid, false);
        }
@@ -699,7 +581,7 @@ add_json(Datum val, bool is_null, StringInfo result,
                outfuncoid = InvalidOid;
        }
        else
-               json_categorize_type(val_type,
+               json_categorize_type(val_type, false,
                                                         &tcategory, &outfuncoid);
 
        datum_to_json(val, is_null, result, tcategory, outfuncoid, key_scalar);
@@ -784,12 +666,13 @@ to_json_is_immutable(Oid typoid)
        JsonTypeCategory tcategory;
        Oid                     outfuncoid;
 
-       json_categorize_type(typoid, &tcategory, &outfuncoid);
+       json_categorize_type(typoid, false, &tcategory, &outfuncoid);
 
        switch (tcategory)
        {
                case JSONTYPE_BOOL:
                case JSONTYPE_JSON:
+               case JSONTYPE_JSONB:
                case JSONTYPE_NULL:
                        return true;
 
@@ -830,7 +713,7 @@ to_json(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("could not determine input data type")));
 
-       json_categorize_type(val_type,
+       json_categorize_type(val_type, false,
                                                 &tcategory, &outfuncoid);
 
        result = makeStringInfo();
@@ -880,7 +763,7 @@ json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
                MemoryContextSwitchTo(oldcontext);
 
                appendStringInfoChar(state->str, '[');
-               json_categorize_type(arg_type, &state->val_category,
+               json_categorize_type(arg_type, false, &state->val_category,
                                                         &state->val_output_func);
        }
        else
@@ -1112,7 +995,7 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("could not determine data type for argument %d", 1)));
 
-               json_categorize_type(arg_type, &state->key_category,
+               json_categorize_type(arg_type, false, &state->key_category,
                                                         &state->key_output_func);
 
                arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
@@ -1122,7 +1005,7 @@ json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("could not determine data type for argument %d", 2)));
 
-               json_categorize_type(arg_type, &state->val_category,
+               json_categorize_type(arg_type, false, &state->val_category,
                                                         &state->val_output_func);
 
                appendStringInfoString(state->str, "{ ");
index cf43c3f2ded19aabd2a913ab914f84d6c70d3afc..fc64f5686826415a7ff085d8fcef480bfb06c13d 100644 (file)
@@ -19,7 +19,6 @@
 #include "funcapi.h"
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
-#include "parser/parse_coerce.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/datetime.h"
@@ -37,29 +36,12 @@ typedef struct JsonbInState
        Node       *escontext;
 } JsonbInState;
 
-/* unlike with json categories, we need to treat json and jsonb differently */
-typedef enum                                   /* type categories for datum_to_jsonb */
-{
-       JSONBTYPE_NULL,                         /* null, so we didn't bother to identify */
-       JSONBTYPE_BOOL,                         /* boolean (built-in types only) */
-       JSONBTYPE_NUMERIC,                      /* numeric (ditto) */
-       JSONBTYPE_DATE,                         /* we use special formatting for datetimes */
-       JSONBTYPE_TIMESTAMP,            /* we use special formatting for timestamp */
-       JSONBTYPE_TIMESTAMPTZ,          /* ... and timestamptz */
-       JSONBTYPE_JSON,                         /* JSON */
-       JSONBTYPE_JSONB,                        /* JSONB */
-       JSONBTYPE_ARRAY,                        /* array */
-       JSONBTYPE_COMPOSITE,            /* composite */
-       JSONBTYPE_JSONCAST,                     /* something with an explicit cast to JSON */
-       JSONBTYPE_OTHER                         /* all else */
-} JsonbTypeCategory;
-
 typedef struct JsonbAggState
 {
        JsonbInState *res;
-       JsonbTypeCategory key_category;
+       JsonTypeCategory key_category;
        Oid                     key_output_func;
-       JsonbTypeCategory val_category;
+       JsonTypeCategory val_category;
        Oid                     val_output_func;
 } JsonbAggState;
 
@@ -72,19 +54,13 @@ static JsonParseErrorType jsonb_in_array_end(void *pstate);
 static JsonParseErrorType jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
 static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
 static JsonParseErrorType jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
-static void jsonb_categorize_type(Oid typoid,
-                                                                 JsonbTypeCategory *tcategory,
-                                                                 Oid *outfuncoid);
 static void composite_to_jsonb(Datum composite, JsonbInState *result);
 static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
                                                           Datum *vals, bool *nulls, int *valcount,
-                                                          JsonbTypeCategory tcategory, Oid outfuncoid);
+                                                          JsonTypeCategory tcategory, Oid outfuncoid);
 static void array_to_jsonb_internal(Datum array, JsonbInState *result);
-static void jsonb_categorize_type(Oid typoid,
-                                                                 JsonbTypeCategory *tcategory,
-                                                                 Oid *outfuncoid);
 static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
-                                                  JsonbTypeCategory tcategory, Oid outfuncoid,
+                                                  JsonTypeCategory tcategory, Oid outfuncoid,
                                                   bool key_scalar);
 static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
                                          Oid val_type, bool key_scalar);
@@ -633,112 +609,6 @@ add_indent(StringInfo out, bool indent, int level)
 }
 
 
-/*
- * Determine how we want to render values of a given type in datum_to_jsonb.
- *
- * Given the datatype OID, return its JsonbTypeCategory, as well as the type's
- * output function OID.  If the returned category is JSONBTYPE_JSONCAST,
- * we return the OID of the relevant cast function instead.
- */
-static void
-jsonb_categorize_type(Oid typoid,
-                                         JsonbTypeCategory *tcategory,
-                                         Oid *outfuncoid)
-{
-       bool            typisvarlena;
-
-       /* Look through any domain */
-       typoid = getBaseType(typoid);
-
-       *outfuncoid = InvalidOid;
-
-       /*
-        * We need to get the output function for everything except date and
-        * timestamp types, booleans, array and composite types, json and jsonb,
-        * and non-builtin types where there's a cast to json. In this last case
-        * we return the oid of the cast function instead.
-        */
-
-       switch (typoid)
-       {
-               case BOOLOID:
-                       *tcategory = JSONBTYPE_BOOL;
-                       break;
-
-               case INT2OID:
-               case INT4OID:
-               case INT8OID:
-               case FLOAT4OID:
-               case FLOAT8OID:
-               case NUMERICOID:
-                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                       *tcategory = JSONBTYPE_NUMERIC;
-                       break;
-
-               case DATEOID:
-                       *tcategory = JSONBTYPE_DATE;
-                       break;
-
-               case TIMESTAMPOID:
-                       *tcategory = JSONBTYPE_TIMESTAMP;
-                       break;
-
-               case TIMESTAMPTZOID:
-                       *tcategory = JSONBTYPE_TIMESTAMPTZ;
-                       break;
-
-               case JSONBOID:
-                       *tcategory = JSONBTYPE_JSONB;
-                       break;
-
-               case JSONOID:
-                       *tcategory = JSONBTYPE_JSON;
-                       break;
-
-               default:
-                       /* Check for arrays and composites */
-                       if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
-                               || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
-                               *tcategory = JSONBTYPE_ARRAY;
-                       else if (type_is_rowtype(typoid))       /* includes RECORDOID */
-                               *tcategory = JSONBTYPE_COMPOSITE;
-                       else
-                       {
-                               /* It's probably the general case ... */
-                               *tcategory = JSONBTYPE_OTHER;
-
-                               /*
-                                * but first let's look for a cast to json (note: not to
-                                * jsonb) if it's not built-in.
-                                */
-                               if (typoid >= FirstNormalObjectId)
-                               {
-                                       Oid                     castfunc;
-                                       CoercionPathType ctype;
-
-                                       ctype = find_coercion_pathway(JSONOID, typoid,
-                                                                                                 COERCION_EXPLICIT, &castfunc);
-                                       if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
-                                       {
-                                               *tcategory = JSONBTYPE_JSONCAST;
-                                               *outfuncoid = castfunc;
-                                       }
-                                       else
-                                       {
-                                               /* not a cast type, so just get the usual output func */
-                                               getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                                       }
-                               }
-                               else
-                               {
-                                       /* any other builtin type */
-                                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
-                               }
-                               break;
-                       }
-       }
-}
-
 /*
  * Turn a Datum into jsonb, adding it to the result JsonbInState.
  *
@@ -753,7 +623,7 @@ jsonb_categorize_type(Oid typoid,
  */
 static void
 datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
-                          JsonbTypeCategory tcategory, Oid outfuncoid,
+                          JsonTypeCategory tcategory, Oid outfuncoid,
                           bool key_scalar)
 {
        char       *outputstr;
@@ -770,11 +640,11 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
                jb.type = jbvNull;
        }
        else if (key_scalar &&
-                        (tcategory == JSONBTYPE_ARRAY ||
-                         tcategory == JSONBTYPE_COMPOSITE ||
-                         tcategory == JSONBTYPE_JSON ||
-                         tcategory == JSONBTYPE_JSONB ||
-                         tcategory == JSONBTYPE_JSONCAST))
+                        (tcategory == JSONTYPE_ARRAY ||
+                         tcategory == JSONTYPE_COMPOSITE ||
+                         tcategory == JSONTYPE_JSON ||
+                         tcategory == JSONTYPE_JSONB ||
+                         tcategory == JSONTYPE_JSON))
        {
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -782,18 +652,18 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
        }
        else
        {
-               if (tcategory == JSONBTYPE_JSONCAST)
+               if (tcategory == JSONTYPE_CAST)
                        val = OidFunctionCall1(outfuncoid, val);
 
                switch (tcategory)
                {
-                       case JSONBTYPE_ARRAY:
+                       case JSONTYPE_ARRAY:
                                array_to_jsonb_internal(val, result);
                                break;
-                       case JSONBTYPE_COMPOSITE:
+                       case JSONTYPE_COMPOSITE:
                                composite_to_jsonb(val, result);
                                break;
-                       case JSONBTYPE_BOOL:
+                       case JSONTYPE_BOOL:
                                if (key_scalar)
                                {
                                        outputstr = DatumGetBool(val) ? "true" : "false";
@@ -807,7 +677,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
                                        jb.val.boolean = DatumGetBool(val);
                                }
                                break;
-                       case JSONBTYPE_NUMERIC:
+                       case JSONTYPE_NUMERIC:
                                outputstr = OidOutputFunctionCall(outfuncoid, val);
                                if (key_scalar)
                                {
@@ -845,26 +715,26 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
                                        }
                                }
                                break;
-                       case JSONBTYPE_DATE:
+                       case JSONTYPE_DATE:
                                jb.type = jbvString;
                                jb.val.string.val = JsonEncodeDateTime(NULL, val,
                                                                                                           DATEOID, NULL);
                                jb.val.string.len = strlen(jb.val.string.val);
                                break;
-                       case JSONBTYPE_TIMESTAMP:
+                       case JSONTYPE_TIMESTAMP:
                                jb.type = jbvString;
                                jb.val.string.val = JsonEncodeDateTime(NULL, val,
                                                                                                           TIMESTAMPOID, NULL);
                                jb.val.string.len = strlen(jb.val.string.val);
                                break;
-                       case JSONBTYPE_TIMESTAMPTZ:
+                       case JSONTYPE_TIMESTAMPTZ:
                                jb.type = jbvString;
                                jb.val.string.val = JsonEncodeDateTime(NULL, val,
                                                                                                           TIMESTAMPTZOID, NULL);
                                jb.val.string.len = strlen(jb.val.string.val);
                                break;
-                       case JSONBTYPE_JSONCAST:
-                       case JSONBTYPE_JSON:
+                       case JSONTYPE_CAST:
+                       case JSONTYPE_JSON:
                                {
                                        /* parse the json right into the existing result object */
                                        JsonLexContext *lex;
@@ -887,7 +757,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
                                        pg_parse_json_or_ereport(lex, &sem);
                                }
                                break;
-                       case JSONBTYPE_JSONB:
+                       case JSONTYPE_JSONB:
                                {
                                        Jsonb      *jsonb = DatumGetJsonbP(val);
                                        JsonbIterator *it;
@@ -931,7 +801,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
 
        /* Now insert jb into result, unless we did it recursively */
        if (!is_null && !scalar_jsonb &&
-               tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONCAST)
+               tcategory >= JSONTYPE_JSON && tcategory <= JSONTYPE_CAST)
        {
                /* work has been done recursively */
                return;
@@ -976,7 +846,7 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
  */
 static void
 array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals,
-                                  bool *nulls, int *valcount, JsonbTypeCategory tcategory,
+                                  bool *nulls, int *valcount, JsonTypeCategory tcategory,
                                   Oid outfuncoid)
 {
        int                     i;
@@ -1020,7 +890,7 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
        int16           typlen;
        bool            typbyval;
        char            typalign;
-       JsonbTypeCategory tcategory;
+       JsonTypeCategory tcategory;
        Oid                     outfuncoid;
 
        ndim = ARR_NDIM(v);
@@ -1037,8 +907,8 @@ array_to_jsonb_internal(Datum array, JsonbInState *result)
        get_typlenbyvalalign(element_type,
                                                 &typlen, &typbyval, &typalign);
 
-       jsonb_categorize_type(element_type,
-                                                 &tcategory, &outfuncoid);
+       json_categorize_type(element_type, true,
+                                                &tcategory, &outfuncoid);
 
        deconstruct_array(v, element_type, typlen, typbyval,
                                          typalign, &elements, &nulls,
@@ -1084,7 +954,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
                Datum           val;
                bool            isnull;
                char       *attname;
-               JsonbTypeCategory tcategory;
+               JsonTypeCategory tcategory;
                Oid                     outfuncoid;
                JsonbValue      v;
                Form_pg_attribute att = TupleDescAttr(tupdesc, i);
@@ -1105,11 +975,12 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
 
                if (isnull)
                {
-                       tcategory = JSONBTYPE_NULL;
+                       tcategory = JSONTYPE_NULL;
                        outfuncoid = InvalidOid;
                }
                else
-                       jsonb_categorize_type(att->atttypid, &tcategory, &outfuncoid);
+                       json_categorize_type(att->atttypid, true, &tcategory,
+                                                                &outfuncoid);
 
                datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false);
        }
@@ -1122,7 +993,7 @@ composite_to_jsonb(Datum composite, JsonbInState *result)
  * Append JSON text for "val" to "result".
  *
  * This is just a thin wrapper around datum_to_jsonb.  If the same type will be
- * printed many times, avoid using this; better to do the jsonb_categorize_type
+ * printed many times, avoid using this; better to do the json_categorize_type
  * lookups only once.
  */
 
@@ -1130,7 +1001,7 @@ static void
 add_jsonb(Datum val, bool is_null, JsonbInState *result,
                  Oid val_type, bool key_scalar)
 {
-       JsonbTypeCategory tcategory;
+       JsonTypeCategory tcategory;
        Oid                     outfuncoid;
 
        if (val_type == InvalidOid)
@@ -1140,12 +1011,12 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
 
        if (is_null)
        {
-               tcategory = JSONBTYPE_NULL;
+               tcategory = JSONTYPE_NULL;
                outfuncoid = InvalidOid;
        }
        else
-               jsonb_categorize_type(val_type,
-                                                         &tcategory, &outfuncoid);
+               json_categorize_type(val_type, true,
+                                                        &tcategory, &outfuncoid);
 
        datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
 }
@@ -1160,33 +1031,33 @@ add_jsonb(Datum val, bool is_null, JsonbInState *result,
 bool
 to_jsonb_is_immutable(Oid typoid)
 {
-       JsonbTypeCategory tcategory;
+       JsonTypeCategory tcategory;
        Oid                     outfuncoid;
 
-       jsonb_categorize_type(typoid, &tcategory, &outfuncoid);
+       json_categorize_type(typoid, true, &tcategory, &outfuncoid);
 
        switch (tcategory)
        {
-               case JSONBTYPE_NULL:
-               case JSONBTYPE_BOOL:
-               case JSONBTYPE_JSON:
-               case JSONBTYPE_JSONB:
+               case JSONTYPE_NULL:
+               case JSONTYPE_BOOL:
+               case JSONTYPE_JSON:
+               case JSONTYPE_JSONB:
                        return true;
 
-               case JSONBTYPE_DATE:
-               case JSONBTYPE_TIMESTAMP:
-               case JSONBTYPE_TIMESTAMPTZ:
+               case JSONTYPE_DATE:
+               case JSONTYPE_TIMESTAMP:
+               case JSONTYPE_TIMESTAMPTZ:
                        return false;
 
-               case JSONBTYPE_ARRAY:
+               case JSONTYPE_ARRAY:
                        return false;           /* TODO recurse into elements */
 
-               case JSONBTYPE_COMPOSITE:
+               case JSONTYPE_COMPOSITE:
                        return false;           /* TODO recurse into fields */
 
-               case JSONBTYPE_NUMERIC:
-               case JSONBTYPE_JSONCAST:
-               case JSONBTYPE_OTHER:
+               case JSONTYPE_NUMERIC:
+               case JSONTYPE_CAST:
+               case JSONTYPE_OTHER:
                        return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
        }
 
@@ -1202,7 +1073,7 @@ to_jsonb(PG_FUNCTION_ARGS)
        Datum           val = PG_GETARG_DATUM(0);
        Oid                     val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
        JsonbInState result;
-       JsonbTypeCategory tcategory;
+       JsonTypeCategory tcategory;
        Oid                     outfuncoid;
 
        if (val_type == InvalidOid)
@@ -1210,8 +1081,8 @@ to_jsonb(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                 errmsg("could not determine input data type")));
 
-       jsonb_categorize_type(val_type,
-                                                 &tcategory, &outfuncoid);
+       json_categorize_type(val_type, true,
+                                                &tcategory, &outfuncoid);
 
        memset(&result, 0, sizeof(JsonbInState));
 
@@ -1636,8 +1507,8 @@ jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
                                                                         WJB_BEGIN_ARRAY, NULL);
                MemoryContextSwitchTo(oldcontext);
 
-               jsonb_categorize_type(arg_type, &state->val_category,
-                                                         &state->val_output_func);
+               json_categorize_type(arg_type, true, &state->val_category,
+                                                        &state->val_output_func);
        }
        else
        {
@@ -1816,8 +1687,8 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("could not determine input data type")));
 
-               jsonb_categorize_type(arg_type, &state->key_category,
-                                                         &state->key_output_func);
+               json_categorize_type(arg_type, true, &state->key_category,
+                                                        &state->key_output_func);
 
                arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
 
@@ -1826,8 +1697,8 @@ jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
                                        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                         errmsg("could not determine input data type")));
 
-               jsonb_categorize_type(arg_type, &state->val_category,
-                                                         &state->val_output_func);
+               json_categorize_type(arg_type, true, &state->val_category,
+                                                        &state->val_output_func);
        }
        else
        {
index 70cb922e6b7aa45629483bb64d3380d07a1e81a0..a4bfa5e4040133e24cd284bfe9ec9d530a99ba76 100644 (file)
@@ -26,6 +26,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/miscnodes.h"
+#include "parser/parse_coerce.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
@@ -5685,3 +5686,113 @@ json_get_first_token(text *json, bool throw_error)
 
        return JSON_TOKEN_INVALID;      /* invalid json */
 }
+
+/*
+ * Determine how we want to print values of a given type in datum_to_json(b).
+ *
+ * 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.
+ */
+void
+json_categorize_type(Oid typoid, bool is_jsonb,
+                                        JsonTypeCategory *tcategory, Oid *outfuncoid)
+{
+       bool            typisvarlena;
+
+       /* Look through any domain */
+       typoid = getBaseType(typoid);
+
+       *outfuncoid = InvalidOid;
+
+       switch (typoid)
+       {
+               case BOOLOID:
+                       *outfuncoid = F_BOOLOUT;
+                       *tcategory = JSONTYPE_BOOL;
+                       break;
+
+               case INT2OID:
+               case INT4OID:
+               case INT8OID:
+               case FLOAT4OID:
+               case FLOAT8OID:
+               case NUMERICOID:
+                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+                       *tcategory = JSONTYPE_NUMERIC;
+                       break;
+
+               case DATEOID:
+                       *outfuncoid = F_DATE_OUT;
+                       *tcategory = JSONTYPE_DATE;
+                       break;
+
+               case TIMESTAMPOID:
+                       *outfuncoid = F_TIMESTAMP_OUT;
+                       *tcategory = JSONTYPE_TIMESTAMP;
+                       break;
+
+               case TIMESTAMPTZOID:
+                       *outfuncoid = F_TIMESTAMPTZ_OUT;
+                       *tcategory = JSONTYPE_TIMESTAMPTZ;
+                       break;
+
+               case JSONOID:
+                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+                       *tcategory = JSONTYPE_JSON;
+                       break;
+
+               case JSONBOID:
+                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+                       *tcategory = is_jsonb ? JSONTYPE_JSONB : JSONTYPE_JSON;
+                       break;
+
+               default:
+                       /* Check for arrays and composites */
+                       if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
+                               || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
+                       {
+                               *outfuncoid = F_ARRAY_OUT;
+                               *tcategory = JSONTYPE_ARRAY;
+                       }
+                       else if (type_is_rowtype(typoid))       /* includes RECORDOID */
+                       {
+                               *outfuncoid = F_RECORD_OUT;
+                               *tcategory = JSONTYPE_COMPOSITE;
+                       }
+                       else
+                       {
+                               /*
+                                * It's probably the general case.  But let's look for a cast
+                                * to json (note: not to jsonb even if is_jsonb is true), if
+                                * it's not built-in.
+                                */
+                               *tcategory = JSONTYPE_OTHER;
+                               if (typoid >= FirstNormalObjectId)
+                               {
+                                       Oid                     castfunc;
+                                       CoercionPathType ctype;
+
+                                       ctype = find_coercion_pathway(JSONOID, typoid,
+                                                                                                 COERCION_EXPLICIT,
+                                                                                                 &castfunc);
+                                       if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
+                                       {
+                                               *outfuncoid = castfunc;
+                                               *tcategory = JSONTYPE_CAST;
+                                       }
+                                       else
+                                       {
+                                               /* non builtin type with no cast */
+                                               getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+                                       }
+                               }
+                               else
+                               {
+                                       /* any other builtin type */
+                                       getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+                               }
+                       }
+                       break;
+       }
+}
index a85203d4a4ba4f773f11ea13f4d1075e1da9dab2..121dfd5d2480a6a0db86582066521c997bae39fa 100644 (file)
@@ -63,4 +63,24 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
 extern text *transform_json_string_values(text *json, void *action_state,
                                                                                  JsonTransformStringValuesAction transform_action);
 
+/* Type categories returned by json_categorize_type */
+typedef enum
+{
+       JSONTYPE_NULL,                          /* null, so we didn't bother to identify */
+       JSONTYPE_BOOL,                          /* boolean (built-in types only) */
+       JSONTYPE_NUMERIC,                       /* numeric (ditto) */
+       JSONTYPE_DATE,                          /* we use special formatting for datetimes */
+       JSONTYPE_TIMESTAMP,
+       JSONTYPE_TIMESTAMPTZ,
+       JSONTYPE_JSON,                          /* JSON (and JSONB, if not is_jsonb) */
+       JSONTYPE_JSONB,                         /* JSONB (if is_jsonb) */
+       JSONTYPE_ARRAY,                         /* array */
+       JSONTYPE_COMPOSITE,                     /* composite */
+       JSONTYPE_CAST,                          /* something with an explicit cast to JSON */
+       JSONTYPE_OTHER,                         /* all else */
+} JsonTypeCategory;
+
+extern void json_categorize_type(Oid typoid, bool is_jsonb,
+                                                                JsonTypeCategory *tcategory, Oid *outfuncoid);
+
 #endif
index a1cf01e38e451cc75496548452b40045a16d48e1..05814136c699375d57fe5ca5eff30982cbabdcc4 100644 (file)
@@ -1318,7 +1318,6 @@ JsonbIteratorToken
 JsonbPair
 JsonbParseState
 JsonbSubWorkspace
-JsonbTypeCategory
 JsonbValue
 JumbleState
 JunkFilter