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

Generated by: LCOV version 2.0-1