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

Generated by: LCOV version 1.14