Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/json.c
Line
Count
Source
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
json_in(PG_FUNCTION_ARGS)
107
0
{
108
0
  char     *json = PG_GETARG_CSTRING(0);
109
0
  text     *result = cstring_to_text(json);
110
0
  JsonLexContext lex;
111
112
  /* validate it */
113
0
  makeJsonLexContext(&lex, result, false);
114
0
  if (!pg_parse_json_or_errsave(&lex, &nullSemAction, fcinfo->context))
115
0
    PG_RETURN_NULL();
116
117
  /* Internal representation is the same as text */
118
0
  PG_RETURN_TEXT_P(result);
119
0
}
120
121
/*
122
 * Output.
123
 */
124
Datum
125
json_out(PG_FUNCTION_ARGS)
126
0
{
127
  /* we needn't detoast because text_to_cstring will handle that */
128
0
  Datum   txt = PG_GETARG_DATUM(0);
129
130
0
  PG_RETURN_CSTRING(TextDatumGetCString(txt));
131
0
}
132
133
/*
134
 * Binary send.
135
 */
136
Datum
137
json_send(PG_FUNCTION_ARGS)
138
0
{
139
0
  text     *t = PG_GETARG_TEXT_PP(0);
140
0
  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
0
}
146
147
/*
148
 * Binary receive.
149
 */
150
Datum
151
json_recv(PG_FUNCTION_ARGS)
152
0
{
153
0
  StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
154
0
  char     *str;
155
0
  int     nbytes;
156
0
  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
0
                 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
0
}
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
datum_to_json_internal(Datum val, bool is_null, StringInfo result,
179
             JsonTypeCategory tcategory, Oid outfuncoid,
180
             bool key_scalar)
181
0
{
182
0
  char     *outputstr;
183
0
  text     *jsontext;
184
185
0
  check_stack_depth();
186
187
  /* callers are expected to ensure that null keys are not passed in */
188
0
  Assert(!(key_scalar && is_null));
189
190
0
  if (is_null)
191
0
  {
192
0
    appendBinaryStringInfo(result, "null", strlen("null"));
193
0
    return;
194
0
  }
195
196
0
  if (key_scalar &&
197
0
    (tcategory == JSONTYPE_ARRAY ||
198
0
     tcategory == JSONTYPE_COMPOSITE ||
199
0
     tcategory == JSONTYPE_JSON ||
200
0
     tcategory == JSONTYPE_CAST))
201
0
    ereport(ERROR,
202
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
203
0
         errmsg("key value must be scalar, not array, composite, or json")));
204
205
0
  switch (tcategory)
206
0
  {
207
0
    case JSONTYPE_ARRAY:
208
0
      array_to_json_internal(val, result, false);
209
0
      break;
210
0
    case JSONTYPE_COMPOSITE:
211
0
      composite_to_json(val, result, false);
212
0
      break;
213
0
    case JSONTYPE_BOOL:
214
0
      if (key_scalar)
215
0
        appendStringInfoChar(result, '"');
216
0
      if (DatumGetBool(val))
217
0
        appendBinaryStringInfo(result, "true", strlen("true"));
218
0
      else
219
0
        appendBinaryStringInfo(result, "false", strlen("false"));
220
0
      if (key_scalar)
221
0
        appendStringInfoChar(result, '"');
222
0
      break;
223
0
    case JSONTYPE_NUMERIC:
224
0
      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
0
      if (!key_scalar &&
233
0
        ((*outputstr >= '0' && *outputstr <= '9') ||
234
0
         (*outputstr == '-' &&
235
0
          (outputstr[1] >= '0' && outputstr[1] <= '9'))))
236
0
        appendStringInfoString(result, outputstr);
237
0
      else
238
0
      {
239
0
        appendStringInfoChar(result, '"');
240
0
        appendStringInfoString(result, outputstr);
241
0
        appendStringInfoChar(result, '"');
242
0
      }
243
0
      pfree(outputstr);
244
0
      break;
245
0
    case JSONTYPE_DATE:
246
0
      {
247
0
        char    buf[MAXDATELEN + 1];
248
249
0
        JsonEncodeDateTime(buf, val, DATEOID, NULL);
250
0
        appendStringInfoChar(result, '"');
251
0
        appendStringInfoString(result, buf);
252
0
        appendStringInfoChar(result, '"');
253
0
      }
254
0
      break;
255
0
    case JSONTYPE_TIMESTAMP:
256
0
      {
257
0
        char    buf[MAXDATELEN + 1];
258
259
0
        JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
260
0
        appendStringInfoChar(result, '"');
261
0
        appendStringInfoString(result, buf);
262
0
        appendStringInfoChar(result, '"');
263
0
      }
264
0
      break;
265
0
    case JSONTYPE_TIMESTAMPTZ:
266
0
      {
267
0
        char    buf[MAXDATELEN + 1];
268
269
0
        JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
270
0
        appendStringInfoChar(result, '"');
271
0
        appendStringInfoString(result, buf);
272
0
        appendStringInfoChar(result, '"');
273
0
      }
274
0
      break;
275
0
    case JSONTYPE_JSON:
276
      /* JSON and JSONB output will already be escaped */
277
0
      outputstr = OidOutputFunctionCall(outfuncoid, val);
278
0
      appendStringInfoString(result, outputstr);
279
0
      pfree(outputstr);
280
0
      break;
281
0
    case JSONTYPE_CAST:
282
      /* outfuncoid refers to a cast function, not an output function */
283
0
      jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
284
0
      appendBinaryStringInfo(result, VARDATA_ANY(jsontext),
285
0
                   VARSIZE_ANY_EXHDR(jsontext));
286
0
      pfree(jsontext);
287
0
      break;
288
0
    default:
289
      /* special-case text types to save useless palloc/memcpy cycles */
290
0
      if (outfuncoid == F_TEXTOUT || outfuncoid == F_VARCHAROUT ||
291
0
        outfuncoid == F_BPCHAROUT)
292
0
        escape_json_text(result, (text *) DatumGetPointer(val));
293
0
      else
294
0
      {
295
0
        outputstr = OidOutputFunctionCall(outfuncoid, val);
296
0
        escape_json(result, outputstr);
297
0
        pfree(outputstr);
298
0
      }
299
0
      break;
300
0
  }
