LCOV - code coverage report
Current view: top level - src/backend/utils/adt - json.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 630 682 92.4 %
Date: 2024-11-21 08:14:44 Functions: 48 51 94.1 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14