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

Generated by: LCOV version 1.16