301
0
}
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
JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
310
0
{
311
0
  if (!buf)
312
0
    buf = palloc(MAXDATELEN + 1);
313
314
0
  switch (typid)
315
0
  {
316
0
    case DATEOID:
317
0
      {
318
0
        DateADT   date;
319
0
        struct pg_tm tm;
320
321
0
        date = DatumGetDateADT(value);
322
323
        /* Same as date_out(), but forcing DateStyle */
324
0
        if (DATE_NOT_FINITE(date))
325
0
          EncodeSpecialDate(date, buf);
326
0
        else
327
0
        {
328
0
          j2date(date + POSTGRES_EPOCH_JDATE,
329
0
               &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
330
0
          EncodeDateOnly(&tm, USE_XSD_DATES, buf);
331
0
        }
332
0
      }
333
0
      break;
334
0
    case TIMEOID:
335
0
      {
336
0
        TimeADT   time = DatumGetTimeADT(value);
337
0
        struct pg_tm tt,
338
0
               *tm = &tt;
339
0
        fsec_t    fsec;
340
341
        /* Same as time_out(), but forcing DateStyle */
342
0
        time2tm(time, tm, &fsec);
343
0
        EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
344
0
      }
345
0
      break;
346
0
    case TIMETZOID:
347
0
      {
348
0
        TimeTzADT  *time = DatumGetTimeTzADTP(value);
349
0
        struct pg_tm tt,
350
0
               *tm = &tt;
351
0
        fsec_t    fsec;
352
0
        int     tz;
353
354
        /* Same as timetz_out(), but forcing DateStyle */
355
0
        timetz2tm(time, tm, &fsec, &tz);
356
0
        EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
357
0
      }
358
0
      break;
359
0
    case TIMESTAMPOID:
360
0
      {
361
0
        Timestamp timestamp;
362
0
        struct pg_tm tm;
363
0
        fsec_t    fsec;
364
365
0
        timestamp = DatumGetTimestamp(value);
366
        /* Same as timestamp_out(), but forcing DateStyle */
367
0
        if (TIMESTAMP_NOT_FINITE(timestamp))
368
0
          EncodeSpecialTimestamp(timestamp, buf);
369
0
        else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
370
0
          EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
371
0
        else
372
0
          ereport(ERROR,
373
0
              (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
374
0
               errmsg("timestamp out of range")));
375
0
      }
376
0
      break;
377
0
    case TIMESTAMPTZOID:
378
0
      {
379
0
        TimestampTz timestamp;
380
0
        struct pg_tm tm;
381
0
        int     tz;
382
0
        fsec_t    fsec;
383
0
        const char *tzn = NULL;
384
385
0
        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
0
        if (tzp)
394
0
        {
395
0
          tz = *tzp;
396
0
          timestamp -= (TimestampTz) tz * USECS_PER_SEC;
397
0
        }
398
399
        /* Same as timestamptz_out(), but forcing DateStyle */
400
0
        if (TIMESTAMP_NOT_FINITE(timestamp))
401
0
          EncodeSpecialTimestamp(timestamp, buf);
402
0
        else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
403
0
                    tzp ? NULL : &tzn, NULL) == 0)
404
0
        {
405
0
          if (tzp)
406
0
            tm.tm_isdst = 1; /* set time-zone presence flag */
407
408
0
          EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
409
0
        }
410
0
        else
411
0
          ereport(ERROR,
412
0
              (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
413
0
               errmsg("timestamp out of range")));
414
0
      }
415
0
      break;
416
0
    default:
417
0
      elog(ERROR, "unknown jsonb value datetime type oid %u", typid);
418
0
      return NULL;
419
0
  }
420
421
0
  return buf;
422
0
}
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
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
0
{
434
0
  int     i;
435
0
  const char *sep;
436
437
0
  Assert(dim < ndims);
438
439
0
  sep = use_line_feeds ? ",\n " : ",";
440
441
0
  appendStringInfoChar(result, '[');
442
443
0
  for (i = 1; i <= dims[dim]; i++)
444
0
  {
445
0
    if (i > 1)
446
0
      appendStringInfoString(result, sep);
447
448
0
    if (dim + 1 == ndims)
449
0
    {
450
0
      datum_to_json_internal(vals[*valcount], nulls[*valcount],
451
0
                   result, tcategory,
452
0
                   outfuncoid, false);
453
0
      (*valcount)++;
454
0
    }
455
0
    else
456
0
    {
457
      /*
458
       * Do we want line feeds on inner dimensions of arrays? For now
459
       * we'll say no.
460
       */
461
0
      array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
462
0
                valcount, tcategory, outfuncoid, false);
463
0
    }
464
0
  }
465
466
0
  appendStringInfoChar(result, ']');
467
0
}
468
469
/*
470
 * Turn an array into JSON.
471
 */
472
static void
473
array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
474
0
{
475
0
  ArrayType  *v = DatumGetArrayTypeP(array);
476
0
  Oid     element_type = ARR_ELEMTYPE(v);
477
0
  int      *dim;
478
0
  int     ndim;
479
0
  int     nitems;
480
0
  int     count = 0;
481
0
  Datum    *elements;
482
0
  bool     *nulls;
483
0
  int16   typlen;
484
0
  bool    typbyval;
485
0
  char    typalign;
486
0
  JsonTypeCategory tcategory;
487
0
  Oid     outfuncoid;
488
489
0
  ndim = ARR_NDIM(v);
490
0
  dim = ARR_DIMS(v);
491
0
  nitems = ArrayGetNItems(ndim, dim);
492
493
0
  if (nitems <= 0)
494
0
  {
495
0
    appendStringInfoString(result, "[]");
496
0
    return;
497
0
  }
498
499
0
  get_typlenbyvalalign(element_type,
500
0
             &typlen, &typbyval, &typalign);
501
502
0
  json_categorize_type(element_type, false,
503
0
             &tcategory, &outfuncoid);
504
505
0
  deconstruct_array(v, element_type, typlen, typbyval,
506
0
            typalign, &elements, &nulls,
507
0
            &nitems);
508
509
0
  array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
510
0
            outfuncoid, use_line_feeds);
511
512
0
  pfree(elements);
513
0
  pfree(nulls);
514
0
}
515
516
/*
517
 * Turn a composite / record into JSON.
518
 * Exported so COPY TO can use it.
519
 */
