LCOV - code coverage report
Current view: top level - src/backend/utils/adt - json.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 94.6 % 672 636
Test Date: 2026-07-22 12:15:41 Functions: 96.1 % 51 49
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 80.5 % 369 297

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

Generated by: LCOV version 2.0-1