LCOV - code coverage report
Current view: top level - src/backend/utils/adt - json.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15beta1 Lines: 616 665 92.6 %
Date: 2022-05-18 03:10:05 Functions: 46 49 93.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * json.c
       4             :  *      JSON data type support.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/utils/adt/json.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/hash.h"
      17             : #include "catalog/pg_proc.h"
      18             : #include "catalog/pg_type.h"
      19             : #include "common/hashfn.h"
      20             : #include "funcapi.h"
      21             : #include "libpq/pqformat.h"
      22             : #include "miscadmin.h"
      23             : #include "parser/parse_coerce.h"
      24             : #include "utils/array.h"
      25             : #include "utils/builtins.h"
      26             : #include "utils/date.h"
      27             : #include "utils/datetime.h"
      28             : #include "utils/json.h"
      29             : #include "utils/jsonfuncs.h"
      30             : #include "utils/lsyscache.h"
      31             : #include "utils/typcache.h"
      32             : 
      33             : /* Common context for key uniqueness check */
      34             : typedef struct HTAB *JsonUniqueCheckState;  /* hash table for key names */
      35             : 
      36             : /* Hash entry for JsonUniqueCheckState */
      37             : typedef struct JsonUniqueHashEntry
      38             : {
      39             :     const char *key;
      40             :     int         key_len;
      41             :     int         object_id;
      42             : } JsonUniqueHashEntry;
      43             : 
      44             : /* Context for key uniqueness check in builder functions */
      45             : typedef struct JsonUniqueBuilderState
      46             : {
      47             :     JsonUniqueCheckState check; /* unique check */
      48             :     StringInfoData skipped_keys;    /* skipped keys with NULL values */
      49             :     MemoryContext mcxt;         /* context for saving skipped keys */
      50             : } JsonUniqueBuilderState;
      51             : 
      52             : /* Element of object stack for key uniqueness check during json parsing */
      53             : typedef struct JsonUniqueStackEntry
      54             : {
      55             :     struct JsonUniqueStackEntry *parent;
      56             :     int         object_id;
      57             : } JsonUniqueStackEntry;
      58             : 
      59             : /* State for key uniqueness check during json parsing */
      60             : typedef struct JsonUniqueParsingState
      61             : {
      62             :     JsonLexContext *lex;
      63             :     JsonUniqueCheckState check;
      64             :     JsonUniqueStackEntry *stack;
      65             :     int         id_counter;
      66             :     bool        unique;
      67             : } JsonUniqueParsingState;
      68             : 
      69             : typedef struct JsonAggState
      70             : {
      71             :     StringInfo  str;
      72             :     JsonTypeCategory key_category;
      73             :     Oid         key_output_func;
      74             :     JsonTypeCategory val_category;
      75             :     Oid         val_output_func;
      76             :     JsonUniqueBuilderState unique_check;
      77             : } JsonAggState;
      78             : 
      79             : static void composite_to_json(Datum composite, StringInfo result,
      80             :                               bool use_line_feeds);
      81             : static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
      82             :                               Datum *vals, bool *nulls, int *valcount,
      83             :                               JsonTypeCategory tcategory, Oid outfuncoid,
      84             :                               bool use_line_feeds);
      85             : static void array_to_json_internal(Datum array, StringInfo result,
      86             :                                    bool use_line_feeds);
      87             : static void datum_to_json(Datum val, bool is_null, StringInfo result,
      88             :                           JsonTypeCategory tcategory, Oid outfuncoid,
      89             :                           bool key_scalar);
      90             : static void add_json(Datum val, bool is_null, StringInfo result,
      91             :                      Oid val_type, bool key_scalar);
      92             : static text *catenate_stringinfo_string(StringInfo buffer, const char *addon);
      93             : 
      94             : /*
      95             :  * Input.
      96             :  */
      97             : Datum
      98        5310 : json_in(PG_FUNCTION_ARGS)
      99             : {
     100        5310 :     char       *json = PG_GETARG_CSTRING(0);
     101        5310 :     text       *result = cstring_to_text(json);
     102             :     JsonLexContext *lex;
     103             : 
     104             :     /* validate it */
     105        5310 :     lex = makeJsonLexContext(result, false);
     106        5310 :     pg_parse_json_or_ereport(lex, &nullSemAction);
     107             : 
     108             :     /* Internal representation is the same as text, for now */
     109        5118 :     PG_RETURN_TEXT_P(result);
     110             : }
     111             : 
     112             : /*
     113             :  * Output.
     114             :  */
     115             : Datum
     116        3242 : json_out(PG_FUNCTION_ARGS)
     117             : {
     118             :     /* we needn't detoast because text_to_cstring will handle that */
     119        3242 :     Datum       txt = PG_GETARG_DATUM(0);
     120             : 
     121        3242 :     PG_RETURN_CSTRING(TextDatumGetCString(txt));
     122             : }
     123             : 
     124             : /*
     125             :  * Binary send.
     126             :  */
     127             : Datum
     128           0 : json_send(PG_FUNCTION_ARGS)
     129             : {
     130           0 :     text       *t = PG_GETARG_TEXT_PP(0);
     131             :     StringInfoData buf;
     132             : 
     133           0 :     pq_begintypsend(&buf);
     134           0 :     pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
     135           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     136             : }
     137             : 
     138             : /*
     139             :  * Binary receive.
     140             :  */
     141             : Datum
     142           0 : json_recv(PG_FUNCTION_ARGS)
     143             : {
     144           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     145             :     char       *str;
     146             :     int         nbytes;
     147             :     JsonLexContext *lex;
     148             : 
     149           0 :     str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     150             : 
     151             :     /* Validate it. */
     152           0 :     lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
     153           0 :     pg_parse_json_or_ereport(lex, &nullSemAction);
     154             : 
     155           0 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
     156             : }
     157             : 
     158             : /*
     159             :  * Determine how we want to print values of a given type in datum_to_json.
     160             :  *
     161             :  * Given the datatype OID, return its JsonTypeCategory, as well as the type's
     162             :  * output function OID.  If the returned category is JSONTYPE_CAST, we
     163             :  * return the OID of the type->JSON cast function instead.
     164             :  */
     165             : void
     166        4052 : json_categorize_type(Oid typoid,
     167             :                      JsonTypeCategory *tcategory,
     168             :                      Oid *outfuncoid)
     169             : {
     170             :     bool        typisvarlena;
     171             : 
     172             :     /* Look through any domain */
     173        4052 :     typoid = getBaseType(typoid);
     174             : 
     175        4052 :     *outfuncoid = InvalidOid;
     176             : 
     177             :     /*
     178             :      * We need to get the output function for everything except date and
     179             :      * timestamp types, array and composite types, booleans, and non-builtin
     180             :      * types where there's a cast to json.
     181             :      */
     182             : 
     183        4052 :     switch (typoid)
     184             :     {
     185          30 :         case BOOLOID:
     186          30 :             *tcategory = JSONTYPE_BOOL;
     187          30 :             break;
     188             : 
     189        2358 :         case INT2OID:
     190             :         case INT4OID:
     191             :         case INT8OID:
     192             :         case FLOAT4OID:
     193             :         case FLOAT8OID:
     194             :         case NUMERICOID:
     195        2358 :             getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     196        2358 :             *tcategory = JSONTYPE_NUMERIC;
     197        2358 :             break;
     198             : 
     199          24 :         case DATEOID:
     200          24 :             *tcategory = JSONTYPE_DATE;
     201          24 :             break;
     202             : 
     203          24 :         case TIMESTAMPOID:
     204          24 :             *tcategory = JSONTYPE_TIMESTAMP;
     205          24 :             break;
     206             : 
     207          24 :         case TIMESTAMPTZOID:
     208          24 :             *tcategory = JSONTYPE_TIMESTAMPTZ;
     209          24 :             break;
     210             : 
     211         130 :         case JSONOID:
     212             :         case JSONBOID:
     213         130 :             getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     214         130 :             *tcategory = JSONTYPE_JSON;
     215         130 :             break;
     216             : 
     217        1462 :         default:
     218             :             /* Check for arrays and composites */
     219        1462 :             if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
     220        1126 :                 || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
     221         336 :                 *tcategory = JSONTYPE_ARRAY;
     222        1126 :             else if (type_is_rowtype(typoid))   /* includes RECORDOID */
     223         262 :                 *tcategory = JSONTYPE_COMPOSITE;
     224             :             else
     225             :             {
     226             :                 /* It's probably the general case ... */
     227         864 :                 *tcategory = JSONTYPE_OTHER;
     228             :                 /* but let's look for a cast to json, if it's not built-in */
     229         864 :                 if (typoid >= FirstNormalObjectId)
     230             :                 {
     231             :                     Oid         castfunc;
     232             :                     CoercionPathType ctype;
     233             : 
     234           4 :                     ctype = find_coercion_pathway(JSONOID, typoid,
     235             :                                                   COERCION_EXPLICIT,
     236             :                                                   &castfunc);
     237           4 :                     if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
     238             :                     {
     239           4 :                         *tcategory = JSONTYPE_CAST;
     240           4 :                         *outfuncoid = castfunc;
     241             :                     }
     242             :                     else
     243             :                     {
     244             :                         /* non builtin type with no cast */
     245           0 :                         getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     246             :                     }
     247             :                 }
     248             :                 else
     249             :                 {
     250             :                     /* any other builtin type */
     251         860 :                     getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     252             :                 }
     253             :             }
     254        1462 :             break;
     255             :     }
     256        4052 : }
     257             : 
     258             : /*
     259             :  * Turn a Datum into JSON text, appending the string to "result".
     260             :  *
     261             :  * tcategory and outfuncoid are from a previous call to json_categorize_type,
     262             :  * except that if is_null is true then they can be invalid.
     263             :  *
     264             :  * If key_scalar is true, the value is being printed as a key, so insist
     265             :  * it's of an acceptable type, and force it to be quoted.
     266             :  */
     267             : static void
     268        5514 : datum_to_json(Datum val, bool is_null, StringInfo result,
     269             :               JsonTypeCategory tcategory, Oid outfuncoid,
     270             :               bool key_scalar)
     271             : {
     272             :     char       *outputstr;
     273             :     text       *jsontext;
     274             : 
     275        5514 :     check_stack_depth();
     276             : 
     277             :     /* callers are expected to ensure that null keys are not passed in */
     278             :     Assert(!(key_scalar && is_null));
     279             : 
     280        5514 :     if (is_null)
     281             :     {
     282         318 :         appendStringInfoString(result, "null");
     283         318 :         return;
     284             :     }
     285             : 
     286        5196 :     if (key_scalar &&
     287         666 :         (tcategory == JSONTYPE_ARRAY ||
     288         660 :          tcategory == JSONTYPE_COMPOSITE ||
     289         648 :          tcategory == JSONTYPE_JSON ||
     290             :          tcategory == JSONTYPE_CAST))
     291          36 :         ereport(ERROR,
     292             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     293             :                  errmsg("key value must be scalar, not array, composite, or json")));
     294             : 
     295        5160 :     switch (tcategory)
     296             :     {
     297         324 :         case JSONTYPE_ARRAY:
     298         324 :             array_to_json_internal(val, result, false);
     299         324 :             break;
     300         500 :         case JSONTYPE_COMPOSITE:
     301         500 :             composite_to_json(val, result, false);
     302         500 :             break;
     303          30 :         case JSONTYPE_BOOL:
     304          30 :             outputstr = DatumGetBool(val) ? "true" : "false";
     305          30 :             if (key_scalar)
     306           0 :                 escape_json(result, outputstr);
     307             :             else
     308          30 :                 appendStringInfoString(result, outputstr);
     309          30 :             break;
     310        3156 :         case JSONTYPE_NUMERIC:
     311        3156 :             outputstr = OidOutputFunctionCall(outfuncoid, val);
     312             : 
     313             :             /*
     314             :              * Don't call escape_json for a non-key if it's a valid JSON
     315             :              * number.
     316             :              */
     317        3156 :             if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
     318        2874 :                 appendStringInfoString(result, outputstr);
     319             :             else
     320         282 :                 escape_json(result, outputstr);
     321        3156 :             pfree(outputstr);
     322        3156 :             break;
     323          24 :         case JSONTYPE_DATE:
     324             :             {
     325             :                 char        buf[MAXDATELEN + 1];
     326             : 
     327          24 :                 JsonEncodeDateTime(buf, val, DATEOID, NULL);
     328          24 :                 appendStringInfo(result, "\"%s\"", buf);
     329             :             }
     330          24 :             break;
     331          24 :         case JSONTYPE_TIMESTAMP:
     332             :             {
     333             :                 char        buf[MAXDATELEN + 1];
     334             : 
     335          24 :                 JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
     336          24 :                 appendStringInfo(result, "\"%s\"", buf);
     337             :             }
     338          24 :             break;
     339          24 :         case JSONTYPE_TIMESTAMPTZ:
     340             :             {
     341             :                 char        buf[MAXDATELEN + 1];
     342             : 
     343          24 :                 JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
     344          24 :                 appendStringInfo(result, "\"%s\"", buf);
     345             :             }
     346          24 :             break;
     347         184 :         case JSONTYPE_JSON:
     348             :             /* JSON and JSONB output will already be escaped */
     349         184 :             outputstr = OidOutputFunctionCall(outfuncoid, val);
     350         184 :             appendStringInfoString(result, outputstr);
     351         184 :             pfree(outputstr);
     352         184 :             break;
     353           4 :         case JSONTYPE_CAST:
     354             :             /* outfuncoid refers to a cast function, not an output function */
     355           4 :             jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
     356           4 :             outputstr = text_to_cstring(jsontext);
     357           4 :             appendStringInfoString(result, outputstr);
     358           4 :             pfree(outputstr);
     359           4 :             pfree(jsontext);
     360           4 :             break;
     361         890 :         default:
     362         890 :             outputstr = OidOutputFunctionCall(outfuncoid, val);
     363         890 :             escape_json(result, outputstr);
     364         890 :             pfree(outputstr);
     365         890 :             break;
     366             :     }
     367             : }
     368             : 
     369             : /*
     370             :  * Encode 'value' of datetime type 'typid' into JSON string in ISO format using
     371             :  * optionally preallocated buffer 'buf'.  Optional 'tzp' determines time-zone
     372             :  * offset (in seconds) in which we want to show timestamptz.
     373             :  */
     374             : char *
     375         636 : JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
     376             : {
     377         636 :     if (!buf)
     378          60 :         buf = palloc(MAXDATELEN + 1);
     379             : 
     380         636 :     switch (typid)
     381             :     {
     382         102 :         case DATEOID:
     383             :             {
     384             :                 DateADT     date;
     385             :                 struct pg_tm tm;
     386             : 
     387         102 :                 date = DatumGetDateADT(value);
     388             : 
     389             :                 /* Same as date_out(), but forcing DateStyle */
     390         102 :                 if (DATE_NOT_FINITE(date))
     391          24 :                     EncodeSpecialDate(date, buf);
     392             :                 else
     393             :                 {
     394          78 :                     j2date(date + POSTGRES_EPOCH_JDATE,
     395             :                            &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
     396          78 :                     EncodeDateOnly(&tm, USE_XSD_DATES, buf);
     397             :                 }
     398             :             }
     399         102 :             break;
     400          60 :         case TIMEOID:
     401             :             {
     402          60 :                 TimeADT     time = DatumGetTimeADT(value);
     403             :                 struct pg_tm tt,
     404          60 :                            *tm = &tt;
     405             :                 fsec_t      fsec;
     406             : 
     407             :                 /* Same as time_out(), but forcing DateStyle */
     408          60 :                 time2tm(time, tm, &fsec);
     409          60 :                 EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
     410             :             }
     411          60 :             break;
     412         120 :         case TIMETZOID:
     413             :             {
     414         120 :                 TimeTzADT  *time = DatumGetTimeTzADTP(value);
     415             :                 struct pg_tm tt,
     416         120 :                            *tm = &tt;
     417             :                 fsec_t      fsec;
     418             :                 int         tz;
     419             : 
     420             :                 /* Same as timetz_out(), but forcing DateStyle */
     421         120 :                 timetz2tm(time, tm, &fsec, &tz);
     422         120 :                 EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
     423             :             }
     424         120 :             break;
     425         132 :         case TIMESTAMPOID:
     426             :             {
     427             :                 Timestamp   timestamp;
     428             :                 struct pg_tm tm;
     429             :                 fsec_t      fsec;
     430             : 
     431         132 :                 timestamp = DatumGetTimestamp(value);
     432             :                 /* Same as timestamp_out(), but forcing DateStyle */
     433         132 :                 if (TIMESTAMP_NOT_FINITE(timestamp))
     434          24 :                     EncodeSpecialTimestamp(timestamp, buf);
     435         108 :                 else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
     436         108 :                     EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
     437             :                 else
     438           0 :                     ereport(ERROR,
     439             :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     440             :                              errmsg("timestamp out of range")));
     441             :             }
     442         132 :             break;
     443         222 :         case TIMESTAMPTZOID:
     444             :             {
     445             :                 TimestampTz timestamp;
     446             :                 struct pg_tm tm;
     447             :                 int         tz;
     448             :                 fsec_t      fsec;
     449         222 :                 const char *tzn = NULL;
     450             : 
     451         222 :                 timestamp = DatumGetTimestampTz(value);
     452             : 
     453             :                 /*
     454             :                  * If a time zone is specified, we apply the time-zone shift,
     455             :                  * convert timestamptz to pg_tm as if it were without a time
     456             :                  * zone, and then use the specified time zone for converting
     457             :                  * the timestamp into a string.
     458             :                  */
     459         222 :                 if (tzp)
     460             :                 {
     461         174 :                     tz = *tzp;
     462         174 :                     timestamp -= (TimestampTz) tz * USECS_PER_SEC;
     463             :                 }
     464             : 
     465             :                 /* Same as timestamptz_out(), but forcing DateStyle */
     466         222 :                 if (TIMESTAMP_NOT_FINITE(timestamp))
     467          24 :                     EncodeSpecialTimestamp(timestamp, buf);
     468         198 :                 else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
     469             :                                       tzp ? NULL : &tzn, NULL) == 0)
     470             :                 {
     471         198 :                     if (tzp)
     472         174 :                         tm.tm_isdst = 1;    /* set time-zone presence flag */
     473             : 
     474         198 :                     EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
     475             :                 }
     476             :                 else
     477           0 :                     ereport(ERROR,
     478             :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     479             :                              errmsg("timestamp out of range")));
     480             :             }
     481         222 :             break;
     482           0 :         default:
     483           0 :             elog(ERROR, "unknown jsonb value datetime type oid %u", typid);
     484             :             return NULL;
     485             :     }
     486             : 
     487         636 :     return buf;
     488             : }
     489             : 
     490             : /*
     491             :  * Process a single dimension of an array.
     492             :  * If it's the innermost dimension, output the values, otherwise call
     493             :  * ourselves recursively to process the next dimension.
     494             :  */
     495             : static void
     496         378 : array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
     497             :                   bool *nulls, int *valcount, JsonTypeCategory tcategory,
     498             :                   Oid outfuncoid, bool use_line_feeds)
     499             : {
     500             :     int         i;
     501             :     const char *sep;
     502             : 
     503             :     Assert(dim < ndims);
     504             : 
     505         378 :     sep = use_line_feeds ? ",\n " : ",";
     506             : 
     507         378 :     appendStringInfoChar(result, '[');
     508             : 
     509        1410 :     for (i = 1; i <= dims[dim]; i++)
     510             :     {
     511        1032 :         if (i > 1)
     512         654 :             appendStringInfoString(result, sep);
     513             : 
     514        1032 :         if (dim + 1 == ndims)
     515             :         {
     516        1020 :             datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
     517             :                           outfuncoid, false);
     518        1020 :             (*valcount)++;
     519             :         }
     520             :         else
     521             :         {
     522             :             /*
     523             :              * Do we want line feeds on inner dimensions of arrays? For now
     524             :              * we'll say no.
     525             :              */
     526          12 :             array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
     527             :                               valcount, tcategory, outfuncoid, false);
     528             :         }
     529             :     }
     530             : 
     531         378 :     appendStringInfoChar(result, ']');
     532         378 : }
     533             : 
     534             : /*
     535             :  * Turn an array into JSON.
     536             :  */
     537             : static void
     538         366 : array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
     539             : {
     540         366 :     ArrayType  *v = DatumGetArrayTypeP(array);
     541         366 :     Oid         element_type = ARR_ELEMTYPE(v);
     542             :     int        *dim;
     543             :     int         ndim;
     544             :     int         nitems;
     545         366 :     int         count = 0;
     546             :     Datum      *elements;
     547             :     bool       *nulls;
     548             :     int16       typlen;
     549             :     bool        typbyval;
     550             :     char        typalign;
     551             :     JsonTypeCategory tcategory;
     552             :     Oid         outfuncoid;
     553             : 
     554         366 :     ndim = ARR_NDIM(v);
     555         366 :     dim = ARR_DIMS(v);
     556         366 :     nitems = ArrayGetNItems(ndim, dim);
     557             : 
     558         366 :     if (nitems <= 0)
     559             :     {
     560           0 :         appendStringInfoString(result, "[]");
     561           0 :         return;
     562             :     }
     563             : 
     564         366 :     get_typlenbyvalalign(element_type,
     565             :                          &typlen, &typbyval, &typalign);
     566             : 
     567         366 :     json_categorize_type(element_type,
     568             :                          &tcategory, &outfuncoid);
     569             : 
     570         366 :     deconstruct_array(v, element_type, typlen, typbyval,
     571             :                       typalign, &elements, &nulls,
     572             :                       &nitems);
     573             : 
     574         366 :     array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
     575             :                       outfuncoid, use_line_feeds);
     576             : 
     577         366 :     pfree(elements);
     578         366 :     pfree(nulls);
     579             : }
     580             : 
     581             : /*
     582             :  * Turn a composite / record into JSON.
     583             :  */
     584             : static void
     585        1028 : composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
     586             : {
     587             :     HeapTupleHeader td;
     588             :     Oid         tupType;
     589             :     int32       tupTypmod;
     590             :     TupleDesc   tupdesc;
     591             :     HeapTupleData tmptup,
     592             :                *tuple;
     593             :     int         i;
     594        1028 :     bool        needsep = false;
     595             :     const char *sep;
     596             : 
     597        1028 :     sep = use_line_feeds ? ",\n " : ",";
     598             : 
     599        1028 :     td = DatumGetHeapTupleHeader(composite);
     600             : 
     601             :     /* Extract rowtype info and find a tupdesc */
     602        1028 :     tupType = HeapTupleHeaderGetTypeId(td);
     603        1028 :     tupTypmod = HeapTupleHeaderGetTypMod(td);
     604        1028 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     605             : 
     606             :     /* Build a temporary HeapTuple control structure */
     607        1028 :     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
     608        1028 :     tmptup.t_data = td;
     609        1028 :     tuple = &tmptup;
     610             : 
     611        1028 :     appendStringInfoChar(result, '{');
     612             : 
     613        3252 :     for (i = 0; i < tupdesc->natts; i++)
     614             :     {
     615             :         Datum       val;
     616             :         bool        isnull;
     617             :         char       *attname;
     618             :         JsonTypeCategory tcategory;
     619             :         Oid         outfuncoid;
     620        2224 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     621             : 
     622        2224 :         if (att->attisdropped)
     623           0 :             continue;
     624             : 
     625        2224 :         if (needsep)
     626        1196 :             appendStringInfoString(result, sep);
     627        2224 :         needsep = true;
     628             : 
     629        2224 :         attname = NameStr(att->attname);
     630        2224 :         escape_json(result, attname);
     631        2224 :         appendStringInfoChar(result, ':');
     632             : 
     633        2224 :         val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
     634             : 
     635        2224 :         if (isnull)
     636             :         {
     637         186 :             tcategory = JSONTYPE_NULL;
     638         186 :             outfuncoid = InvalidOid;
     639             :         }
     640             :         else
     641        2038 :             json_categorize_type(att->atttypid, &tcategory, &outfuncoid);
     642             : 
     643        2224 :         datum_to_json(val, isnull, result, tcategory, outfuncoid, false);
     644             :     }
     645             : 
     646        1028 :     appendStringInfoChar(result, '}');
     647        1028 :     ReleaseTupleDesc(tupdesc);
     648        1028 : }
     649             : 
     650             : /*
     651             :  * Append JSON text for "val" to "result".
     652             :  *
     653             :  * This is just a thin wrapper around datum_to_json.  If the same type will be
     654             :  * printed many times, avoid using this; better to do the json_categorize_type
     655             :  * lookups only once.
     656             :  */
     657             : static void
     658        1152 : add_json(Datum val, bool is_null, StringInfo result,
     659             :          Oid val_type, bool key_scalar)
     660             : {
     661             :     JsonTypeCategory tcategory;
     662             :     Oid         outfuncoid;
     663             : 
     664        1152 :     if (val_type == InvalidOid)
     665           0 :         ereport(ERROR,
     666             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     667             :                  errmsg("could not determine input data type")));
     668             : 
     669        1152 :     if (is_null)
     670             :     {
     671          66 :         tcategory = JSONTYPE_NULL;
     672          66 :         outfuncoid = InvalidOid;
     673             :     }
     674             :     else
     675        1086 :         json_categorize_type(val_type,
     676             :                              &tcategory, &outfuncoid);
     677             : 
     678        1152 :     datum_to_json(val, is_null, result, tcategory, outfuncoid, key_scalar);
     679        1116 : }
     680             : 
     681             : /*
     682             :  * SQL function array_to_json(row)
     683             :  */
     684             : Datum
     685          18 : array_to_json(PG_FUNCTION_ARGS)
     686             : {
     687          18 :     Datum       array = PG_GETARG_DATUM(0);
     688             :     StringInfo  result;
     689             : 
     690          18 :     result = makeStringInfo();
     691             : 
     692          18 :     array_to_json_internal(array, result, false);
     693             : 
     694          18 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     695             : }
     696             : 
     697             : /*
     698             :  * SQL function array_to_json(row, prettybool)
     699             :  */
     700             : Datum
     701          24 : array_to_json_pretty(PG_FUNCTION_ARGS)
     702             : {
     703          24 :     Datum       array = PG_GETARG_DATUM(0);
     704          24 :     bool        use_line_feeds = PG_GETARG_BOOL(1);
     705             :     StringInfo  result;
     706             : 
     707          24 :     result = makeStringInfo();
     708             : 
     709          24 :     array_to_json_internal(array, result, use_line_feeds);
     710             : 
     711          24 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     712             : }
     713             : 
     714             : /*
     715             :  * SQL function row_to_json(row)
     716             :  */
     717             : Datum
     718         480 : row_to_json(PG_FUNCTION_ARGS)
     719             : {
     720         480 :     Datum       array = PG_GETARG_DATUM(0);
     721             :     StringInfo  result;
     722             : 
     723         480 :     result = makeStringInfo();
     724             : 
     725         480 :     composite_to_json(array, result, false);
     726             : 
     727         480 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     728             : }
     729             : 
     730             : /*
     731             :  * SQL function row_to_json(row, prettybool)
     732             :  */
     733             : Datum
     734          48 : row_to_json_pretty(PG_FUNCTION_ARGS)
     735             : {
     736          48 :     Datum       array = PG_GETARG_DATUM(0);
     737          48 :     bool        use_line_feeds = PG_GETARG_BOOL(1);
     738             :     StringInfo  result;
     739             : 
     740          48 :     result = makeStringInfo();
     741             : 
     742          48 :     composite_to_json(array, result, use_line_feeds);
     743             : 
     744          48 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     745             : }
     746             : 
     747             : Datum
     748         240 : to_json_worker(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
     749             : {
     750         240 :     StringInfo  result = makeStringInfo();
     751             : 
     752         240 :     datum_to_json(val, false, result, tcategory, outfuncoid, false);
     753             : 
     754         240 :     return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
     755             : }
     756             : 
     757             : bool
     758           0 : to_json_is_immutable(Oid typoid)
     759             : {
     760             :     JsonTypeCategory tcategory;
     761             :     Oid         outfuncoid;
     762             : 
     763           0 :     json_categorize_type(typoid, &tcategory, &outfuncoid);
     764             : 
     765           0 :     switch (tcategory)
     766             :     {
     767           0 :         case JSONTYPE_BOOL:
     768             :         case JSONTYPE_JSON:
     769           0 :             return true;
     770             : 
     771           0 :         case JSONTYPE_DATE:
     772             :         case JSONTYPE_TIMESTAMP:
     773             :         case JSONTYPE_TIMESTAMPTZ:
     774           0 :             return false;
     775             : 
     776           0 :         case JSONTYPE_ARRAY:
     777           0 :             return false;       /* TODO recurse into elements */
     778             : 
     779           0 :         case JSONTYPE_COMPOSITE:
     780           0 :             return false;       /* TODO recurse into fields */
     781             : 
     782           0 :         case JSONTYPE_NUMERIC:
     783             :         case JSONTYPE_CAST:
     784             :         default:
     785           0 :             return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
     786             :     }
     787             : }
     788             : 
     789             : /*
     790             :  * SQL function to_json(anyvalue)
     791             :  */
     792             : Datum
     793         180 : to_json(PG_FUNCTION_ARGS)
     794             : {
     795         180 :     Datum       val = PG_GETARG_DATUM(0);
     796         180 :     Oid         val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
     797             :     JsonTypeCategory tcategory;
     798             :     Oid         outfuncoid;
     799             : 
     800         180 :     if (val_type == InvalidOid)
     801           0 :         ereport(ERROR,
     802             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     803             :                  errmsg("could not determine input data type")));
     804             : 
     805         180 :     json_categorize_type(val_type,
     806             :                          &tcategory, &outfuncoid);
     807             : 
     808         180 :     PG_RETURN_DATUM(to_json_worker(val, tcategory, outfuncoid));
     809             : }
     810             : 
     811             : /*
     812             :  * json_agg transition function
     813             :  *
     814             :  * aggregate input column as a json array value.
     815             :  */
     816             : static Datum
     817         542 : json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
     818             : {
     819             :     MemoryContext aggcontext,
     820             :                 oldcontext;
     821             :     JsonAggState *state;
     822             :     Datum       val;
     823             : 
     824         542 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     825             :     {
     826             :         /* cannot be called directly because of internal-type argument */
     827           0 :         elog(ERROR, "json_agg_transfn called in non-aggregate context");
     828             :     }
     829             : 
     830         542 :     if (PG_ARGISNULL(0))
     831             :     {
     832         112 :         Oid         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
     833             : 
     834         112 :         if (arg_type == InvalidOid)
     835           0 :             ereport(ERROR,
     836             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     837             :                      errmsg("could not determine input data type")));
     838             : 
     839             :         /*
     840             :          * Make this state object in a context where it will persist for the
     841             :          * duration of the aggregate call.  MemoryContextSwitchTo is only
     842             :          * needed the first time, as the StringInfo routines make sure they
     843             :          * use the right context to enlarge the object if necessary.
     844             :          */
     845         112 :         oldcontext = MemoryContextSwitchTo(aggcontext);
     846         112 :         state = (JsonAggState *) palloc(sizeof(JsonAggState));
     847         112 :         state->str = makeStringInfo();
     848         112 :         MemoryContextSwitchTo(oldcontext);
     849             : 
     850         112 :         appendStringInfoChar(state->str, '[');
     851         112 :         json_categorize_type(arg_type, &state->val_category,
     852             :                              &state->val_output_func);
     853             :     }
     854             :     else
     855             :     {
     856         430 :         state = (JsonAggState *) PG_GETARG_POINTER(0);
     857             :     }
     858             : 
     859         542 :     if (absent_on_null && PG_ARGISNULL(1))
     860          90 :         PG_RETURN_POINTER(state);
     861             : 
     862         452 :     if (state->str->len > 1)
     863         346 :         appendStringInfoString(state->str, ", ");
     864             : 
     865             :     /* fast path for NULLs */
     866         452 :     if (PG_ARGISNULL(1))
     867             :     {
     868          54 :         datum_to_json((Datum) 0, true, state->str, JSONTYPE_NULL,
     869             :                       InvalidOid, false);
     870          54 :         PG_RETURN_POINTER(state);
     871             :     }
     872             : 
     873         398 :     val = PG_GETARG_DATUM(1);
     874             : 
     875             :     /* add some whitespace if structured type and not first item */
     876         398 :     if (!PG_ARGISNULL(0) && state->str->len > 1 &&
     877         304 :         (state->val_category == JSONTYPE_ARRAY ||
     878         298 :          state->val_category == JSONTYPE_COMPOSITE))
     879             :     {
     880         112 :         appendStringInfoString(state->str, "\n ");
     881             :     }
     882             : 
     883         398 :     datum_to_json(val, false, state->str, state->val_category,
     884             :                   state->val_output_func, false);
     885             : 
     886             :     /*
     887             :      * The transition type for json_agg() is declared to be "internal", which
     888             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     889             :      * pass the JsonAggState pointer through nodeAgg.c's machinations.
     890             :      */
     891         398 :     PG_RETURN_POINTER(state);
     892             : }
     893             : 
     894             : 
     895             : /*
     896             :  * json_agg aggregate function
     897             :  */
     898             : Datum
     899         152 : json_agg_transfn(PG_FUNCTION_ARGS)
     900             : {
     901         152 :     return json_agg_transfn_worker(fcinfo, false);
     902             : }
     903             : 
     904             : /*
     905             :  * json_agg_strict aggregate function
     906             :  */
     907             : Datum
     908         390 : json_agg_strict_transfn(PG_FUNCTION_ARGS)
     909             : {
     910         390 :     return json_agg_transfn_worker(fcinfo, true);
     911             : }
     912             : 
     913             : /*
     914             :  * json_agg final function
     915             :  */
     916             : Datum
     917         124 : json_agg_finalfn(PG_FUNCTION_ARGS)
     918             : {
     919             :     JsonAggState *state;
     920             : 
     921             :     /* cannot be called directly because of internal-type argument */
     922             :     Assert(AggCheckCallContext(fcinfo, NULL));
     923             : 
     924         248 :     state = PG_ARGISNULL(0) ?
     925         124 :         NULL :
     926         112 :         (JsonAggState *) PG_GETARG_POINTER(0);
     927             : 
     928             :     /* NULL result for no rows in, as is standard with aggregates */
     929         124 :     if (state == NULL)
     930          12 :         PG_RETURN_NULL();
     931             : 
     932             :     /* Else return state with appropriate array terminator added */
     933         112 :     PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
     934             : }
     935             : 
     936             : /* Functions implementing hash table for key uniqueness check */
     937             : static uint32
     938         396 : json_unique_hash(const void *key, Size keysize)
     939             : {
     940         396 :     const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
     941         396 :     uint32      hash = hash_bytes_uint32(entry->object_id);
     942             : 
     943         396 :     hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
     944             : 
     945         396 :     return DatumGetUInt32(hash);
     946             : }
     947             : 
     948             : static int
     949          90 : json_unique_hash_match(const void *key1, const void *key2, Size keysize)
     950             : {
     951          90 :     const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
     952          90 :     const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
     953             : 
     954          90 :     if (entry1->object_id != entry2->object_id)
     955           0 :         return entry1->object_id > entry2->object_id ? 1 : -1;
     956             : 
     957          90 :     if (entry1->key_len != entry2->key_len)
     958           0 :         return entry1->key_len > entry2->key_len ? 1 : -1;
     959             : 
     960          90 :     return strncmp(entry1->key, entry2->key, entry1->key_len);
     961             : }
     962             : 
     963             : /* Functions implementing object key uniqueness check */
     964             : static void
     965         288 : json_unique_check_init(JsonUniqueCheckState *cxt)
     966             : {
     967             :     HASHCTL     ctl;
     968             : 
     969         288 :     memset(&ctl, 0, sizeof(ctl));
     970         288 :     ctl.keysize = sizeof(JsonUniqueHashEntry);
     971         288 :     ctl.entrysize = sizeof(JsonUniqueHashEntry);
     972         288 :     ctl.hcxt = CurrentMemoryContext;
     973         288 :     ctl.hash = json_unique_hash;
     974         288 :     ctl.match = json_unique_hash_match;
     975             : 
     976         288 :     *cxt = hash_create("json object hashtable",
     977             :                        32,
     978             :                        &ctl,
     979             :                        HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
     980         288 : }
     981             : 
     982             : static bool
     983         396 : json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
     984             : {
     985             :     JsonUniqueHashEntry entry;
     986             :     bool        found;
     987             : 
     988         396 :     entry.key = key;
     989         396 :     entry.key_len = strlen(key);
     990         396 :     entry.object_id = object_id;
     991             : 
     992         396 :     (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
     993             : 
     994         396 :     return !found;
     995             : }
     996             : 
     997             : static void
     998          60 : json_unique_builder_init(JsonUniqueBuilderState *cxt)
     999             : {
    1000          60 :     json_unique_check_init(&cxt->check);
    1001          60 :     cxt->mcxt = CurrentMemoryContext;
    1002          60 :     cxt->skipped_keys.data = NULL;
    1003          60 : }
    1004             : 
    1005             : /* On-demand initialization of skipped_keys StringInfo structure */
    1006             : static StringInfo
    1007          42 : json_unique_builder_get_skipped_keys(JsonUniqueBuilderState *cxt)
    1008             : {
    1009          42 :     StringInfo  out = &cxt->skipped_keys;
    1010             : 
    1011          42 :     if (!out->data)
    1012             :     {
    1013          30 :         MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
    1014             : 
    1015          30 :         initStringInfo(out);
    1016          30 :         MemoryContextSwitchTo(oldcxt);
    1017             :     }
    1018             : 
    1019          42 :     return out;
    1020             : }
    1021             : 
    1022             : /*
    1023             :  * json_object_agg transition function.
    1024             :  *
    1025             :  * aggregate two input columns as a single json object value.
    1026             :  */
    1027             : static Datum
    1028         270 : json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
    1029             :                                bool absent_on_null, bool unique_keys)
    1030             : {
    1031             :     MemoryContext aggcontext,
    1032             :                 oldcontext;
    1033             :     JsonAggState *state;
    1034             :     StringInfo  out;
    1035             :     Datum       arg;
    1036             :     bool        skip;
    1037             :     int         key_offset;
    1038             : 
    1039         270 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
    1040             :     {
    1041             :         /* cannot be called directly because of internal-type argument */
    1042           0 :         elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
    1043             :     }
    1044             : 
    1045         270 :     if (PG_ARGISNULL(0))
    1046             :     {
    1047             :         Oid         arg_type;
    1048             : 
    1049             :         /*
    1050             :          * Make the StringInfo in a context where it will persist for the
    1051             :          * duration of the aggregate call. Switching context is only needed
    1052             :          * for this initial step, as the StringInfo routines make sure they
    1053             :          * use the right context to enlarge the object if necessary.
    1054             :          */
    1055          90 :         oldcontext = MemoryContextSwitchTo(aggcontext);
    1056          90 :         state = (JsonAggState *) palloc(sizeof(JsonAggState));
    1057          90 :         state->str = makeStringInfo();
    1058          90 :         if (unique_keys)
    1059          36 :             json_unique_builder_init(&state->unique_check);
    1060             :         else
    1061          54 :             memset(&state->unique_check, 0, sizeof(state->unique_check));
    1062          90 :         MemoryContextSwitchTo(oldcontext);
    1063             : 
    1064          90 :         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
    1065             : 
    1066          90 :         if (arg_type == InvalidOid)
    1067           0 :             ereport(ERROR,
    1068             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1069             :                      errmsg("could not determine data type for argument %d", 1)));
    1070             : 
    1071          90 :         json_categorize_type(arg_type, &state->key_category,
    1072             :                              &state->key_output_func);
    1073             : 
    1074          90 :         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
    1075             : 
    1076          90 :         if (arg_type == InvalidOid)
    1077           0 :             ereport(ERROR,
    1078             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1079             :                      errmsg("could not determine data type for argument %d", 2)));
    1080             : 
    1081          90 :         json_categorize_type(arg_type, &state->val_category,
    1082             :                              &state->val_output_func);
    1083             : 
    1084          90 :         appendStringInfoString(state->str, "{ ");
    1085             :     }
    1086             :     else
    1087             :     {
    1088         180 :         state = (JsonAggState *) PG_GETARG_POINTER(0);
    1089             :     }
    1090             : 
    1091             :     /*
    1092             :      * Note: since json_object_agg() is declared as taking type "any", the
    1093             :      * parser will not do any type conversion on unknown-type literals (that
    1094             :      * is, undecorated strings or NULLs).  Such values will arrive here as
    1095             :      * type UNKNOWN, which fortunately does not matter to us, since
    1096             :      * unknownout() works fine.
    1097             :      */
    1098             : 
    1099         270 :     if (PG_ARGISNULL(1))
    1100          12 :         ereport(ERROR,
    1101             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1102             :                  errmsg("field name must not be null")));
    1103             : 
    1104             :     /* Skip null values if absent_on_null */
    1105         258 :     skip = absent_on_null && PG_ARGISNULL(2);
    1106             : 
    1107         258 :     if (skip)
    1108             :     {
    1109             :         /* If key uniqueness check is needed we must save skipped keys */
    1110          54 :         if (!unique_keys)
    1111          24 :             PG_RETURN_POINTER(state);
    1112             : 
    1113          30 :         out = json_unique_builder_get_skipped_keys(&state->unique_check);
    1114             :     }
    1115             :     else
    1116             :     {
    1117         204 :         out = state->str;
    1118             : 
    1119             :         /*
    1120             :          * Append comma delimiter only if we have already outputted some
    1121             :          * fields after the initial string "{ ".
    1122             :          */
    1123         204 :         if (out->len > 2)
    1124         120 :             appendStringInfoString(out, ", ");
    1125             :     }
    1126             : 
    1127         234 :     arg = PG_GETARG_DATUM(1);
    1128             : 
    1129         234 :     key_offset = out->len;
    1130             : 
    1131         234 :     datum_to_json(arg, false, out, state->key_category,
    1132             :                   state->key_output_func, true);
    1133             : 
    1134         234 :     if (unique_keys)
    1135             :     {
    1136          90 :         const char *key = &out->data[key_offset];
    1137             : 
    1138          90 :         if (!json_unique_check_key(&state->unique_check.check, key, 0))
    1139          24 :             ereport(ERROR,
    1140             :                     (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
    1141             :                      errmsg("duplicate JSON key %s", key)));
    1142             : 
    1143          66 :         if (skip)
    1144          18 :             PG_RETURN_POINTER(state);
    1145             :     }
    1146             : 
    1147         192 :     appendStringInfoString(state->str, " : ");
    1148             : 
    1149         192 :     if (PG_ARGISNULL(2))
    1150          12 :         arg = (Datum) 0;
    1151             :     else
    1152         180 :         arg = PG_GETARG_DATUM(2);
    1153             : 
    1154         192 :     datum_to_json(arg, PG_ARGISNULL(2), state->str, state->val_category,
    1155             :                   state->val_output_func, false);
    1156             : 
    1157         192 :     PG_RETURN_POINTER(state);
    1158             : }
    1159             : 
    1160             : /*
    1161             :  * json_object_agg aggregate function
    1162             :  */
    1163             : Datum
    1164         120 : json_object_agg_transfn(PG_FUNCTION_ARGS)
    1165             : {
    1166         120 :     return json_object_agg_transfn_worker(fcinfo, false, false);
    1167             : }
    1168             : 
    1169             : /*
    1170             :  * json_object_agg_strict aggregate function
    1171             :  */
    1172             : Datum
    1173          60 : json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
    1174             : {
    1175          60 :     return json_object_agg_transfn_worker(fcinfo, true, false);
    1176             : }
    1177             : 
    1178             : /*
    1179             :  * json_object_agg_unique aggregate function
    1180             :  */
    1181             : Datum
    1182          36 : json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
    1183             : {
    1184          36 :     return json_object_agg_transfn_worker(fcinfo, false, true);
    1185             : }
    1186             : 
    1187             : /*
    1188             :  * json_object_agg_unique_strict aggregate function
    1189             :  */
    1190             : Datum
    1191          54 : json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
    1192             : {
    1193          54 :     return json_object_agg_transfn_worker(fcinfo, true, true);
    1194             : }
    1195             : 
    1196             : /*
    1197             :  * json_object_agg final function.
    1198             :  */
    1199             : Datum
    1200          72 : json_object_agg_finalfn(PG_FUNCTION_ARGS)
    1201             : {
    1202             :     JsonAggState *state;
    1203             : 
    1204             :     /* cannot be called directly because of internal-type argument */
    1205             :     Assert(AggCheckCallContext(fcinfo, NULL));
    1206             : 
    1207          72 :     state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
    1208             : 
    1209             :     /* NULL result for no rows in, as is standard with aggregates */
    1210          72 :     if (state == NULL)
    1211           6 :         PG_RETURN_NULL();
    1212             : 
    1213             :     /* Else return state with appropriate object terminator added */
    1214          66 :     PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
    1215             : }
    1216             : 
    1217             : /*
    1218             :  * Helper function for aggregates: return given StringInfo's contents plus
    1219             :  * specified trailing string, as a text datum.  We need this because aggregate
    1220             :  * final functions are not allowed to modify the aggregate state.
    1221             :  */
    1222             : static text *
    1223         178 : catenate_stringinfo_string(StringInfo buffer, const char *addon)
    1224             : {
    1225             :     /* custom version of cstring_to_text_with_len */
    1226         178 :     int         buflen = buffer->len;
    1227         178 :     int         addlen = strlen(addon);
    1228         178 :     text       *result = (text *) palloc(buflen + addlen + VARHDRSZ);
    1229             : 
    1230         178 :     SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
    1231         178 :     memcpy(VARDATA(result), buffer->data, buflen);
    1232         178 :     memcpy(VARDATA(result) + buflen, addon, addlen);
    1233             : 
    1234         178 :     return result;
    1235             : }
    1236             : 
    1237             : Datum
    1238         384 : json_build_object_worker(int nargs, Datum *args, bool *nulls, Oid *types,
    1239             :                          bool absent_on_null, bool unique_keys)
    1240             : {
    1241             :     int         i;
    1242         384 :     const char *sep = "";
    1243             :     StringInfo  result;
    1244             :     JsonUniqueBuilderState unique_check;
    1245             : 
    1246         384 :     if (nargs % 2 != 0)
    1247          18 :         ereport(ERROR,
    1248             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1249             :                  errmsg("argument list must have even number of elements"),
    1250             :         /* translator: %s is a SQL function name */
    1251             :                  errhint("The arguments of %s must consist of alternating keys and values.",
    1252             :                          "json_build_object()")));
    1253             : 
    1254         366 :     result = makeStringInfo();
    1255             : 
    1256         366 :     appendStringInfoChar(result, '{');
    1257             : 
    1258         366 :     if (unique_keys)
    1259          24 :         json_unique_builder_init(&unique_check);
    1260             : 
    1261         768 :     for (i = 0; i < nargs; i += 2)
    1262             :     {
    1263             :         StringInfo  out;
    1264             :         bool        skip;
    1265             :         int         key_offset;
    1266             : 
    1267             :         /* Skip null values if absent_on_null */
    1268         486 :         skip = absent_on_null && nulls[i + 1];
    1269             : 
    1270         486 :         if (skip)
    1271             :         {
    1272             :             /* If key uniqueness check is needed we must save skipped keys */
    1273          24 :             if (!unique_keys)
    1274          12 :                 continue;
    1275             : 
    1276          12 :             out = json_unique_builder_get_skipped_keys(&unique_check);
    1277             :         }
    1278             :         else
    1279             :         {
    1280         462 :             appendStringInfoString(result, sep);
    1281         462 :             sep = ", ";
    1282         462 :             out = result;
    1283             :         }
    1284             : 
    1285             :         /* process key */
    1286         474 :         if (nulls[i])
    1287          24 :             ereport(ERROR,
    1288             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1289             :                      errmsg("argument %d cannot be null", i + 1),
    1290             :                      errhint("Object keys should be text.")));
    1291             : 
    1292             :         /* save key offset before key appending */
    1293         450 :         key_offset = out->len;
    1294             : 
    1295         450 :         add_json(args[i], false, out, types[i], true);
    1296             : 
    1297         414 :         if (unique_keys)
    1298             :         {
    1299             :             /* check key uniqueness after key appending */
    1300          60 :             const char *key = &out->data[key_offset];
    1301             : 
    1302          60 :             if (!json_unique_check_key(&unique_check.check, key, 0))
    1303          24 :                 ereport(ERROR,
    1304             :                         (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
    1305             :                          errmsg("duplicate JSON key %s", key)));
    1306             : 
    1307          36 :             if (skip)
    1308           6 :                 continue;
    1309             :         }
    1310             : 
    1311         384 :         appendStringInfoString(result, " : ");
    1312             : 
    1313             :         /* process value */
    1314         384 :         add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
    1315             :     }
    1316             : 
    1317         282 :     appendStringInfoChar(result, '}');
    1318             : 
    1319         282 :     return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
    1320             : }
    1321             : 
    1322             : /*
    1323             :  * SQL function json_build_object(variadic "any")
    1324             :  */
    1325             : Datum
    1326         156 : json_build_object(PG_FUNCTION_ARGS)
    1327             : {
    1328             :     Datum      *args;
    1329             :     bool       *nulls;
    1330             :     Oid        *types;
    1331             : 
    1332             :     /* build argument values to build the object */
    1333         156 :     int         nargs = extract_variadic_args(fcinfo, 0, true,
    1334             :                                               &args, &types, &nulls);
    1335             : 
    1336         156 :     if (nargs < 0)
    1337           6 :         PG_RETURN_NULL();
    1338             : 
    1339         150 :     PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
    1340             : }
    1341             : 
    1342             : /*
    1343             :  * degenerate case of json_build_object where it gets 0 arguments.
    1344             :  */
    1345             : Datum
    1346           6 : json_build_object_noargs(PG_FUNCTION_ARGS)
    1347             : {
    1348           6 :     PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
    1349             : }
    1350             : 
    1351             : Datum
    1352         186 : json_build_array_worker(int nargs, Datum *args, bool *nulls, Oid *types,
    1353             :                         bool absent_on_null)
    1354             : {
    1355             :     int         i;
    1356         186 :     const char *sep = "";
    1357             :     StringInfo  result;
    1358             : 
    1359         186 :     result = makeStringInfo();
    1360             : 
    1361         186 :     appendStringInfoChar(result, '[');
    1362             : 
    1363         522 :     for (i = 0; i < nargs; i++)
    1364             :     {
    1365         336 :         if (absent_on_null && nulls[i])
    1366          18 :             continue;
    1367             : 
    1368         318 :         appendStringInfoString(result, sep);
    1369         318 :         sep = ", ";
    1370         318 :         add_json(args[i], nulls[i], result, types[i], false);
    1371             :     }
    1372             : 
    1373         186 :     appendStringInfoChar(result, ']');
    1374             : 
    1375         186 :     return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
    1376             : }
    1377             : 
    1378             : /*
    1379             :  * SQL function json_build_array(variadic "any")
    1380             :  */
    1381             : Datum
    1382          54 : json_build_array(PG_FUNCTION_ARGS)
    1383             : {
    1384             :     Datum      *args;
    1385             :     bool       *nulls;
    1386             :     Oid        *types;
    1387             : 
    1388             :     /* build argument values to build the object */
    1389          54 :     int         nargs = extract_variadic_args(fcinfo, 0, true,
    1390             :                                               &args, &types, &nulls);
    1391             : 
    1392          54 :     if (nargs < 0)
    1393           6 :         PG_RETURN_NULL();
    1394             : 
    1395          48 :     PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
    1396             : }
    1397             : 
    1398             : /*
    1399             :  * degenerate case of json_build_array where it gets 0 arguments.
    1400             :  */
    1401             : Datum
    1402           6 : json_build_array_noargs(PG_FUNCTION_ARGS)
    1403             : {
    1404           6 :     PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
    1405             : }
    1406             : 
    1407             : /*
    1408             :  * SQL function json_object(text[])
    1409             :  *
    1410             :  * take a one or two dimensional array of text as key/value pairs
    1411             :  * for a json object.
    1412             :  */
    1413             : Datum
    1414          48 : json_object(PG_FUNCTION_ARGS)
    1415             : {
    1416          48 :     ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
    1417          48 :     int         ndims = ARR_NDIM(in_array);
    1418             :     StringInfoData result;
    1419             :     Datum      *in_datums;
    1420             :     bool       *in_nulls;
    1421             :     int         in_count,
    1422             :                 count,
    1423             :                 i;
    1424             :     text       *rval;
    1425             :     char       *v;
    1426             : 
    1427          48 :     switch (ndims)
    1428             :     {
    1429           6 :         case 0:
    1430           6 :             PG_RETURN_DATUM(CStringGetTextDatum("{}"));
    1431             :             break;
    1432             : 
    1433          18 :         case 1:
    1434          18 :             if ((ARR_DIMS(in_array)[0]) % 2)
    1435           6 :                 ereport(ERROR,
    1436             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1437             :                          errmsg("array must have even number of elements")));
    1438          12 :             break;
    1439             : 
    1440          18 :         case 2:
    1441          18 :             if ((ARR_DIMS(in_array)[1]) != 2)
    1442          12 :                 ereport(ERROR,
    1443             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1444             :                          errmsg("array must have two columns")));
    1445           6 :             break;
    1446             : 
    1447           6 :         default:
    1448           6 :             ereport(ERROR,
    1449             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1450             :                      errmsg("wrong number of array subscripts")));
    1451             :     }
    1452             : 
    1453          18 :     deconstruct_array(in_array,
    1454             :                       TEXTOID, -1, false, TYPALIGN_INT,
    1455             :                       &in_datums, &in_nulls, &in_count);
    1456             : 
    1457          18 :     count = in_count / 2;
    1458             : 
    1459          18 :     initStringInfo(&result);
    1460             : 
    1461          18 :     appendStringInfoChar(&result, '{');
    1462             : 
    1463        1866 :     for (i = 0; i < count; ++i)
    1464             :     {
    1465        1848 :         if (in_nulls[i * 2])
    1466           0 :             ereport(ERROR,
    1467             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1468             :                      errmsg("null value not allowed for object key")));
    1469             : 
    1470        1848 :         v = TextDatumGetCString(in_datums[i * 2]);
    1471        1848 :         if (i > 0)
    1472        1830 :             appendStringInfoString(&result, ", ");
    1473        1848 :         escape_json(&result, v);
    1474        1848 :         appendStringInfoString(&result, " : ");
    1475        1848 :         pfree(v);
    1476        1848 :         if (in_nulls[i * 2 + 1])
    1477          12 :             appendStringInfoString(&result, "null");
    1478             :         else
    1479             :         {
    1480        1836 :             v = TextDatumGetCString(in_datums[i * 2 + 1]);
    1481        1836 :             escape_json(&result, v);
    1482        1836 :             pfree(v);
    1483             :         }
    1484             :     }
    1485             : 
    1486          18 :     appendStringInfoChar(&result, '}');
    1487             : 
    1488          18 :     pfree(in_datums);
    1489          18 :     pfree(in_nulls);
    1490             : 
    1491          18 :     rval = cstring_to_text_with_len(result.data, result.len);
    1492          18 :     pfree(result.data);
    1493             : 
    1494          18 :     PG_RETURN_TEXT_P(rval);
    1495             : }
    1496             : 
    1497             : /*
    1498             :  * SQL function json_object(text[], text[])
    1499             :  *
    1500             :  * take separate key and value arrays of text to construct a json object
    1501             :  * pairwise.
    1502             :  */
    1503             : Datum
    1504          42 : json_object_two_arg(PG_FUNCTION_ARGS)
    1505             : {
    1506          42 :     ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
    1507          42 :     ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
    1508          42 :     int         nkdims = ARR_NDIM(key_array);
    1509          42 :     int         nvdims = ARR_NDIM(val_array);
    1510             :     StringInfoData result;
    1511             :     Datum      *key_datums,
    1512             :                *val_datums;
    1513             :     bool       *key_nulls,
    1514             :                *val_nulls;
    1515             :     int         key_count,
    1516             :                 val_count,
    1517             :                 i;
    1518             :     text       *rval;
    1519             :     char       *v;
    1520             : 
    1521          42 :     if (nkdims > 1 || nkdims != nvdims)
    1522           6 :         ereport(ERROR,
    1523             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1524             :                  errmsg("wrong number of array subscripts")));
    1525             : 
    1526          36 :     if (nkdims == 0)
    1527           6 :         PG_RETURN_DATUM(CStringGetTextDatum("{}"));
    1528             : 
    1529          30 :     deconstruct_array(key_array,
    1530             :                       TEXTOID, -1, false, TYPALIGN_INT,
    1531             :                       &key_datums, &key_nulls, &key_count);
    1532             : 
    1533          30 :     deconstruct_array(val_array,
    1534             :                       TEXTOID, -1, false, TYPALIGN_INT,
    1535             :                       &val_datums, &val_nulls, &val_count);
    1536             : 
    1537          30 :     if (key_count != val_count)
    1538          12 :         ereport(ERROR,
    1539             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1540             :                  errmsg("mismatched array dimensions")));
    1541             : 
    1542          18 :     initStringInfo(&result);
    1543             : 
    1544          18 :     appendStringInfoChar(&result, '{');
    1545             : 
    1546          78 :     for (i = 0; i < key_count; ++i)
    1547             :     {
    1548          66 :         if (key_nulls[i])
    1549           6 :             ereport(ERROR,
    1550             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1551             :                      errmsg("null value not allowed for object key")));
    1552             : 
    1553          60 :         v = TextDatumGetCString(key_datums[i]);
    1554          60 :         if (i > 0)
    1555          42 :             appendStringInfoString(&result, ", ");
    1556          60 :         escape_json(&result, v);
    1557          60 :         appendStringInfoString(&result, " : ");
    1558          60 :         pfree(v);
    1559          60 :         if (val_nulls[i])
    1560           0 :             appendStringInfoString(&result, "null");
    1561             :         else
    1562             :         {
    1563          60 :             v = TextDatumGetCString(val_datums[i]);
    1564          60 :             escape_json(&result, v);
    1565          60 :             pfree(v);
    1566             :         }
    1567             :     }
    1568             : 
    1569          12 :     appendStringInfoChar(&result, '}');
    1570             : 
    1571          12 :     pfree(key_datums);
    1572          12 :     pfree(key_nulls);
    1573          12 :     pfree(val_datums);
    1574          12 :     pfree(val_nulls);
    1575             : 
    1576          12 :     rval = cstring_to_text_with_len(result.data, result.len);
    1577          12 :     pfree(result.data);
    1578             : 
    1579          12 :     PG_RETURN_TEXT_P(rval);
    1580             : }
    1581             : 
    1582             : 
    1583             : /*
    1584             :  * Produce a JSON string literal, properly escaping characters in the text.
    1585             :  */
    1586             : void
    1587      301968 : escape_json(StringInfo buf, const char *str)
    1588             : {
    1589             :     const char *p;
    1590             : 
    1591      301968 :     appendStringInfoCharMacro(buf, '"');
    1592     3306728 :     for (p = str; *p; p++)
    1593             :     {
    1594     3004760 :         switch (*p)
    1595             :         {
    1596          18 :             case '\b':
    1597          18 :                 appendStringInfoString(buf, "\\b");
    1598          18 :                 break;
    1599           6 :             case '\f':
    1600           6 :                 appendStringInfoString(buf, "\\f");
    1601           6 :                 break;
    1602          24 :             case '\n':
    1603          24 :                 appendStringInfoString(buf, "\\n");
    1604          24 :                 break;
    1605           6 :             case '\r':
    1606           6 :                 appendStringInfoString(buf, "\\r");
    1607           6 :                 break;
    1608          12 :             case '\t':
    1609          12 :                 appendStringInfoString(buf, "\\t");
    1610          12 :                 break;
    1611         270 :             case '"':
    1612         270 :                 appendStringInfoString(buf, "\\\"");
    1613         270 :                 break;
    1614          90 :             case '\\':
    1615          90 :                 appendStringInfoString(buf, "\\\\");
    1616          90 :                 break;
    1617     3004334 :             default:
    1618     3004334 :                 if ((unsigned char) *p < ' ')
    1619           6 :                     appendStringInfo(buf, "\\u%04x", (int) *p);
    1620             :                 else
    1621     3004328 :                     appendStringInfoCharMacro(buf, *p);
    1622     3004334 :                 break;
    1623             :         }
    1624             :     }
    1625      301968 :     appendStringInfoCharMacro(buf, '"');
    1626      301968 : }
    1627             : 
    1628             : /* Semantic actions for key uniqueness check */
    1629             : static void
    1630         174 : json_unique_object_start(void *_state)
    1631             : {
    1632         174 :     JsonUniqueParsingState *state = _state;
    1633             :     JsonUniqueStackEntry *entry;
    1634             : 
    1635         174 :     if (!state->unique)
    1636           0 :         return;
    1637             : 
    1638             :     /* push object entry to stack */
    1639         174 :     entry = palloc(sizeof(*entry));
    1640         174 :     entry->object_id = state->id_counter++;
    1641         174 :     entry->parent = state->stack;
    1642         174 :     state->stack = entry;
    1643             : }
    1644             : 
    1645             : static void
    1646         168 : json_unique_object_end(void *_state)
    1647             : {
    1648         168 :     JsonUniqueParsingState *state = _state;
    1649             :     JsonUniqueStackEntry *entry;
    1650             : 
    1651         168 :     if (!state->unique)
    1652          60 :         return;
    1653             : 
    1654         108 :     entry = state->stack;
    1655         108 :     state->stack = entry->parent; /* pop object from stack */
    1656         108 :     pfree(entry);
    1657             : }
    1658             : 
    1659             : static void
    1660         246 : json_unique_object_field_start(void *_state, char *field, bool isnull)
    1661             : {
    1662         246 :     JsonUniqueParsingState *state = _state;
    1663             :     JsonUniqueStackEntry *entry;
    1664             : 
    1665         246 :     if (!state->unique)
    1666           0 :         return;
    1667             : 
    1668             :     /* find key collision in the current object */
    1669         246 :     if (json_unique_check_key(&state->check, field, state->stack->object_id))
    1670         204 :         return;
    1671             : 
    1672          42 :     state->unique = false;
    1673             : 
    1674             :     /* pop all objects entries */
    1675         102 :     while ((entry = state->stack))
    1676             :     {
    1677          60 :         state->stack = entry->parent;
    1678          60 :         pfree(entry);
    1679             :     }
    1680             : }
    1681             : 
    1682             : /* Validate JSON text and additionally check key uniqueness */
    1683             : bool
    1684        1314 : json_validate(text *json, bool check_unique_keys, bool throw_error)
    1685             : {
    1686        1314 :     JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
    1687        1314 :     JsonSemAction uniqueSemAction = {0};
    1688             :     JsonUniqueParsingState state;
    1689             :     JsonParseErrorType result;
    1690             : 
    1691        1314 :     if (check_unique_keys)
    1692             :     {
    1693         228 :         state.lex = lex;
    1694         228 :         state.stack = NULL;
    1695         228 :         state.id_counter = 0;
    1696         228 :         state.unique = true;
    1697         228 :         json_unique_check_init(&state.check);
    1698             : 
    1699         228 :         uniqueSemAction.semstate = &state;
    1700         228 :         uniqueSemAction.object_start = json_unique_object_start;
    1701         228 :         uniqueSemAction.object_field_start = json_unique_object_field_start;
    1702         228 :         uniqueSemAction.object_end = json_unique_object_end;
    1703             :     }
    1704             : 
    1705        1314 :     result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
    1706             : 
    1707        1314 :     if (result != JSON_SUCCESS)
    1708             :     {
    1709         216 :         if (throw_error)
    1710           0 :             json_ereport_error(result, lex);
    1711             : 
    1712         216 :         return false;           /* invalid json */
    1713             :     }
    1714             : 
    1715        1098 :     if (check_unique_keys && !state.unique)
    1716             :     {
    1717          42 :         if (throw_error)
    1718           6 :             ereport(ERROR,
    1719             :                     (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
    1720             :                      errmsg("duplicate JSON object key value")));
    1721             : 
    1722          36 :         return false;           /* not unique keys */
    1723             :     }
    1724             : 
    1725        1056 :     return true;                /* ok */
    1726             : }
    1727             : 
    1728             : /*
    1729             :  * SQL function json_typeof(json) -> text
    1730             :  *
    1731             :  * Returns the type of the outermost JSON value as TEXT.  Possible types are
    1732             :  * "object", "array", "string", "number", "boolean", and "null".
    1733             :  *
    1734             :  * Performs a single call to json_lex() to get the first token of the supplied
    1735             :  * value.  This initial token uniquely determines the value's type.  As our
    1736             :  * input must already have been validated by json_in() or json_recv(), the
    1737             :  * initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
    1738             :  * JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
    1739             :  */
    1740             : Datum
    1741          60 : json_typeof(PG_FUNCTION_ARGS)
    1742             : {
    1743          60 :     text       *json = PG_GETARG_TEXT_PP(0);
    1744             :     char       *type;
    1745             :     JsonTokenType tok;
    1746             : 
    1747             :     /* Lex exactly one token from the input and check its type. */
    1748          60 :     tok = json_get_first_token(json, true);
    1749             : 
    1750          60 :     switch (tok)
    1751             :     {
    1752          12 :         case JSON_TOKEN_OBJECT_START:
    1753          12 :             type = "object";
    1754          12 :             break;
    1755          12 :         case JSON_TOKEN_ARRAY_START:
    1756          12 :             type = "array";
    1757          12 :             break;
    1758           6 :         case JSON_TOKEN_STRING:
    1759           6 :             type = "string";
    1760           6 :             break;
    1761          12 :         case JSON_TOKEN_NUMBER:
    1762          12 :             type = "number";
    1763          12 :             break;
    1764          12 :         case JSON_TOKEN_TRUE:
    1765             :         case JSON_TOKEN_FALSE:
    1766          12 :             type = "boolean";
    1767          12 :             break;
    1768           6 :         case JSON_TOKEN_NULL:
    1769           6 :             type = "null";
    1770           6 :             break;
    1771           0 :         default:
    1772           0 :             elog(ERROR, "unexpected json token: %d", tok);
    1773             :     }
    1774             : 
    1775          60 :     PG_RETURN_TEXT_P(cstring_to_text(type));
    1776             : }

Generated by: LCOV version 1.14