520
void
521
composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
522
0
{
523
0
  HeapTupleHeader td;
524
0
  Oid     tupType;
525
0
  int32   tupTypmod;
526
0
  TupleDesc tupdesc;
527
0
  HeapTupleData tmptup,
528
0
         *tuple;
529
0
  int     i;
530
0
  bool    needsep = false;
531
0
  const char *sep;
532
0
  int     seplen;
533
534
  /*
535
   * We can avoid expensive strlen() calls by precalculating the separator
536
   * length.
537
   */
538
0
  sep = use_line_feeds ? ",\n " : ",";
539
0
  seplen = use_line_feeds ? strlen(",\n ") : strlen(",");
540
541
0
  td = DatumGetHeapTupleHeader(composite);
542
543
  /* Extract rowtype info and find a tupdesc */
544
0
  tupType = HeapTupleHeaderGetTypeId(td);
545
0
  tupTypmod = HeapTupleHeaderGetTypMod(td);
546
0
  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
547
548
  /* Build a temporary HeapTuple control structure */
549
0
  tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
550
0
  tmptup.t_data = td;
551
0
  tuple = &tmptup;
552
553
0
  appendStringInfoChar(result, '{');
554
555
0
  for (i = 0; i < tupdesc->natts; i++)
556
0
  {
557
0
    Datum   val;
558
0
    bool    isnull;
559
0
    char     *attname;
560
0
    JsonTypeCategory tcategory;
561
0
    Oid     outfuncoid;
562
0
    Form_pg_attribute att = TupleDescAttr(tupdesc, i);
563
564
0
    if (att->attisdropped)
565
0
      continue;
566
567
0
    if (needsep)
568
0
      appendBinaryStringInfo(result, sep, seplen);
569
0
    needsep = true;
570
571
0
    attname = NameStr(att->attname);
572
0
    escape_json(result, attname);
573
0
    appendStringInfoChar(result, ':');
574
575
0
    val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
576
577
0
    if (isnull)
578
0
    {
579
0
      tcategory = JSONTYPE_NULL;
580
0
      outfuncoid = InvalidOid;
581
0
    }
582
0
    else
583
0
      json_categorize_type(att->atttypid, false, &tcategory,
584
0
                 &outfuncoid);
585
586
0
    datum_to_json_internal(val, isnull, result, tcategory, outfuncoid,
587
0
                 false);
588
0
  }
589
590
0
  appendStringInfoChar(result, '}');
591
0
  ReleaseTupleDesc(tupdesc);
592
0
}
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
add_json(Datum val, bool is_null, StringInfo result,
603
     Oid val_type, bool key_scalar)
604
0
{
605
0
  JsonTypeCategory tcategory;
606
0
  Oid     outfuncoid;
607
608
0
  if (val_type == InvalidOid)
609
0
    ereport(ERROR,
610
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
611
0
         errmsg("could not determine input data type")));
612
613
0
  if (is_null)
614
0
  {
615
0
    tcategory = JSONTYPE_NULL;
616
0
    outfuncoid = InvalidOid;
617
0
  }
618
0
  else
619
0
    json_categorize_type(val_type, false,
620
0
               &tcategory, &outfuncoid);
621
622
0
  datum_to_json_internal(val, is_null, result, tcategory, outfuncoid,
623
0
               key_scalar);
624
0
}
625
626
/*
627
 * SQL function array_to_json(row)
628
 */
629
Datum
630
array_to_json(PG_FUNCTION_ARGS)
631
0
{
632
0
  Datum   array = PG_GETARG_DATUM(0);
633
0
  StringInfoData result;
634
635
0
  initStringInfo(&result);
636
637
0
  array_to_json_internal(array, &result, false);
638
639
0
  PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
640
0
}
641
642
/*
643
 * SQL function array_to_json(row, prettybool)
644
 */
645
Datum
646
array_to_json_pretty(PG_FUNCTION_ARGS)
647
0
{
648
0
  Datum   array = PG_GETARG_DATUM(0);
649
0
  bool    use_line_feeds = PG_GETARG_BOOL(1);
650
0
  StringInfoData result;
651
652
0
  initStringInfo(&result);
653
654
0
  array_to_json_internal(array, &result, use_line_feeds);
655
656
0
  PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
657
0
}
658
659
/*
660
 * SQL function row_to_json(row)
661
 */
662
Datum
663
row_to_json(PG_FUNCTION_ARGS)
664
0
{
665
0
  Datum   array = PG_GETARG_DATUM(0);
666
0
  StringInfoData result;
667
668
0
  initStringInfo(&result);
669
670
0
  composite_to_json(array, &result, false);
671
672
0
  PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
673
0
}
674
675
/*
676
 * SQL function row_to_json(row, prettybool)
677
 */
678
Datum
679
row_to_json_pretty(PG_FUNCTION_ARGS)
680
0
{
681
0
  Datum   array = PG_GETARG_DATUM(0);
682
0
  bool    use_line_feeds = PG_GETARG_BOOL(1);
683
0
  StringInfoData result;
684
685
0
  initStringInfo(&result);
686
687
0
  composite_to_json(array, &result, use_line_feeds);
688
689
0
  PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
690
0
}
691
692
/*
693
 * Is the given type immutable when coming out of a JSON context?
694
 */
695
bool
696
to_json_is_immutable(Oid typoid)
697
0
{
698
0
  bool    has_mutable = false;
699
700
0
  json_check_mutability(typoid, false, &has_mutable);
701
0
  return !has_mutable;
702
0
}
703
704
/*
705
 * SQL function to_json(anyvalue)
706
 */
707
Datum
708
to_json(PG_FUNCTION_ARGS)
709
0
{
710
0
  Datum   val = PG_GETARG_DATUM(0);
711
0
  Oid     val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
712
0
  JsonTypeCategory tcategory;
713
0
  Oid     outfuncoid;
714
715
0
  if (val_type == InvalidOid)
716
0
    ereport(ERROR,
717
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
718
0
         errmsg("could not determine input data type")));
719
720
0
  json_categorize_type(val_type, false,
721
0
             &tcategory, &outfuncoid);
722
723
0
  PG_RETURN_DATUM(datum_to_json(val, tcategory, outfuncoid));
