LCOV - code coverage report
Current view: top level - src/backend/utils/adt - json.c (source / functions) Hit Total Coverage
Test: PostgreSQL 14devel Lines: 485 522 92.9 %
Date: 2020-08-05 08:07:15 Functions: 27 29 93.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * json.c
       4             :  *      JSON data type support.
       5             :  *
       6             :  * Portions Copyright (c) 1996-2020, 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_type.h"
      17             : #include "funcapi.h"
      18             : #include "libpq/pqformat.h"
      19             : #include "miscadmin.h"
      20             : #include "parser/parse_coerce.h"
      21             : #include "utils/array.h"
      22             : #include "utils/builtins.h"
      23             : #include "utils/date.h"
      24             : #include "utils/datetime.h"
      25             : #include "utils/json.h"
      26             : #include "utils/jsonfuncs.h"
      27             : #include "utils/lsyscache.h"
      28             : #include "utils/typcache.h"
      29             : 
      30             : typedef enum                    /* type categories for datum_to_json */
      31             : {
      32             :     JSONTYPE_NULL,              /* null, so we didn't bother to identify */
      33             :     JSONTYPE_BOOL,              /* boolean (built-in types only) */
      34             :     JSONTYPE_NUMERIC,           /* numeric (ditto) */
      35             :     JSONTYPE_DATE,              /* we use special formatting for datetimes */
      36             :     JSONTYPE_TIMESTAMP,
      37             :     JSONTYPE_TIMESTAMPTZ,
      38             :     JSONTYPE_JSON,              /* JSON itself (and JSONB) */
      39             :     JSONTYPE_ARRAY,             /* array */
      40             :     JSONTYPE_COMPOSITE,         /* composite */
      41             :     JSONTYPE_CAST,              /* something with an explicit cast to JSON */
      42             :     JSONTYPE_OTHER              /* all else */
      43             : } JsonTypeCategory;
      44             : 
      45             : typedef struct JsonAggState
      46             : {
      47             :     StringInfo  str;
      48             :     JsonTypeCategory key_category;
      49             :     Oid         key_output_func;
      50             :     JsonTypeCategory val_category;
      51             :     Oid         val_output_func;
      52             : } JsonAggState;
      53             : 
      54             : static void composite_to_json(Datum composite, StringInfo result,
      55             :                               bool use_line_feeds);
      56             : static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
      57             :                               Datum *vals, bool *nulls, int *valcount,
      58             :                               JsonTypeCategory tcategory, Oid outfuncoid,
      59             :                               bool use_line_feeds);
      60             : static void array_to_json_internal(Datum array, StringInfo result,
      61             :                                    bool use_line_feeds);
      62             : static void json_categorize_type(Oid typoid,
      63             :                                  JsonTypeCategory *tcategory,
      64             :                                  Oid *outfuncoid);
      65             : static void datum_to_json(Datum val, bool is_null, StringInfo result,
      66             :                           JsonTypeCategory tcategory, Oid outfuncoid,
      67             :                           bool key_scalar);
      68             : static void add_json(Datum val, bool is_null, StringInfo result,
      69             :                      Oid val_type, bool key_scalar);
      70             : static text *catenate_stringinfo_string(StringInfo buffer, const char *addon);
      71             : 
      72             : /*
      73             :  * Input.
      74             :  */
      75             : Datum
      76        2784 : json_in(PG_FUNCTION_ARGS)
      77             : {
      78        2784 :     char       *json = PG_GETARG_CSTRING(0);
      79        2784 :     text       *result = cstring_to_text(json);
      80             :     JsonLexContext *lex;
      81             : 
      82             :     /* validate it */
      83        2784 :     lex = makeJsonLexContext(result, false);
      84        2784 :     pg_parse_json_or_ereport(lex, &nullSemAction);
      85             : 
      86             :     /* Internal representation is the same as text, for now */
      87        2668 :     PG_RETURN_TEXT_P(result);
      88             : }
      89             : 
      90             : /*
      91             :  * Output.
      92             :  */
      93             : Datum
      94        1374 : json_out(PG_FUNCTION_ARGS)
      95             : {
      96             :     /* we needn't detoast because text_to_cstring will handle that */
      97        1374 :     Datum       txt = PG_GETARG_DATUM(0);
      98             : 
      99        1374 :     PG_RETURN_CSTRING(TextDatumGetCString(txt));
     100             : }
     101             : 
     102             : /*
     103             :  * Binary send.
     104             :  */
     105             : Datum
     106           0 : json_send(PG_FUNCTION_ARGS)
     107             : {
     108           0 :     text       *t = PG_GETARG_TEXT_PP(0);
     109             :     StringInfoData buf;
     110             : 
     111           0 :     pq_begintypsend(&buf);
     112           0 :     pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
     113           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     114             : }
     115             : 
     116             : /*
     117             :  * Binary receive.
     118             :  */
     119             : Datum
     120           0 : json_recv(PG_FUNCTION_ARGS)
     121             : {
     122           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     123             :     char       *str;
     124             :     int         nbytes;
     125             :     JsonLexContext *lex;
     126             : 
     127           0 :     str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     128             : 
     129             :     /* Validate it. */
     130           0 :     lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
     131           0 :     pg_parse_json_or_ereport(lex, &nullSemAction);
     132             : 
     133           0 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
     134             : }
     135             : 
     136             : /*
     137             :  * Determine how we want to print values of a given type in datum_to_json.
     138             :  *
     139             :  * Given the datatype OID, return its JsonTypeCategory, as well as the type's
     140             :  * output function OID.  If the returned category is JSONTYPE_CAST, we
     141             :  * return the OID of the type->JSON cast function instead.
     142             :  */
     143             : static void
     144        2040 : json_categorize_type(Oid typoid,
     145             :                      JsonTypeCategory *tcategory,
     146             :                      Oid *outfuncoid)
     147             : {
     148             :     bool        typisvarlena;
     149             : 
     150             :     /* Look through any domain */
     151        2040 :     typoid = getBaseType(typoid);
     152             : 
     153        2040 :     *outfuncoid = InvalidOid;
     154             : 
     155             :     /*
     156             :      * We need to get the output function for everything except date and
     157             :      * timestamp types, array and composite types, booleans, and non-builtin
     158             :      * types where there's a cast to json.
     159             :      */
     160             : 
     161        2040 :     switch (typoid)
     162             :     {
     163          12 :         case BOOLOID:
     164          12 :             *tcategory = JSONTYPE_BOOL;
     165          12 :             break;
     166             : 
     167        1248 :         case INT2OID:
     168             :         case INT4OID:
     169             :         case INT8OID:
     170             :         case FLOAT4OID:
     171             :         case FLOAT8OID:
     172             :         case NUMERICOID:
     173        1248 :             getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     174        1248 :             *tcategory = JSONTYPE_NUMERIC;
     175        1248 :             break;
     176             : 
     177          12 :         case DATEOID:
     178          12 :             *tcategory = JSONTYPE_DATE;
     179          12 :             break;
     180             : 
     181          12 :         case TIMESTAMPOID:
     182          12 :             *tcategory = JSONTYPE_TIMESTAMP;
     183          12 :             break;
     184             : 
     185          16 :         case TIMESTAMPTZOID:
     186          16 :             *tcategory = JSONTYPE_TIMESTAMPTZ;
     187          16 :             break;
     188             : 
     189          44 :         case JSONOID:
     190             :         case JSONBOID:
     191          44 :             getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     192          44 :             *tcategory = JSONTYPE_JSON;
     193          44 :             break;
     194             : 
     195         696 :         default:
     196             :             /* Check for arrays and composites */
     197         696 :             if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
     198         480 :                 || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
     199         216 :                 *tcategory = JSONTYPE_ARRAY;
     200         480 :             else if (type_is_rowtype(typoid))   /* includes RECORDOID */
     201         136 :                 *tcategory = JSONTYPE_COMPOSITE;
     202             :             else
     203             :             {
     204             :                 /* It's probably the general case ... */
     205         344 :                 *tcategory = JSONTYPE_OTHER;
     206             :                 /* but let's look for a cast to json, if it's not built-in */
     207         344 :                 if (typoid >= FirstNormalObjectId)
     208             :                 {
     209             :                     Oid         castfunc;
     210             :                     CoercionPathType ctype;
     211             : 
     212           4 :                     ctype = find_coercion_pathway(JSONOID, typoid,
     213             :                                                   COERCION_EXPLICIT,
     214             :                                                   &castfunc);
     215           4 :                     if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
     216             :                     {
     217           4 :                         *tcategory = JSONTYPE_CAST;
     218           4 :                         *outfuncoid = castfunc;
     219             :                     }
     220             :                     else
     221             :                     {
     222             :                         /* non builtin type with no cast */
     223           0 :                         getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     224             :                     }
     225             :                 }
     226             :                 else
     227             :                 {
     228             :                     /* any other builtin type */
     229         340 :                     getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
     230             :                 }
     231             :             }
     232         696 :             break;
     233             :     }
     234        2040 : }
     235             : 
     236             : /*
     237             :  * Turn a Datum into JSON text, appending the string to "result".
     238             :  *
     239             :  * tcategory and outfuncoid are from a previous call to json_categorize_type,
     240             :  * except that if is_null is true then they can be invalid.
     241             :  *
     242             :  * If key_scalar is true, the value is being printed as a key, so insist
     243             :  * it's of an acceptable type, and force it to be quoted.
     244             :  */
     245             : static void
     246        2672 : datum_to_json(Datum val, bool is_null, StringInfo result,
     247             :               JsonTypeCategory tcategory, Oid outfuncoid,
     248             :               bool key_scalar)
     249             : {
     250             :     char       *outputstr;
     251             :     text       *jsontext;
     252             : 
     253        2672 :     check_stack_depth();
     254             : 
     255             :     /* callers are expected to ensure that null keys are not passed in */
     256             :     Assert(!(key_scalar && is_null));
     257             : 
     258        2672 :     if (is_null)
     259             :     {
     260         124 :         appendStringInfoString(result, "null");
     261         124 :         return;
     262             :     }
     263             : 
     264        2548 :     if (key_scalar &&
     265         156 :         (tcategory == JSONTYPE_ARRAY ||
     266         152 :          tcategory == JSONTYPE_COMPOSITE ||
     267         148 :          tcategory == JSONTYPE_JSON ||
     268             :          tcategory == JSONTYPE_CAST))
     269          16 :         ereport(ERROR,
     270             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     271             :                  errmsg("key value must be scalar, not array, composite, or json")));
     272             : 
     273        2532 :     switch (tcategory)
     274             :     {
     275         208 :         case JSONTYPE_ARRAY:
     276         208 :             array_to_json_internal(val, result, false);
     277         208 :             break;
     278         256 :         case JSONTYPE_COMPOSITE:
     279         256 :             composite_to_json(val, result, false);
     280         256 :             break;
     281          12 :         case JSONTYPE_BOOL:
     282          12 :             outputstr = DatumGetBool(val) ? "true" : "false";
     283          12 :             if (key_scalar)
     284           0 :                 escape_json(result, outputstr);
     285             :             else
     286          12 :                 appendStringInfoString(result, outputstr);
     287          12 :             break;
     288        1580 :         case JSONTYPE_NUMERIC:
     289        1580 :             outputstr = OidOutputFunctionCall(outfuncoid, val);
     290             : 
     291             :             /*
     292             :              * Don't call escape_json for a non-key if it's a valid JSON
     293             :              * number.
     294             :              */
     295        1580 :             if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
     296        1532 :                 appendStringInfoString(result, outputstr);
     297             :             else
     298          48 :                 escape_json(result, outputstr);
     299        1580 :             pfree(outputstr);
     300        1580 :             break;
     301          12 :         case JSONTYPE_DATE:
     302          12 :             {
     303             :                 char        buf[MAXDATELEN + 1];
     304             : 
     305          12 :                 JsonEncodeDateTime(buf, val, DATEOID, NULL);
     306          12 :                 appendStringInfo(result, "\"%s\"", buf);
     307             :             }
     308          12 :             break;
     309          12 :         case JSONTYPE_TIMESTAMP:
     310          12 :             {
     311             :                 char        buf[MAXDATELEN + 1];
     312             : 
     313          12 :                 JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
     314          12 :                 appendStringInfo(result, "\"%s\"", buf);
     315             :             }
     316          12 :             break;
     317          16 :         case JSONTYPE_TIMESTAMPTZ:
     318          16 :             {
     319             :                 char        buf[MAXDATELEN + 1];
     320             : 
     321          16 :                 JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
     322          16 :                 appendStringInfo(result, "\"%s\"", buf);
     323             :             }
     324          16 :             break;
     325          52 :         case JSONTYPE_JSON:
     326             :             /* JSON and JSONB output will already be escaped */
     327          52 :             outputstr = OidOutputFunctionCall(outfuncoid, val);
     328          52 :             appendStringInfoString(result, outputstr);
     329          52 :             pfree(outputstr);
     330          52 :             break;
     331           4 :         case JSONTYPE_CAST:
     332             :             /* outfuncoid refers to a cast function, not an output function */
     333           4 :             jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
     334           4 :             outputstr = text_to_cstring(jsontext);
     335           4 :             appendStringInfoString(result, outputstr);
     336           4 :             pfree(outputstr);
     337           4 :             pfree(jsontext);
     338           4 :             break;
     339         380 :         default:
     340         380 :             outputstr = OidOutputFunctionCall(outfuncoid, val);
     341         380 :             escape_json(result, outputstr);
     342         380 :             pfree(outputstr);
     343         380 :             break;
     344             :     }
     345             : }
     346             : 
     347             : /*
     348             :  * Encode 'value' of datetime type 'typid' into JSON string in ISO format using
     349             :  * optionally preallocated buffer 'buf'.  Optional 'tzp' determines time-zone
     350             :  * offset (in seconds) in which we want to show timestamptz.
     351             :  */
     352             : char *
     353         388 : JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
     354             : {
     355         388 :     if (!buf)
     356          40 :         buf = palloc(MAXDATELEN + 1);
     357             : 
     358         388 :     switch (typid)
     359             :     {
     360          64 :         case DATEOID:
     361          64 :             {
     362             :                 DateADT     date;
     363             :                 struct pg_tm tm;
     364             : 
     365          64 :                 date = DatumGetDateADT(value);
     366             : 
     367             :                 /* Same as date_out(), but forcing DateStyle */
     368          64 :                 if (DATE_NOT_FINITE(date))
     369          16 :                     EncodeSpecialDate(date, buf);
     370             :                 else
     371             :                 {
     372          48 :                     j2date(date + POSTGRES_EPOCH_JDATE,
     373             :                            &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
     374          48 :                     EncodeDateOnly(&tm, USE_XSD_DATES, buf);
     375             :                 }
     376             :             }
     377          64 :             break;
     378          40 :         case TIMEOID:
     379          40 :             {
     380          40 :                 TimeADT     time = DatumGetTimeADT(value);
     381             :                 struct pg_tm tt,
     382          40 :                            *tm = &tt;
     383             :                 fsec_t      fsec;
     384             : 
     385             :                 /* Same as time_out(), but forcing DateStyle */
     386          40 :                 time2tm(time, tm, &fsec);
     387          40 :                 EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
     388             :             }
     389          40 :             break;
     390          80 :         case TIMETZOID:
     391          80 :             {
     392          80 :                 TimeTzADT  *time = DatumGetTimeTzADTP(value);
     393             :                 struct pg_tm tt,
     394          80 :                            *tm = &tt;
     395             :                 fsec_t      fsec;
     396             :                 int         tz;
     397             : 
     398             :                 /* Same as timetz_out(), but forcing DateStyle */
     399          80 :                 timetz2tm(time, tm, &fsec, &tz);
     400          80 :                 EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
     401             :             }
     402          80 :             break;
     403          80 :         case TIMESTAMPOID:
     404          80 :             {
     405             :                 Timestamp   timestamp;
     406             :                 struct pg_tm tm;
     407             :                 fsec_t      fsec;
     408             : 
     409          80 :                 timestamp = DatumGetTimestamp(value);
     410             :                 /* Same as timestamp_out(), but forcing DateStyle */
     411          80 :                 if (TIMESTAMP_NOT_FINITE(timestamp))
     412          16 :                     EncodeSpecialTimestamp(timestamp, buf);
     413          64 :                 else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
     414          64 :                     EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
     415             :                 else
     416           0 :                     ereport(ERROR,
     417             :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     418             :                              errmsg("timestamp out of range")));
     419             :             }
     420          80 :             break;
     421         124 :         case TIMESTAMPTZOID:
     422         124 :             {
     423             :                 TimestampTz timestamp;
     424             :                 struct pg_tm tm;
     425             :                 int         tz;
     426             :                 fsec_t      fsec;
     427         124 :                 const char *tzn = NULL;
     428             : 
     429         124 :                 timestamp = DatumGetTimestampTz(value);
     430             : 
     431             :                 /*
     432             :                  * If a time zone is specified, we apply the time-zone shift,
     433             :                  * convert timestamptz to pg_tm as if it were without a time
     434             :                  * zone, and then use the specified time zone for converting
     435             :                  * the timestamp into a string.
     436             :                  */
     437         124 :                 if (tzp)
     438             :                 {
     439          92 :                     tz = *tzp;
     440          92 :                     timestamp -= (TimestampTz) tz * USECS_PER_SEC;
     441             :                 }
     442             : 
     443             :                 /* Same as timestamptz_out(), but forcing DateStyle */
     444         124 :                 if (TIMESTAMP_NOT_FINITE(timestamp))
     445          16 :                     EncodeSpecialTimestamp(timestamp, buf);
     446         108 :                 else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
     447             :                                       tzp ? NULL : &tzn, NULL) == 0)
     448             :                 {
     449         108 :                     if (tzp)
     450          92 :                         tm.tm_isdst = 1;    /* set time-zone presence flag */
     451             : 
     452         108 :                     EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
     453             :                 }
     454             :                 else
     455           0 :                     ereport(ERROR,
     456             :                             (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
     457             :                              errmsg("timestamp out of range")));
     458             :             }
     459         124 :             break;
     460           0 :         default:
     461           0 :             elog(ERROR, "unknown jsonb value datetime type oid %u", typid);
     462             :             return NULL;
     463             :     }
     464             : 
     465         388 :     return buf;
     466             : }
     467             : 
     468             : /*
     469             :  * Process a single dimension of an array.
     470             :  * If it's the innermost dimension, output the values, otherwise call
     471             :  * ourselves recursively to process the next dimension.
     472             :  */
     473             : static void
     474         244 : array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
     475             :                   bool *nulls, int *valcount, JsonTypeCategory tcategory,
     476             :                   Oid outfuncoid, bool use_line_feeds)
     477             : {
     478             :     int         i;
     479             :     const char *sep;
     480             : 
     481             :     Assert(dim < ndims);
     482             : 
     483         244 :     sep = use_line_feeds ? ",\n " : ",";
     484             : 
     485         244 :     appendStringInfoChar(result, '[');
     486             : 
     487         916 :     for (i = 1; i <= dims[dim]; i++)
     488             :     {
     489         672 :         if (i > 1)
     490         428 :             appendStringInfoString(result, sep);
     491             : 
     492         672 :         if (dim + 1 == ndims)
     493             :         {
     494         664 :             datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
     495             :                           outfuncoid, false);
     496         664 :             (*valcount)++;
     497             :         }
     498             :         else
     499             :         {
     500             :             /*
     501             :              * Do we want line feeds on inner dimensions of arrays? For now
     502             :              * we'll say no.
     503             :              */
     504           8 :             array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
     505             :                               valcount, tcategory, outfuncoid, false);
     506             :         }
     507             :     }
     508             : 
     509         244 :     appendStringInfoChar(result, ']');
     510         244 : }
     511             : 
     512             : /*
     513             :  * Turn an array into JSON.
     514             :  */
     515             : static void
     516         236 : array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
     517             : {
     518         236 :     ArrayType  *v = DatumGetArrayTypeP(array);
     519         236 :     Oid         element_type = ARR_ELEMTYPE(v);
     520             :     int        *dim;
     521             :     int         ndim;
     522             :     int         nitems;
     523         236 :     int         count = 0;
     524             :     Datum      *elements;
     525             :     bool       *nulls;
     526             :     int16       typlen;
     527             :     bool        typbyval;
     528             :     char        typalign;
     529             :     JsonTypeCategory tcategory;
     530             :     Oid         outfuncoid;
     531             : 
     532         236 :     ndim = ARR_NDIM(v);
     533         236 :     dim = ARR_DIMS(v);
     534         236 :     nitems = ArrayGetNItems(ndim, dim);
     535             : 
     536         236 :     if (nitems <= 0)
     537             :     {
     538           0 :         appendStringInfoString(result, "[]");
     539           0 :         return;
     540             :     }
     541             : 
     542         236 :     get_typlenbyvalalign(element_type,
     543             :                          &typlen, &typbyval, &typalign);
     544             : 
     545         236 :     json_categorize_type(element_type,
     546             :                          &tcategory, &outfuncoid);
     547             : 
     548         236 :     deconstruct_array(v, element_type, typlen, typbyval,
     549             :                       typalign, &elements, &nulls,
     550             :                       &nitems);
     551             : 
     552         236 :     array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
     553             :                       outfuncoid, use_line_feeds);
     554             : 
     555         236 :     pfree(elements);
     556         236 :     pfree(nulls);
     557             : }
     558             : 
     559             : /*
     560             :  * Turn a composite / record into JSON.
     561             :  */
     562             : static void
     563         636 : composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
     564             : {
     565             :     HeapTupleHeader td;
     566             :     Oid         tupType;
     567             :     int32       tupTypmod;
     568             :     TupleDesc   tupdesc;
     569             :     HeapTupleData tmptup,
     570             :                *tuple;
     571             :     int         i;
     572         636 :     bool        needsep = false;
     573             :     const char *sep;
     574             : 
     575         636 :     sep = use_line_feeds ? ",\n " : ",";
     576             : 
     577         636 :     td = DatumGetHeapTupleHeader(composite);
     578             : 
     579             :     /* Extract rowtype info and find a tupdesc */
     580         636 :     tupType = HeapTupleHeaderGetTypeId(td);
     581         636 :     tupTypmod = HeapTupleHeaderGetTypMod(td);
     582         636 :     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
     583             : 
     584             :     /* Build a temporary HeapTuple control structure */
     585         636 :     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
     586         636 :     tmptup.t_data = td;
     587         636 :     tuple = &tmptup;
     588             : 
     589         636 :     appendStringInfoChar(result, '{');
     590             : 
     591        2072 :     for (i = 0; i < tupdesc->natts; i++)
     592             :     {
     593             :         Datum       val;
     594             :         bool        isnull;
     595             :         char       *attname;
     596             :         JsonTypeCategory tcategory;
     597             :         Oid         outfuncoid;
     598        1436 :         Form_pg_attribute att = TupleDescAttr(tupdesc, i);
     599             : 
     600        1436 :         if (att->attisdropped)
     601           0 :             continue;
     602             : 
     603        1436 :         if (needsep)
     604         800 :             appendStringInfoString(result, sep);
     605        1436 :         needsep = true;
     606             : 
     607        1436 :         attname = NameStr(att->attname);
     608        1436 :         escape_json(result, attname);
     609        1436 :         appendStringInfoChar(result, ':');
     610             : 
     611        1436 :         val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
     612             : 
     613        1436 :         if (isnull)
     614             :         {
     615         100 :             tcategory = JSONTYPE_NULL;
     616         100 :             outfuncoid = InvalidOid;
     617             :         }
     618             :         else
     619        1336 :             json_categorize_type(att->atttypid, &tcategory, &outfuncoid);
     620             : 
     621        1436 :         datum_to_json(val, isnull, result, tcategory, outfuncoid, false);
     622             :     }
     623             : 
     624         636 :     appendStringInfoChar(result, '}');
     625         636 :     ReleaseTupleDesc(tupdesc);
     626         636 : }
     627             : 
     628             : /*
     629             :  * Append JSON text for "val" to "result".
     630             :  *
     631             :  * This is just a thin wrapper around datum_to_json.  If the same type will be
     632             :  * printed many times, avoid using this; better to do the json_categorize_type
     633             :  * lookups only once.
     634             :  */
     635             : static void
     636         364 : add_json(Datum val, bool is_null, StringInfo result,
     637             :          Oid val_type, bool key_scalar)
     638             : {
     639             :     JsonTypeCategory tcategory;
     640             :     Oid         outfuncoid;
     641             : 
     642         364 :     if (val_type == InvalidOid)
     643           0 :         ereport(ERROR,
     644             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     645             :                  errmsg("could not determine input data type")));
     646             : 
     647         364 :     if (is_null)
     648             :     {
     649          24 :         tcategory = JSONTYPE_NULL;
     650          24 :         outfuncoid = InvalidOid;
     651             :     }
     652             :     else
     653         340 :         json_categorize_type(val_type,
     654             :                              &tcategory, &outfuncoid);
     655             : 
     656         364 :     datum_to_json(val, is_null, result, tcategory, outfuncoid, key_scalar);
     657         348 : }
     658             : 
     659             : /*
     660             :  * SQL function array_to_json(row)
     661             :  */
     662             : Datum
     663          12 : array_to_json(PG_FUNCTION_ARGS)
     664             : {
     665          12 :     Datum       array = PG_GETARG_DATUM(0);
     666             :     StringInfo  result;
     667             : 
     668          12 :     result = makeStringInfo();
     669             : 
     670          12 :     array_to_json_internal(array, result, false);
     671             : 
     672          12 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     673             : }
     674             : 
     675             : /*
     676             :  * SQL function array_to_json(row, prettybool)
     677             :  */
     678             : Datum
     679          16 : array_to_json_pretty(PG_FUNCTION_ARGS)
     680             : {
     681          16 :     Datum       array = PG_GETARG_DATUM(0);
     682          16 :     bool        use_line_feeds = PG_GETARG_BOOL(1);
     683             :     StringInfo  result;
     684             : 
     685          16 :     result = makeStringInfo();
     686             : 
     687          16 :     array_to_json_internal(array, result, use_line_feeds);
     688             : 
     689          16 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     690             : }
     691             : 
     692             : /*
     693             :  * SQL function row_to_json(row)
     694             :  */
     695             : Datum
     696         348 : row_to_json(PG_FUNCTION_ARGS)
     697             : {
     698         348 :     Datum       array = PG_GETARG_DATUM(0);
     699             :     StringInfo  result;
     700             : 
     701         348 :     result = makeStringInfo();
     702             : 
     703         348 :     composite_to_json(array, result, false);
     704             : 
     705         348 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     706             : }
     707             : 
     708             : /*
     709             :  * SQL function row_to_json(row, prettybool)
     710             :  */
     711             : Datum
     712          32 : row_to_json_pretty(PG_FUNCTION_ARGS)
     713             : {
     714          32 :     Datum       array = PG_GETARG_DATUM(0);
     715          32 :     bool        use_line_feeds = PG_GETARG_BOOL(1);
     716             :     StringInfo  result;
     717             : 
     718          32 :     result = makeStringInfo();
     719             : 
     720          32 :     composite_to_json(array, result, use_line_feeds);
     721             : 
     722          32 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     723             : }
     724             : 
     725             : /*
     726             :  * SQL function to_json(anyvalue)
     727             :  */
     728             : Datum
     729          88 : to_json(PG_FUNCTION_ARGS)
     730             : {
     731          88 :     Datum       val = PG_GETARG_DATUM(0);
     732          88 :     Oid         val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
     733             :     StringInfo  result;
     734             :     JsonTypeCategory tcategory;
     735             :     Oid         outfuncoid;
     736             : 
     737          88 :     if (val_type == InvalidOid)
     738           0 :         ereport(ERROR,
     739             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     740             :                  errmsg("could not determine input data type")));
     741             : 
     742          88 :     json_categorize_type(val_type,
     743             :                          &tcategory, &outfuncoid);
     744             : 
     745          88 :     result = makeStringInfo();
     746             : 
     747          88 :     datum_to_json(val, false, result, tcategory, outfuncoid, false);
     748             : 
     749          88 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
     750             : }
     751             : 
     752             : /*
     753             :  * json_agg transition function
     754             :  *
     755             :  * aggregate input column as a json array value.
     756             :  */
     757             : Datum
     758          48 : json_agg_transfn(PG_FUNCTION_ARGS)
     759             : {
     760             :     MemoryContext aggcontext,
     761             :                 oldcontext;
     762             :     JsonAggState *state;
     763             :     Datum       val;
     764             : 
     765          48 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     766             :     {
     767             :         /* cannot be called directly because of internal-type argument */
     768           0 :         elog(ERROR, "json_agg_transfn called in non-aggregate context");
     769             :     }
     770             : 
     771          48 :     if (PG_ARGISNULL(0))
     772             :     {
     773          16 :         Oid         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
     774             : 
     775          16 :         if (arg_type == InvalidOid)
     776           0 :             ereport(ERROR,
     777             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     778             :                      errmsg("could not determine input data type")));
     779             : 
     780             :         /*
     781             :          * Make this state object in a context where it will persist for the
     782             :          * duration of the aggregate call.  MemoryContextSwitchTo is only
     783             :          * needed the first time, as the StringInfo routines make sure they
     784             :          * use the right context to enlarge the object if necessary.
     785             :          */
     786          16 :         oldcontext = MemoryContextSwitchTo(aggcontext);
     787          16 :         state = (JsonAggState *) palloc(sizeof(JsonAggState));
     788          16 :         state->str = makeStringInfo();
     789          16 :         MemoryContextSwitchTo(oldcontext);
     790             : 
     791          16 :         appendStringInfoChar(state->str, '[');
     792          16 :         json_categorize_type(arg_type, &state->val_category,
     793             :                              &state->val_output_func);
     794             :     }
     795             :     else
     796             :     {
     797          32 :         state = (JsonAggState *) PG_GETARG_POINTER(0);
     798          32 :         appendStringInfoString(state->str, ", ");
     799             :     }
     800             : 
     801             :     /* fast path for NULLs */
     802          48 :     if (PG_ARGISNULL(1))
     803             :     {
     804           0 :         datum_to_json((Datum) 0, true, state->str, JSONTYPE_NULL,
     805             :                       InvalidOid, false);
     806           0 :         PG_RETURN_POINTER(state);
     807             :     }
     808             : 
     809          48 :     val = PG_GETARG_DATUM(1);
     810             : 
     811             :     /* add some whitespace if structured type and not first item */
     812          48 :     if (!PG_ARGISNULL(0) &&
     813          32 :         (state->val_category == JSONTYPE_ARRAY ||
     814          32 :          state->val_category == JSONTYPE_COMPOSITE))
     815             :     {
     816          32 :         appendStringInfoString(state->str, "\n ");
     817             :     }
     818             : 
     819          48 :     datum_to_json(val, false, state->str, state->val_category,
     820             :                   state->val_output_func, false);
     821             : 
     822             :     /*
     823             :      * The transition type for json_agg() is declared to be "internal", which
     824             :      * is a pass-by-value type the same size as a pointer.  So we can safely
     825             :      * pass the JsonAggState pointer through nodeAgg.c's machinations.
     826             :      */
     827          48 :     PG_RETURN_POINTER(state);
     828             : }
     829             : 
     830             : /*
     831             :  * json_agg final function
     832             :  */
     833             : Datum
     834          16 : json_agg_finalfn(PG_FUNCTION_ARGS)
     835             : {
     836             :     JsonAggState *state;
     837             : 
     838             :     /* cannot be called directly because of internal-type argument */
     839             :     Assert(AggCheckCallContext(fcinfo, NULL));
     840             : 
     841          32 :     state = PG_ARGISNULL(0) ?
     842          16 :         NULL :
     843          16 :         (JsonAggState *) PG_GETARG_POINTER(0);
     844             : 
     845             :     /* NULL result for no rows in, as is standard with aggregates */
     846          16 :     if (state == NULL)
     847           0 :         PG_RETURN_NULL();
     848             : 
     849             :     /* Else return state with appropriate array terminator added */
     850          16 :     PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
     851             : }
     852             : 
     853             : /*
     854             :  * json_object_agg transition function.
     855             :  *
     856             :  * aggregate two input columns as a single json object value.
     857             :  */
     858             : Datum
     859          40 : json_object_agg_transfn(PG_FUNCTION_ARGS)
     860             : {
     861             :     MemoryContext aggcontext,
     862             :                 oldcontext;
     863             :     JsonAggState *state;
     864             :     Datum       arg;
     865             : 
     866          40 :     if (!AggCheckCallContext(fcinfo, &aggcontext))
     867             :     {
     868             :         /* cannot be called directly because of internal-type argument */
     869           0 :         elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
     870             :     }
     871             : 
     872          40 :     if (PG_ARGISNULL(0))
     873             :     {
     874             :         Oid         arg_type;
     875             : 
     876             :         /*
     877             :          * Make the StringInfo in a context where it will persist for the
     878             :          * duration of the aggregate call. Switching context is only needed
     879             :          * for this initial step, as the StringInfo routines make sure they
     880             :          * use the right context to enlarge the object if necessary.
     881             :          */
     882          12 :         oldcontext = MemoryContextSwitchTo(aggcontext);
     883          12 :         state = (JsonAggState *) palloc(sizeof(JsonAggState));
     884          12 :         state->str = makeStringInfo();
     885          12 :         MemoryContextSwitchTo(oldcontext);
     886             : 
     887          12 :         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
     888             : 
     889          12 :         if (arg_type == InvalidOid)
     890           0 :             ereport(ERROR,
     891             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     892             :                      errmsg("could not determine data type for argument %d", 1)));
     893             : 
     894          12 :         json_categorize_type(arg_type, &state->key_category,
     895             :                              &state->key_output_func);
     896             : 
     897          12 :         arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
     898             : 
     899          12 :         if (arg_type == InvalidOid)
     900           0 :             ereport(ERROR,
     901             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     902             :                      errmsg("could not determine data type for argument %d", 2)));
     903             : 
     904          12 :         json_categorize_type(arg_type, &state->val_category,
     905             :                              &state->val_output_func);
     906             : 
     907          12 :         appendStringInfoString(state->str, "{ ");
     908             :     }
     909             :     else
     910             :     {
     911          28 :         state = (JsonAggState *) PG_GETARG_POINTER(0);
     912          28 :         appendStringInfoString(state->str, ", ");
     913             :     }
     914             : 
     915             :     /*
     916             :      * Note: since json_object_agg() is declared as taking type "any", the
     917             :      * parser will not do any type conversion on unknown-type literals (that
     918             :      * is, undecorated strings or NULLs).  Such values will arrive here as
     919             :      * type UNKNOWN, which fortunately does not matter to us, since
     920             :      * unknownout() works fine.
     921             :      */
     922             : 
     923          40 :     if (PG_ARGISNULL(1))
     924           4 :         ereport(ERROR,
     925             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     926             :                  errmsg("field name must not be null")));
     927             : 
     928          36 :     arg = PG_GETARG_DATUM(1);
     929             : 
     930          36 :     datum_to_json(arg, false, state->str, state->key_category,
     931             :                   state->key_output_func, true);
     932             : 
     933          36 :     appendStringInfoString(state->str, " : ");
     934             : 
     935          36 :     if (PG_ARGISNULL(2))
     936           0 :         arg = (Datum) 0;
     937             :     else
     938          36 :         arg = PG_GETARG_DATUM(2);
     939             : 
     940          36 :     datum_to_json(arg, PG_ARGISNULL(2), state->str, state->val_category,
     941             :                   state->val_output_func, false);
     942             : 
     943          36 :     PG_RETURN_POINTER(state);
     944             : }
     945             : 
     946             : /*
     947             :  * json_object_agg final function.
     948             :  */
     949             : Datum
     950           8 : json_object_agg_finalfn(PG_FUNCTION_ARGS)
     951             : {
     952             :     JsonAggState *state;
     953             : 
     954             :     /* cannot be called directly because of internal-type argument */
     955             :     Assert(AggCheckCallContext(fcinfo, NULL));
     956             : 
     957           8 :     state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
     958             : 
     959             :     /* NULL result for no rows in, as is standard with aggregates */
     960           8 :     if (state == NULL)
     961           0 :         PG_RETURN_NULL();
     962             : 
     963             :     /* Else return state with appropriate object terminator added */
     964           8 :     PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
     965             : }
     966             : 
     967             : /*
     968             :  * Helper function for aggregates: return given StringInfo's contents plus
     969             :  * specified trailing string, as a text datum.  We need this because aggregate
     970             :  * final functions are not allowed to modify the aggregate state.
     971             :  */
     972             : static text *
     973          24 : catenate_stringinfo_string(StringInfo buffer, const char *addon)
     974             : {
     975             :     /* custom version of cstring_to_text_with_len */
     976          24 :     int         buflen = buffer->len;
     977          24 :     int         addlen = strlen(addon);
     978          24 :     text       *result = (text *) palloc(buflen + addlen + VARHDRSZ);
     979             : 
     980          24 :     SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
     981          24 :     memcpy(VARDATA(result), buffer->data, buflen);
     982          24 :     memcpy(VARDATA(result) + buflen, addon, addlen);
     983             : 
     984          24 :     return result;
     985             : }
     986             : 
     987             : /*
     988             :  * SQL function json_build_object(variadic "any")
     989             :  */
     990             : Datum
     991         104 : json_build_object(PG_FUNCTION_ARGS)
     992             : {
     993         104 :     int         nargs = PG_NARGS();
     994             :     int         i;
     995         104 :     const char *sep = "";
     996             :     StringInfo  result;
     997             :     Datum      *args;
     998             :     bool       *nulls;
     999             :     Oid        *types;
    1000             : 
    1001             :     /* fetch argument values to build the object */
    1002         104 :     nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
    1003             : 
    1004         104 :     if (nargs < 0)
    1005           4 :         PG_RETURN_NULL();
    1006             : 
    1007         100 :     if (nargs % 2 != 0)
    1008          12 :         ereport(ERROR,
    1009             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1010             :                  errmsg("argument list must have even number of elements"),
    1011             :         /* translator: %s is a SQL function name */
    1012             :                  errhint("The arguments of %s must consist of alternating keys and values.",
    1013             :                          "json_build_object()")));
    1014             : 
    1015          88 :     result = makeStringInfo();
    1016             : 
    1017          88 :     appendStringInfoChar(result, '{');
    1018             : 
    1019         200 :     for (i = 0; i < nargs; i += 2)
    1020             :     {
    1021         140 :         appendStringInfoString(result, sep);
    1022         140 :         sep = ", ";
    1023             : 
    1024             :         /* process key */
    1025         140 :         if (nulls[i])
    1026          12 :             ereport(ERROR,
    1027             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1028             :                      errmsg("argument %d cannot be null", i + 1),
    1029             :                      errhint("Object keys should be text.")));
    1030             : 
    1031         128 :         add_json(args[i], false, result, types[i], true);
    1032             : 
    1033         112 :         appendStringInfoString(result, " : ");
    1034             : 
    1035             :         /* process value */
    1036         112 :         add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
    1037             :     }
    1038             : 
    1039          60 :     appendStringInfoChar(result, '}');
    1040             : 
    1041          60 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
    1042             : }
    1043             : 
    1044             : /*
    1045             :  * degenerate case of json_build_object where it gets 0 arguments.
    1046             :  */
    1047             : Datum
    1048           4 : json_build_object_noargs(PG_FUNCTION_ARGS)
    1049             : {
    1050           4 :     PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
    1051             : }
    1052             : 
    1053             : /*
    1054             :  * SQL function json_build_array(variadic "any")
    1055             :  */
    1056             : Datum
    1057          36 : json_build_array(PG_FUNCTION_ARGS)
    1058             : {
    1059             :     int         nargs;
    1060             :     int         i;
    1061          36 :     const char *sep = "";
    1062             :     StringInfo  result;
    1063             :     Datum      *args;
    1064             :     bool       *nulls;
    1065             :     Oid        *types;
    1066             : 
    1067             :     /* fetch argument values to build the array */
    1068          36 :     nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
    1069             : 
    1070          36 :     if (nargs < 0)
    1071           4 :         PG_RETURN_NULL();
    1072             : 
    1073          32 :     result = makeStringInfo();
    1074             : 
    1075          32 :     appendStringInfoChar(result, '[');
    1076             : 
    1077         156 :     for (i = 0; i < nargs; i++)
    1078             :     {
    1079         124 :         appendStringInfoString(result, sep);
    1080         124 :         sep = ", ";
    1081         124 :         add_json(args[i], nulls[i], result, types[i], false);
    1082             :     }
    1083             : 
    1084          32 :     appendStringInfoChar(result, ']');
    1085             : 
    1086          32 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
    1087             : }
    1088             : 
    1089             : /*
    1090             :  * degenerate case of json_build_array where it gets 0 arguments.
    1091             :  */
    1092             : Datum
    1093           4 : json_build_array_noargs(PG_FUNCTION_ARGS)
    1094             : {
    1095           4 :     PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
    1096             : }
    1097             : 
    1098             : /*
    1099             :  * SQL function json_object(text[])
    1100             :  *
    1101             :  * take a one or two dimensional array of text as key/value pairs
    1102             :  * for a json object.
    1103             :  */
    1104             : Datum
    1105          32 : json_object(PG_FUNCTION_ARGS)
    1106             : {
    1107          32 :     ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
    1108          32 :     int         ndims = ARR_NDIM(in_array);
    1109             :     StringInfoData result;
    1110             :     Datum      *in_datums;
    1111             :     bool       *in_nulls;
    1112             :     int         in_count,
    1113             :                 count,
    1114             :                 i;
    1115             :     text       *rval;
    1116             :     char       *v;
    1117             : 
    1118          32 :     switch (ndims)
    1119             :     {
    1120           4 :         case 0:
    1121           4 :             PG_RETURN_DATUM(CStringGetTextDatum("{}"));
    1122             :             break;
    1123             : 
    1124          12 :         case 1:
    1125          12 :             if ((ARR_DIMS(in_array)[0]) % 2)
    1126           4 :                 ereport(ERROR,
    1127             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1128             :                          errmsg("array must have even number of elements")));
    1129           8 :             break;
    1130             : 
    1131          12 :         case 2:
    1132          12 :             if ((ARR_DIMS(in_array)[1]) != 2)
    1133           8 :                 ereport(ERROR,
    1134             :                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1135             :                          errmsg("array must have two columns")));
    1136           4 :             break;
    1137             : 
    1138           4 :         default:
    1139           4 :             ereport(ERROR,
    1140             :                     (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1141             :                      errmsg("wrong number of array subscripts")));
    1142             :     }
    1143             : 
    1144          12 :     deconstruct_array(in_array,
    1145             :                       TEXTOID, -1, false, TYPALIGN_INT,
    1146             :                       &in_datums, &in_nulls, &in_count);
    1147             : 
    1148          12 :     count = in_count / 2;
    1149             : 
    1150          12 :     initStringInfo(&result);
    1151             : 
    1152          12 :     appendStringInfoChar(&result, '{');
    1153             : 
    1154        1244 :     for (i = 0; i < count; ++i)
    1155             :     {
    1156        1232 :         if (in_nulls[i * 2])
    1157           0 :             ereport(ERROR,
    1158             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1159             :                      errmsg("null value not allowed for object key")));
    1160             : 
    1161        1232 :         v = TextDatumGetCString(in_datums[i * 2]);
    1162        1232 :         if (i > 0)
    1163        1220 :             appendStringInfoString(&result, ", ");
    1164        1232 :         escape_json(&result, v);
    1165        1232 :         appendStringInfoString(&result, " : ");
    1166        1232 :         pfree(v);
    1167        1232 :         if (in_nulls[i * 2 + 1])
    1168           8 :             appendStringInfoString(&result, "null");
    1169             :         else
    1170             :         {
    1171        1224 :             v = TextDatumGetCString(in_datums[i * 2 + 1]);
    1172        1224 :             escape_json(&result, v);
    1173        1224 :             pfree(v);
    1174             :         }
    1175             :     }
    1176             : 
    1177          12 :     appendStringInfoChar(&result, '}');
    1178             : 
    1179          12 :     pfree(in_datums);
    1180          12 :     pfree(in_nulls);
    1181             : 
    1182          12 :     rval = cstring_to_text_with_len(result.data, result.len);
    1183          12 :     pfree(result.data);
    1184             : 
    1185          12 :     PG_RETURN_TEXT_P(rval);
    1186             : 
    1187             : }
    1188             : 
    1189             : /*
    1190             :  * SQL function json_object(text[], text[])
    1191             :  *
    1192             :  * take separate key and value arrays of text to construct a json object
    1193             :  * pairwise.
    1194             :  */
    1195             : Datum
    1196          28 : json_object_two_arg(PG_FUNCTION_ARGS)
    1197             : {
    1198          28 :     ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
    1199          28 :     ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
    1200          28 :     int         nkdims = ARR_NDIM(key_array);
    1201          28 :     int         nvdims = ARR_NDIM(val_array);
    1202             :     StringInfoData result;
    1203             :     Datum      *key_datums,
    1204             :                *val_datums;
    1205             :     bool       *key_nulls,
    1206             :                *val_nulls;
    1207             :     int         key_count,
    1208             :                 val_count,
    1209             :                 i;
    1210             :     text       *rval;
    1211             :     char       *v;
    1212             : 
    1213          28 :     if (nkdims > 1 || nkdims != nvdims)
    1214           4 :         ereport(ERROR,
    1215             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1216             :                  errmsg("wrong number of array subscripts")));
    1217             : 
    1218          24 :     if (nkdims == 0)
    1219           4 :         PG_RETURN_DATUM(CStringGetTextDatum("{}"));
    1220             : 
    1221          20 :     deconstruct_array(key_array,
    1222             :                       TEXTOID, -1, false, TYPALIGN_INT,
    1223             :                       &key_datums, &key_nulls, &key_count);
    1224             : 
    1225          20 :     deconstruct_array(val_array,
    1226             :                       TEXTOID, -1, false, TYPALIGN_INT,
    1227             :                       &val_datums, &val_nulls, &val_count);
    1228             : 
    1229          20 :     if (key_count != val_count)
    1230           8 :         ereport(ERROR,
    1231             :                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
    1232             :                  errmsg("mismatched array dimensions")));
    1233             : 
    1234          12 :     initStringInfo(&result);
    1235             : 
    1236          12 :     appendStringInfoChar(&result, '{');
    1237             : 
    1238          52 :     for (i = 0; i < key_count; ++i)
    1239             :     {
    1240          44 :         if (key_nulls[i])
    1241           4 :             ereport(ERROR,
    1242             :                     (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
    1243             :                      errmsg("null value not allowed for object key")));
    1244             : 
    1245          40 :         v = TextDatumGetCString(key_datums[i]);
    1246          40 :         if (i > 0)
    1247          28 :             appendStringInfoString(&result, ", ");
    1248          40 :         escape_json(&result, v);
    1249          40 :         appendStringInfoString(&result, " : ");
    1250          40 :         pfree(v);
    1251          40 :         if (val_nulls[i])
    1252           0 :             appendStringInfoString(&result, "null");
    1253             :         else
    1254             :         {
    1255          40 :             v = TextDatumGetCString(val_datums[i]);
    1256          40 :             escape_json(&result, v);
    1257          40 :             pfree(v);
    1258             :         }
    1259             :     }
    1260             : 
    1261           8 :     appendStringInfoChar(&result, '}');
    1262             : 
    1263           8 :     pfree(key_datums);
    1264           8 :     pfree(key_nulls);
    1265           8 :     pfree(val_datums);
    1266           8 :     pfree(val_nulls);
    1267             : 
    1268           8 :     rval = cstring_to_text_with_len(result.data, result.len);
    1269           8 :     pfree(result.data);
    1270             : 
    1271           8 :     PG_RETURN_TEXT_P(rval);
    1272             : }
    1273             : 
    1274             : 
    1275             : /*
    1276             :  * Produce a JSON string literal, properly escaping characters in the text.
    1277             :  */
    1278             : void
    1279      181604 : escape_json(StringInfo buf, const char *str)
    1280             : {
    1281             :     const char *p;
    1282             : 
    1283      181604 :     appendStringInfoCharMacro(buf, '"');
    1284     2345178 :     for (p = str; *p; p++)
    1285             :     {
    1286     2163574 :         switch (*p)
    1287             :         {
    1288          12 :             case '\b':
    1289          12 :                 appendStringInfoString(buf, "\\b");
    1290          12 :                 break;
    1291           4 :             case '\f':
    1292           4 :                 appendStringInfoString(buf, "\\f");
    1293           4 :                 break;
    1294          16 :             case '\n':
    1295          16 :                 appendStringInfoString(buf, "\\n");
    1296          16 :                 break;
    1297           4 :             case '\r':
    1298           4 :                 appendStringInfoString(buf, "\\r");
    1299           4 :                 break;
    1300           8 :             case '\t':
    1301           8 :                 appendStringInfoString(buf, "\\t");
    1302           8 :                 break;
    1303          52 :             case '"':
    1304          52 :                 appendStringInfoString(buf, "\\\"");
    1305          52 :                 break;
    1306          48 :             case '\\':
    1307          48 :                 appendStringInfoString(buf, "\\\\");
    1308          48 :                 break;
    1309     2163430 :             default:
    1310     2163430 :                 if ((unsigned char) *p < ' ')
    1311           4 :                     appendStringInfo(buf, "\\u%04x", (int) *p);
    1312             :                 else
    1313     2163426 :                     appendStringInfoCharMacro(buf, *p);
    1314     2163430 :                 break;
    1315             :         }
    1316             :     }
    1317      181604 :     appendStringInfoCharMacro(buf, '"');
    1318      181604 : }
    1319             : 
    1320             : /*
    1321             :  * SQL function json_typeof(json) -> text
    1322             :  *
    1323             :  * Returns the type of the outermost JSON value as TEXT.  Possible types are
    1324             :  * "object", "array", "string", "number", "boolean", and "null".
    1325             :  *
    1326             :  * Performs a single call to json_lex() to get the first token of the supplied
    1327             :  * value.  This initial token uniquely determines the value's type.  As our
    1328             :  * input must already have been validated by json_in() or json_recv(), the
    1329             :  * initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
    1330             :  * JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
    1331             :  */
    1332             : Datum
    1333          40 : json_typeof(PG_FUNCTION_ARGS)
    1334             : {
    1335             :     text       *json;
    1336             : 
    1337             :     JsonLexContext *lex;
    1338             :     JsonTokenType tok;
    1339             :     char       *type;
    1340             :     JsonParseErrorType result;
    1341             : 
    1342          40 :     json = PG_GETARG_TEXT_PP(0);
    1343          40 :     lex = makeJsonLexContext(json, false);
    1344             : 
    1345             :     /* Lex exactly one token from the input and check its type. */
    1346          40 :     result = json_lex(lex);
    1347          40 :     if (result != JSON_SUCCESS)
    1348           0 :         json_ereport_error(result, lex);
    1349          40 :     tok = lex->token_type;
    1350          40 :     switch (tok)
    1351             :     {
    1352           8 :         case JSON_TOKEN_OBJECT_START:
    1353           8 :             type = "object";
    1354           8 :             break;
    1355           8 :         case JSON_TOKEN_ARRAY_START:
    1356           8 :             type = "array";
    1357           8 :             break;
    1358           4 :         case JSON_TOKEN_STRING:
    1359           4 :             type = "string";
    1360           4 :             break;
    1361           8 :         case JSON_TOKEN_NUMBER:
    1362           8 :             type = "number";
    1363           8 :             break;
    1364           8 :         case JSON_TOKEN_TRUE:
    1365             :         case JSON_TOKEN_FALSE:
    1366           8 :             type = "boolean";
    1367           8 :             break;
    1368           4 :         case JSON_TOKEN_NULL:
    1369           4 :             type = "null";
    1370           4 :             break;
    1371           0 :         default:
    1372           0 :             elog(ERROR, "unexpected json token: %d", tok);
    1373             :     }
    1374             : 
    1375          40 :     PG_RETURN_TEXT_P(cstring_to_text(type));
    1376             : }

Generated by: LCOV version 1.13