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

Generated by: LCOV version 1.14