724
0
}
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
datum_to_json(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
733
0
{
734
0
  StringInfoData result;
735
736
0
  initStringInfo(&result);
737
0
  datum_to_json_internal(val, false, &result, tcategory, outfuncoid,
738
0
               false);
739
740
0
  return PointerGetDatum(cstring_to_text_with_len(result.data, result.len));
741
0
}
742
743
/*
744
 * json_agg transition function
745
 *
746
 * aggregate input column as a json array value.
747
 */
748
static Datum
749
json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
750
0
{
751
0
  MemoryContext aggcontext,
752
0
        oldcontext;
753
0
  JsonAggState *state;
754
0
  Datum   val;
755
756
0
  if (!AggCheckCallContext(fcinfo, &aggcontext))
757
0
  {
758
    /* cannot be called directly because of internal-type argument */
759
0
    elog(ERROR, "json_agg_transfn called in non-aggregate context");
760
0
  }
761
762
0
  if (PG_ARGISNULL(0))
763
0
  {
764
0
    Oid     arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
765
766
0
    if (arg_type == InvalidOid)
767
0
      ereport(ERROR,
768
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
769
0
           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
0
    oldcontext = MemoryContextSwitchTo(aggcontext);
778
0
    state = palloc_object(JsonAggState);
779
0
    state->str = makeStringInfo();
780
0
    MemoryContextSwitchTo(oldcontext);
781
782
0
    appendStringInfoChar(state->str, '[');
783
0
    json_categorize_type(arg_type, false, &state->val_category,
784
0
               &state->val_output_func);
785
0
  }
786
0
  else
787
0
  {
788
0
    state = (JsonAggState *) PG_GETARG_POINTER(0);
789
0
  }
790
791
0
  if (absent_on_null && PG_ARGISNULL(1))
792
0
    PG_RETURN_POINTER(state);
793
794
0
  if (state->str->len > 1)
795
0
    appendStringInfoString(state->str, ", ");
796
797
  /* fast path for NULLs */
798
0
  if (PG_ARGISNULL(1))
799
0
  {
800
0
    datum_to_json_internal((Datum) 0, true, state->str, JSONTYPE_NULL,
801
0
                 InvalidOid, false);
802
0
    PG_RETURN_POINTER(state);
803
0
  }
804
805
0
  val = PG_GETARG_DATUM(1);
806
807
  /* add some whitespace if structured type and not first item */
808
0
  if (!PG_ARGISNULL(0) && state->str->len > 1 &&
809
0
    (state->val_category == JSONTYPE_ARRAY ||
810
0
     state->val_category == JSONTYPE_COMPOSITE))
811
0
  {
812
0
    appendStringInfoString(state->str, "\n ");
813
0
  }
814
815
0
  datum_to_json_internal(val, false, state->str, state->val_category,
816
0
               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
0
  PG_RETURN_POINTER(state);
824
0
}
825
826
827
/*
828
 * json_agg aggregate function
829
 */
830
Datum
831
json_agg_transfn(PG_FUNCTION_ARGS)
832
0
{
833
0
  return json_agg_transfn_worker(fcinfo, false);
834
0
}
835
836
/*
837
 * json_agg_strict aggregate function
838
 */
839
Datum
840
json_agg_strict_transfn(PG_FUNCTION_ARGS)
841
0
{
842
0
  return json_agg_transfn_worker(fcinfo, true);
843
0
}
844
845
/*
846
 * json_agg final function
847
 */
848
Datum
849
json_agg_finalfn(PG_FUNCTION_ARGS)
850
0
{
851
0
  JsonAggState *state;
852
853
  /* cannot be called directly because of internal-type argument */
854
0
  Assert(AggCheckCallContext(fcinfo, NULL));
855
856
0
  state = PG_ARGISNULL(0) ?
857
0
    NULL :
858
0
    (JsonAggState *) PG_GETARG_POINTER(0);
859
860
  /* NULL result for no rows in, as is standard with aggregates */
861
0
  if (state == NULL)
862
0
    PG_RETURN_NULL();
863
864
  /* Else return state with appropriate array terminator added */
865
0
  PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
866
0
}
867
868
/* Functions implementing hash table for key uniqueness check */
869
static uint32
870
json_unique_hash(const void *key, Size keysize)
871
0
{
872
0
  const JsonUniqueHashEntry *entry = (const JsonUniqueHashEntry *) key;
873
0
  uint32    hash = hash_bytes_uint32(entry->object_id);
874
875
0
  hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
876
877
0
  return hash;
878
0
}
879
880
static int
881
json_unique_hash_match(const void *key1, const void *key2, Size keysize)
882
0
{
883
0
  const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
884
0
  const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
885
886
0
  if (entry1->object_id != entry2->object_id)
887
0
    return entry1->object_id > entry2->object_id ? 1 : -1;
888
889
0
  if (entry1->key_len != entry2->key_len)
890
0
    return entry1->key_len > entry2->key_len ? 1 : -1;
891
892
0
  return strncmp(entry1->key, entry2->key, entry1->key_len);
893
0
}
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
json_unique_check_init(JsonUniqueCheckState *cxt)
903
0
{
904
0
  HASHCTL   ctl;
905
906
0
  memset(&ctl, 0, sizeof(ctl));
907
0
  ctl.keysize = sizeof(JsonUniqueHashEntry);
908
0
  ctl.entrysize = sizeof(JsonUniqueHashEntry);
909
0
  ctl.hcxt = CurrentMemoryContext;
910
0
  ctl.hash = json_unique_hash;
911
0
  ctl.match = json_unique_hash_match;
912
913
0
  *cxt = hash_create("json object hashtable",
914
0
             32,
915
0
             &ctl,
916
0
             HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
917
0
}
918
919
static void
920
json_unique_builder_init(JsonUniqueBuilderState *cxt)
921
0
{
922
0
  json_unique_check_init(&cxt->check);
923
0
  cxt->mcxt = CurrentMemoryContext;
924
0
  cxt->skipped_keys.data = NULL;
925
0
}
926
927
static bool
928
json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
929
0
{
930
0
  JsonUniqueHashEntry entry;
931
0
  bool    found;
932
933
0
  entry.key = key;
934
0
  entry.key_len = strlen(key);
935
0
  entry.object_id = object_id;
936
937
0
  (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
938
939
0
  return !found;
940
0
}
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
json_unique_builder_get_throwawaybuf(JsonUniqueBuilderState *cxt)
949
0
{
950
0
  StringInfo  out = &cxt->skipped_keys;
951
952
0
  if (!out->data)
953
0
  {
954
0
    MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
955
956
0
    initStringInfo(out);
957
0
    MemoryContextSwitchTo(oldcxt);
958
0
  }
959
0
  else
960
    /* Just reset the string to empty */
961
0
    out->len = 0;
962
963
0
  return out;
964
0
}
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
json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
973
                 bool absent_on_null, bool unique_keys)
974
0
{
975
0
  MemoryContext aggcontext,
976
0
        oldcontext;
977
0
  JsonAggState *state;
978
0
  StringInfo  out;
979
0
  Datum   arg;
980
0
  bool    skip;
981
0
  int     key_offset;
982
983
0
  if (!AggCheckCallContext(fcinfo, &aggcontext))
984
0
  {
985
    /* cannot be called directly because of internal-type argument */
986
0
    elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
987
0
  }
988
989
0
  if (PG_ARGISNULL(0))
990
0
  {
991
0
    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
0
    oldcontext = MemoryContextSwitchTo(aggcontext);
1000
0
    state = palloc_object(JsonAggState);
1001
0
    state->str = makeStringInfo();
1002
0
    if (unique_keys)
1003
0
      json_unique_builder_init(&state->unique_check);
1004
0
    else
1005
0
      memset(&state->unique_check, 0, sizeof(state->unique_check));
1006
0
    MemoryContextSwitchTo(oldcontext);
1007
1008
0
    arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1009
1010
0
    if (arg_type == InvalidOid)
1011
0
      ereport(ERROR,
1012
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1013
0
           errmsg("could not determine data type for argument %d", 1)));
1014
1015
0
    json_categorize_type(arg_type, false, &state->key_category,
1016
0
               &state->key_output_func);
1017
1018
0
    arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
1019
1020
0
    if (arg_type == InvalidOid)
1021
0
      ereport(ERROR,
1022
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1023
0
           errmsg("could not determine data type for argument %d", 2)));
1024
1025
0
    json_categorize_type(arg_type, false, &state->val_category,
1026
0
               &state->val_output_func);
1027
1028
0
    appendStringInfoString(state->str, "{ ");
1029
0
  }
1030
0
  else
1031
0
  {
1032
0
    state = (JsonAggState *) PG_GETARG_POINTER(0);
1033
0
  }
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
0
  if (PG_ARGISNULL(1))
1044
0
    ereport(ERROR,
1045
0
        (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1046
0
         errmsg("null value not allowed for object key")));
1047
1048
  /* Skip null values if absent_on_null */
1049
0
  skip = absent_on_null && PG_ARGISNULL(2);
1050
1051
0
  if (skip)
1052
0
  {
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
0
    if (!unique_keys)
1059
0
      PG_RETURN_POINTER(state);
1060
1061
0
    out = json_unique_builder_get_throwawaybuf(&state->unique_check);
1062
0
  }
1063
0
  else
1064
0
  {
1065
0
    out = state->str;
1066
1067
    /*
1068
     * Append comma delimiter only if we have already output some fields
1069
     * after the initial string "{ ".
1070
     */
1071
0
    if (out->len > 2)
1072
0
      appendStringInfoString(out, ", ");
1073
0
  }
1074
1075
0
  arg = PG_GETARG_DATUM(1);
1076
1077
0
  key_offset = out->len;
1078
1079
0
  datum_to_json_internal(arg, false, out, state->key_category,
1080
0
               state->key_output_func, true);
1081
1082
0
  if (unique_keys)
1083
0
  {
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
0
    const char *key = MemoryContextStrdup(aggcontext,
1091
0
                        &out->data[key_offset]);
1092
1093
0
    if (!json_unique_check_key(&state->unique_check.check, key, 0))
1094
0
      ereport(ERROR,
1095
0
          errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1096
0
          errmsg("duplicate JSON object key value: %s", key));
1097
1098
0
    if (skip)
1099
0
      PG_RETURN_POINTER(state);
1100
0
  }
1101
1102
0
  appendStringInfoString(state->str, " : ");
1103
1104
0
  if (PG_ARGISNULL(2))
1105
0
    arg = (Datum) 0;
1106
0
  else
1107
0
    arg = PG_GETARG_DATUM(2);
1108
1109
0
  datum_to_json_internal(arg, PG_ARGISNULL(2), state->str,
1110
0
               state->val_category,
1111
0
               state->val_output_func, false);
1112
1113
0
  PG_RETURN_POINTER(state);
1114
0
}
1115
1116
/*
1117
 * json_object_agg aggregate function
1118
 */
1119
Datum
1120
json_object_agg_transfn(PG_FUNCTION_ARGS)
1121
0
{
1122
0
  return json_object_agg_transfn_worker(fcinfo, false, false);
1123
0
}
1124
1125
/*
1126
 * json_object_agg_strict aggregate function
1127
 */
1128
Datum
1129
json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
1130
0
{
1131
0
  return json_object_agg_transfn_worker(fcinfo, true, false);
1132
0
}
1133
1134
/*
1135
 * json_object_agg_unique aggregate function
1136
 */
1137
Datum
1138
json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
1139
0
{
1140
0
  return json_object_agg_transfn_worker(fcinfo, false, true);
1141
0
}
1142
1143
/*
1144
 * json_object_agg_unique_strict aggregate function
1145
 */
1146
Datum
1147
json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
1148
0
{
1149
0
  return json_object_agg_transfn_worker(fcinfo, true, true);
1150
0
}
1151
1152
/*
1153
 * json_object_agg final function.
1154
 */
1155
Datum
1156
json_object_agg_finalfn(PG_FUNCTION_ARGS)
1157
0
{
1158
0
  JsonAggState *state;
1159
1160
  /* cannot be called directly because of internal-type argument */
1161
0
  Assert(AggCheckCallContext(fcinfo, NULL));
1162
1163
0
  state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
1164
1165
  /* NULL result for no rows in, as is standard with aggregates */
1166
0
  if (state == NULL)
1167
0
    PG_RETURN_NULL();
1168
1169
  /* Else return state with appropriate object terminator added */
1170
0
  PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
1171
0
}
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
catenate_stringinfo_string(StringInfo buffer, const char *addon)
1180
0
{
1181
  /* custom version of cstring_to_text_with_len */
1182
0
  int     buflen = buffer->len;
1183
0
  int     addlen = strlen(addon);
1184
0
  text     *result = (text *) palloc(buflen + addlen + VARHDRSZ);
1185
1186
0
  SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
1187
0
  memcpy(VARDATA(result), buffer->data, buflen);
1188
0
  memcpy(VARDATA(result) + buflen, addon, addlen);
1189
1190
0
  return result;
1191
0
}
1192
1193
Datum
1194
json_build_object_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1195
             bool absent_on_null, bool unique_keys)
1196
0
{
1197
0
  int     i;
1198
0
  const char *sep = "";
1199
0
  StringInfo  result;
1200
0
  JsonUniqueBuilderState unique_check;
1201
1202
0
  if (nargs % 2 != 0)
1203
0
    ereport(ERROR,
1204
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1205
0
         errmsg("argument list must have even number of elements"),
1206
    /* translator: %s is a SQL function name */
1207
0
         errhint("The arguments of %s must consist of alternating keys and values.",
1208
0
             "json_build_object()")));
1209
1210
0
  result = makeStringInfo();
1211
1212
0
  appendStringInfoChar(result, '{');
1213
1214
0
  if (unique_keys)
1215
0
    json_unique_builder_init(&unique_check);
1216
1217
0
  for (i = 0; i < nargs; i += 2)
1218
0
  {
1219
0
    StringInfo  out;
1220
0
    bool    skip;
1221
0
    int     key_offset;
1222
1223
    /* Skip null values if absent_on_null */
1224
0
    skip = absent_on_null && nulls[i + 1];
1225
1226
0
    if (skip)
1227
0
    {
1228
      /* If key uniqueness check is needed we must save skipped keys */
1229
0
      if (!unique_keys)
1230
0
        continue;
1231
1232
0
      out = json_unique_builder_get_throwawaybuf(&unique_check);
1233
0
    }
1234
0
    else
1235
0
    {
1236
0
      appendStringInfoString(result, sep);
1237
0
      sep = ", ";
1238
0
      out = result;
1239
0
    }
1240
1241
    /* process key */
1242
0
    if (nulls[i])
1243
0
      ereport(ERROR,
1244
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1245
0
           errmsg("null value not allowed for object key")));
1246
1247
    /* save key offset before appending it */
1248
0
    key_offset = out->len;
1249
1250
0
    add_json(args[i], false, out, types[i], true);
1251
1252
0
    if (unique_keys)
1253
0
    {
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
0
      const char *key = pstrdup(&out->data[key_offset]);
1263
1264
0
      if (!json_unique_check_key(&unique_check.check, key, 0))
1265
0
        ereport(ERROR,
1266
0
            errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1267
0
            errmsg("duplicate JSON object key value: %s", key));
1268
1269
0
      if (skip)
1270
0
        continue;
1271
0
    }
1272
1273
0
    appendStringInfoString(result, " : ");
1274
1275
    /* process value */
1276
0
    add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
1277
0
  }
