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

Generated by: LCOV version 1.14