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

Generated by: LCOV version 2.0-1