1278
1279
0
  appendStringInfoChar(result, '}');
1280
1281
0
  return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
1282
0
}
1283
1284
/*
1285
 * SQL function json_build_object(variadic "any")
1286
 */
1287
Datum
1288
json_build_object(PG_FUNCTION_ARGS)
1289
0
{
1290
0
  Datum    *args;
1291
0
  bool     *nulls;
1292
0
  Oid      *types;
1293
1294
  /* build argument values to build the object */
1295
0
  int     nargs = extract_variadic_args(fcinfo, 0, true,
1296
0
                        &args, &types, &nulls);
1297
1298
0
  if (nargs < 0)
1299
0
    PG_RETURN_NULL();
1300
1301
0
  PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
1302
0
}
1303
1304
/*
1305
 * degenerate case of json_build_object where it gets 0 arguments.
1306
 */
1307
Datum
1308
json_build_object_noargs(PG_FUNCTION_ARGS)
1309
0
{
1310
0
  PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1311
0
}
1312
1313
Datum
1314
json_build_array_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1315
            bool absent_on_null)
1316
0
{
1317
0
  int     i;
1318
0
  const char *sep = "";
1319
0
  StringInfoData result;
1320
1321
0
  initStringInfo(&result);
1322
1323
0
  appendStringInfoChar(&result, '[');
1324
1325
0
  for (i = 0; i < nargs; i++)
1326
0
  {
1327
0
    if (absent_on_null && nulls[i])
1328
0
      continue;
1329
1330
0
    appendStringInfoString(&result, sep);
1331
0
    sep = ", ";
1332
0
    add_json(args[i], nulls[i], &result, types[i], false);
1333
0
  }
1334
1335
0
  appendStringInfoChar(&result, ']');
1336
1337
0
  return PointerGetDatum(cstring_to_text_with_len(result.data, result.len));
1338
0
}
1339
1340
/*
1341
 * SQL function json_build_array(variadic "any")
1342
 */
