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

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

Generated by: LCOV version 1.16