1343
Datum
1344
json_build_array(PG_FUNCTION_ARGS)
1345
0
{
1346
0
  Datum    *args;
1347
0
  bool     *nulls;
1348
0
  Oid      *types;
1349
1350
  /* build argument values to build the object */
1351
0
  int     nargs = extract_variadic_args(fcinfo, 0, true,
1352
0
                        &args, &types, &nulls);
1353
1354
0
  if (nargs < 0)
1355
0
    PG_RETURN_NULL();
1356
1357
0
  PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
1358
0
}
1359
1360
/*
1361
 * degenerate case of json_build_array where it gets 0 arguments.
1362
 */
1363
Datum
1364
json_build_array_noargs(PG_FUNCTION_ARGS)
1365
0
{
1366
0
  PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
1367
0
}
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
json_object(PG_FUNCTION_ARGS)
1377
0
{
1378
0
  ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
1379
0
  int     ndims = ARR_NDIM(in_array);
1380
0
  StringInfoData result;
1381
0
  Datum    *in_datums;
1382
0
  bool     *in_nulls;
1383
0
  int     in_count,
1384
0
        count,
1385
0
        i;
1386
0
  text     *rval;
1387
1388
0
  switch (ndims)
1389
0
  {
1390
0
    case 0:
1391
0
      PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1392
0
      break;
1393
1394
0
    case 1:
1395
0
      if ((ARR_DIMS(in_array)[0]) % 2)
1396
0
        ereport(ERROR,
1397
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1398
0
             errmsg("array must have even number of elements")));
1399
0
      break;
1400
1401
0
    case 2:
1402
0
      if ((ARR_DIMS(in_array)[1]) != 2)
1403
0
        ereport(ERROR,
1404
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1405
0
             errmsg("array must have two columns")));
1406
0
      break;
1407
1408
0
    default:
1409
0
      ereport(ERROR,
1410
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1411
0
           errmsg("wrong number of array subscripts")));
1412
0
  }
1413
1414
0
  deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
1415
1416
0
  count = in_count / 2;
1417
1418
0
  initStringInfo(&result);
1419
1420
0
  appendStringInfoChar(&result, '{');
1421
1422
0
  for (i = 0; i < count; ++i)
1423
0
  {
1424
0
    if (in_nulls[i * 2])
1425
0
      ereport(ERROR,
1426
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1427
0
           errmsg("null value not allowed for object key")));
1428
1429
0
    if (i > 0)
1430
0
      appendStringInfoString(&result, ", ");
1431
0
    escape_json_text(&result, (text *) DatumGetPointer(in_datums[i * 2]));
1432
0
    appendStringInfoString(&result, " : ");
1433
0
    if (in_nulls[i * 2 + 1])
1434
0
      appendStringInfoString(&result, "null");
1435
0
    else
1436
0
    {
1437
0
      escape_json_text(&result,
1438
0
               (text *) DatumGetPointer(in_datums[i * 2 + 1]));
1439
0
    }
1440
0
  }
1441
1442
0
  appendStringInfoChar(&result, '}');
1443
1444
0
  pfree(in_datums);
1445
0
  pfree(in_nulls);
1446
1447
0
  rval = cstring_to_text_with_len(result.data, result.len);
1448
0
  pfree(result.data);
1449
1450
0
  PG_RETURN_TEXT_P(rval);
1451
0
}
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
json_object_two_arg(PG_FUNCTION_ARGS)
1461
0
{
1462
0
  ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
1463
0
  ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
1464
0
  int     nkdims = ARR_NDIM(key_array);
1465
0
  int     nvdims = ARR_NDIM(val_array);
1466
0
  StringInfoData result;
1467
0
  Datum    *key_datums,
1468
0
         *val_datums;
1469
0
  bool     *key_nulls,
1470
0
         *val_nulls;
1471
0
  int     key_count,
1472
0
        val_count,
1473
0
        i;
1474
0
  text     *rval;
1475
1476
0
  if (nkdims > 1 || nkdims != nvdims)
1477
0
    ereport(ERROR,
1478
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1479
0
         errmsg("wrong number of array subscripts")));
1480
1481
0
  if (nkdims == 0)
1482
0
    PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1483
1484
0
  deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
1485
0
  deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
1486
1487
0
  if (key_count != val_count)
1488
0
    ereport(ERROR,
1489
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1490
0
         errmsg("mismatched array dimensions")));
1491
1492
0
  initStringInfo(&result);
1493
1494
0
  appendStringInfoChar(&result, '{');
1495
1496
0
  for (i = 0; i < key_count; ++i)
1497
0
  {
1498
0
    if (key_nulls[i])
1499
0
      ereport(ERROR,
1500
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1501
0
           errmsg("null value not allowed for object key")));
1502
1503
0
    if (i > 0)
1504
0
      appendStringInfoString(&result, ", ");
1505
0
    escape_json_text(&result, (text *) DatumGetPointer(key_datums[i]));
1506
0
    appendStringInfoString(&result, " : ");
1507
0
    if (val_nulls[i])
1508
0
      appendStringInfoString(&result, "null");
1509
0
    else
1510
0
      escape_json_text(&result,
1511
0
               (text *) DatumGetPointer(val_datums[i]));
1512
0
  }
1513
1514
0
  appendStringInfoChar(&result, '}');
1515
1516
0
  pfree(key_datums);
1517
0
  pfree(key_nulls);
1518
0
  pfree(val_datums);
1519
0
  pfree(val_nulls);
1520
1521
0
  rval = cstring_to_text_with_len(result.data, result.len);
1522
0
  pfree(result.data);
1523
1524
0
  PG_RETURN_TEXT_P(rval);
1525
0
}
1526
1527
/*
1528
 * escape_json_char
1529
 *    Inline helper function for escape_json* functions
1530
 */
1531
static pg_always_inline void
1532
escape_json_char(StringInfo buf, char c)
1533
0
{
1534
0
  switch (c)
1535
0
  {
1536
0
    case '\b':
1537
0
      appendStringInfoString(buf, "\\b");
1538
0
      break;
1539
0
    case '\f':
1540
0
      appendStringInfoString(buf, "\\f");
1541
0
      break;
1542
0
    case '\n':
1543
0
      appendStringInfoString(buf, "\\n");
1544
0
      break;
1545
0
    case '\r':
1546
0
      appendStringInfoString(buf, "\\r");
1547
0
      break;
1548
0
    case '\t':
1549
0
      appendStringInfoString(buf, "\\t");
1550
0
      break;
1551
0
    case '"':
1552
0
      appendStringInfoString(buf, "\\\"");
1553
0
      break;
1554
0
    case '\\':
1555
0
      appendStringInfoString(buf, "\\\\");
1556
0
      break;
1557
0
    default:
1558
0
      if ((unsigned char) c < ' ')
1559
0
        appendStringInfo(buf, "\\u%04x", (int) c);
1560
0
      else
1561
0
        appendStringInfoCharMacro(buf, c);
1562
0
      break;
1563
0
  }
1564
0
}
1565
1566
/*
1567
 * escape_json
1568
 *    Produce a JSON string literal, properly escaping the NUL-terminated
1569
 *    cstring.
1570
 */
1571
void
1572
escape_json(StringInfo buf, const char *str)
1573
0
{
1574
0
  appendStringInfoCharMacro(buf, '"');
1575
1576
0
  for (; *str != '\0'; str++)
1577
0
    escape_json_char(buf, *str);
1578
1579
0
  appendStringInfoCharMacro(buf, '"');
1580
0
}
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
0
#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
escape_json_with_len(StringInfo buf, const char *str, int len)
1602
0
{
1603
0
  int     vlen;
1604
1605
0
  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
0
  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
0
  vlen = len & (int) (~(sizeof(Vector8) - 1));
1619
1620
0
  appendStringInfoCharMacro(buf, '"');
1621
1622
0
  for (int i = 0, copypos = 0;;)
1623
0
  {
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
0
    for (; i < vlen; i += sizeof(Vector8))
1635
0
    {
1636
0
      Vector8   chunk;
1637
1638
0
      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
0
      if (vector8_has_le(chunk, (unsigned char) 0x1F) ||
1645
0
        vector8_has(chunk, (unsigned char) '"') ||
1646
0
        vector8_has(chunk, (unsigned char) '\\'))
1647
0
        break;
1648
1649
0
#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
0
      if (i - copypos >= ESCAPE_JSON_FLUSH_AFTER)
1657
0
      {
1658
0
        appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1659
0
        copypos = i;
1660
0
      }
1661
0
#endif
1662
0
    }
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
0
    if (copypos < i)
1670
0
    {
1671
0
      appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1672
0
      copypos = i;
1673
0
    }
1674
1675
    /*
1676
     * Per-byte loop for Vector8s containing special chars and for
1677
     * processing the tail of the string.
1678
     */
1679
0
    for (size_t b = 0; b < sizeof(Vector8); b++)
1680
0
    {
1681
      /* check if we've finished */
1682
0
      if (i == len)
1683
0
        goto done;
1684
1685
0
      Assert(i < len);
1686
1687
0
      escape_json_char(buf, str[i++]);
1688
0
    }
1689
1690
0
    copypos = i;
1691
    /* We're not done yet.  Try the vector search again. */
1692
0
  }
1693
1694
0
done:
1695
0
  appendStringInfoCharMacro(buf, '"');
1696
0
}
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
escape_json_text(StringInfo buf, const text *txt)
1707
0
{
1708
  /* must cast away the const, unfortunately */
1709
0
  text     *tunpacked = pg_detoast_datum_packed(unconstify(text *, txt));
1710
0
  int     len = VARSIZE_ANY_EXHDR(tunpacked);
1711
0
  char     *str;
1712
1713
0
  str = VARDATA_ANY(tunpacked);
1714
1715
0
  escape_json_with_len(buf, str, len);
1716
1717
  /* pfree any detoasted values */
1718
0
  if (tunpacked != txt)
1719
0
    pfree(tunpacked);
1720
0
}
1721
1722
/* Semantic actions for key uniqueness check */
1723
static JsonParseErrorType
1724
json_unique_object_start(void *_state)
1725
0
{
1726
0
  JsonUniqueParsingState *state = _state;
1727
0
  JsonUniqueStackEntry *entry;
1728
1729
0
  if (!state->unique)
1730
0
    return JSON_SUCCESS;
1731
1732
  /* push object entry to stack */
1733
0
  entry = palloc_object(JsonUniqueStackEntry);
1734
0
  entry->object_id = state->id_counter++;
1735
0
  entry->parent = state->stack;
1736
0
  state->stack = entry;
1737
1738
0
  return JSON_SUCCESS;
1739
0
}
1740
1741
static JsonParseErrorType
1742
json_unique_object_end(void *_state)
1743
0
{
1744
0
  JsonUniqueParsingState *state = _state;
1745
0
  JsonUniqueStackEntry *entry;
1746
1747
0
  if (!state->unique)
1748
0
    return JSON_SUCCESS;
1749
1750
0
  entry = state->stack;
1751
0
  state->stack = entry->parent; /* pop object from stack */
1752
0
  pfree(entry);
1753
0
  return JSON_SUCCESS;
1754
0
}
1755
1756
static JsonParseErrorType
1757
json_unique_object_field_start(void *_state, char *field, bool isnull)
1758
0
{
1759
0
  JsonUniqueParsingState *state = _state;
1760
0
  JsonUniqueStackEntry *entry;
1761
1762
0
  if (!state->unique)
1763
0
    return JSON_SUCCESS;
1764
1765
  /* find key collision in the current object */
1766
0
  if (json_unique_check_key(&state->check, field, state->stack->object_id))
1767
0
    return JSON_SUCCESS;
1768
1769
0
  state->unique = false;
1770
1771
  /* pop all objects entries */
1772
0
  while ((entry = state->stack))
1773
0
  {
1774
0
    state->stack = entry->parent;
1775
0
    pfree(entry);
1776
0
  }
1777
0
  return JSON_SUCCESS;
1778
0
}
1779
1780
/* Validate JSON text and additionally check key uniqueness */
1781
bool
1782
json_validate(text *json, bool check_unique_keys, bool throw_error)
1783
0
{
1784
0
  JsonLexContext lex;
1785
0
  JsonSemAction uniqueSemAction = {0};
1786
0
  JsonUniqueParsingState state;
1787
0
  JsonParseErrorType result;
1788
1789
0
  makeJsonLexContext(&lex, json, check_unique_keys);
1790
1791
0
  if (check_unique_keys)
1792
0
  {
1793
0
    state.lex = &lex;
1794
0
    state.stack = NULL;
1795
0
    state.id_counter = 0;
1796
0
    state.unique = true;
1797
0
    json_unique_check_init(&state.check);
1798
1799
0
    uniqueSemAction.semstate = &state;
1800
0
    uniqueSemAction.object_start = json_unique_object_start;
1801
0
    uniqueSemAction.object_field_start = json_unique_object_field_start;
1802
0
    uniqueSemAction.object_end = json_unique_object_end;
1803
0
  }
1804
1805
0
  result = pg_parse_json(&lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
1806
1807
0
  if (result != JSON_SUCCESS)
1808
0
  {
1809
0
    if (throw_error)
1810
0
      json_errsave_error(result, &lex, NULL);
1811
1812
0
    return false;      /* invalid json */
1813
0
  }
1814
1815
0
  if (check_unique_keys && !state.unique)
1816
0
  {
1817
0
    if (throw_error)
1818
0
      ereport(ERROR,
1819
0
          (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1820
0
           errmsg("duplicate JSON object key value")));
1821
1822
0
    return false;     /* not unique keys */
1823
0
  }
1824
1825
0
  if (check_unique_keys)
1826
0
    freeJsonLexContext(&lex);
1827
1828
0
  return true;       /* ok */
1829
0
}
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
json_typeof(PG_FUNCTION_ARGS)
1845
0
{
1846
0
  text     *json = PG_GETARG_TEXT_PP(0);
1847
0
  JsonLexContext lex;
1848
0
  char     *type;
1849
0
  JsonParseErrorType result;
1850
1851
  /* Lex exactly one token from the input and check its type. */
1852
0
  makeJsonLexContext(&lex, json, false);
1853
0
  result = json_lex(&lex);
1854
0
  if (result != JSON_SUCCESS)
1855
0
    json_errsave_error(result, &lex, NULL);
1856
1857
0
  switch (lex.token_type)
1858
0
  {
1859
0
    case JSON_TOKEN_OBJECT_START:
1860
0
      type = "object";
1861
0
      break;
1862
0
    case JSON_TOKEN_ARRAY_START:
1863
0
      type = "array";
1864
0
      break;
1865
0
    case JSON_TOKEN_STRING:
1866
0
      type = "string";
1867
0
      break;
1868
0
    case JSON_TOKEN_NUMBER:
1869
0
      type = "number";
1870
0
      break;
1871
0
    case JSON_TOKEN_TRUE:
1872
0
    case JSON_TOKEN_FALSE:
1873
0
      type = "boolean";
1874
0
      break;
1875
0
    case JSON_TOKEN_NULL:
1876
0
      type = "null";
1877
0
      break;
1878
0
    default:
1879
0
      elog(ERROR, "unexpected json token: %d", lex.token_type);
1880
0
  }
1881
1882
0
  PG_RETURN_TEXT_P(cstring_to_text(type));
1883
0
}