Coverage Report

Created: 2026-11-09 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/utils/adt/jsonb.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * jsonb.c
4
 *    I/O routines for jsonb type
5
 *
6
 * Copyright (c) 2014-2026, PostgreSQL Global Development Group
7
 *
8
 * IDENTIFICATION
9
 *    src/backend/utils/adt/jsonb.c
10
 *
11
 *-------------------------------------------------------------------------
12
 */
13
#include "postgres.h"
14
15
#include "access/htup_details.h"
16
#include "catalog/pg_proc.h"
17
#include "catalog/pg_type.h"
18
#include "funcapi.h"
19
#include "libpq/pqformat.h"
20
#include "miscadmin.h"
21
#include "utils/builtins.h"
22
#include "utils/fmgroids.h"
23
#include "utils/json.h"
24
#include "utils/jsonb.h"
25
#include "utils/jsonfuncs.h"
26
#include "utils/lsyscache.h"
27
#include "utils/typcache.h"
28
29
typedef struct JsonbAggState
30
{
31
  JsonbInState pstate;
32
  JsonTypeCategory key_category;
33
  Oid     key_output_func;
34
  JsonTypeCategory val_category;
35
  Oid     val_output_func;
36
} JsonbAggState;
37
38
static inline Datum jsonb_from_cstring(char *json, int len, bool unique_keys,
39
                     Node *escontext);
40
static bool checkStringLen(size_t len, Node *escontext);
41
static JsonParseErrorType jsonb_in_object_start(void *pstate);
42
static JsonParseErrorType jsonb_in_object_end(void *pstate);
43
static JsonParseErrorType jsonb_in_array_start(void *pstate);
44
static JsonParseErrorType jsonb_in_array_end(void *pstate);
45
static JsonParseErrorType jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
46
static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
47
static JsonParseErrorType jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
48
static void composite_to_jsonb(Datum composite, JsonbInState *result);
49
static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
50
                 const Datum *vals, const bool *nulls, int *valcount,
51
                 JsonTypeCategory tcategory, Oid outfuncoid);
52
static void array_to_jsonb_internal(Datum array, JsonbInState *result);
53
static void datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
54
                  JsonTypeCategory tcategory, Oid outfuncoid,
55
                  bool key_scalar);
56
static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
57
            Oid val_type, bool key_scalar);
58
static char *JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent);
59
static void add_indent(StringInfo out, bool indent, int level);
60
61
/*
62
 * jsonb type input function
63
 */
64
Datum
65
jsonb_in(PG_FUNCTION_ARGS)
66
0
{
67
0
  char     *json = PG_GETARG_CSTRING(0);
68
69
0
  return jsonb_from_cstring(json, strlen(json), false, fcinfo->context);
70
0
}
71
72
/*
73
 * jsonb type recv function
74
 *
75
 * The type is sent as text in binary mode, so this is almost the same
76
 * as the input function, but it's prefixed with a version number so we
77
 * can change the binary format sent in future if necessary. For now,
78
 * only version 1 is supported.
79
 */
80
Datum
81
jsonb_recv(PG_FUNCTION_ARGS)
82
0
{
83
0
  StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
84
0
  int     version = pq_getmsgint(buf, 1);
85
0
  char     *str;
86
0
  int     nbytes;
87
88
0
  if (version == 1)
89
0
    str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
90
0
  else
91
0
    elog(ERROR, "unsupported jsonb version number %d", version);
92
93
0
  return jsonb_from_cstring(str, nbytes, false, NULL);
94
0
}
95
96
/*
97
 * jsonb type output function
98
 */
99
Datum
100
jsonb_out(PG_FUNCTION_ARGS)
101
0
{
102
0
  Jsonb    *jb = PG_GETARG_JSONB_P(0);
103
0
  char     *out;
104
105
0
  out = JsonbToCString(NULL, &jb->root, VARSIZE(jb));
106
107
0
  PG_RETURN_CSTRING(out);
108
0
}
109
110
/*
111
 * jsonb type send function
112
 *
113
 * Just send jsonb as a version number, then a string of text
114
 */
115
Datum
116
jsonb_send(PG_FUNCTION_ARGS)
117
0
{
118
0
  Jsonb    *jb = PG_GETARG_JSONB_P(0);
119
0
  StringInfoData buf;
120
0
  StringInfoData jtext;
121
0
  int     version = 1;
122
123
0
  initStringInfo(&jtext);
124
0
  (void) JsonbToCString(&jtext, &jb->root, VARSIZE(jb));
125
126
0
  pq_begintypsend(&buf);
127
0
  pq_sendint8(&buf, version);
128
0
  pq_sendtext(&buf, jtext.data, jtext.len);
129
0
  pfree(jtext.data);
130
131
0
  PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
132
0
}
133
134
/*
135
 * jsonb_from_text
136
 *
137
 * Turns json text string into a jsonb Datum.
138
 */
139
Datum
140
jsonb_from_text(text *js, bool unique_keys)
141
0
{
142
0
  return jsonb_from_cstring(VARDATA_ANY(js),
143
0
                VARSIZE_ANY_EXHDR(js),
144
0
                unique_keys,
145
0
                NULL);
146
0
}
147
148
/*
149
 * Get the type name of a jsonb container.
150
 */
151
static const char *
152
JsonbContainerTypeName(JsonbContainer *jbc)
153
0
{
154
0
  JsonbValue  scalar;
155
156
0
  if (JsonbExtractScalar(jbc, &scalar))
157
0
    return JsonbTypeName(&scalar);
158
0
  else if (JsonContainerIsArray(jbc))
159
0
    return "array";
160
0
  else if (JsonContainerIsObject(jbc))
161
0
    return "object";
162
0
  else
163
0
  {
164
0
    elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
165
0
    return "unknown";
166
0
  }
167
0
}
168
169
/*
170
 * Get the type name of a jsonb value.
171
 */
172
const char *
173
JsonbTypeName(JsonbValue *val)
174
0
{
175
0
  switch (val->type)
176
0
  {
177
0
    case jbvBinary:
178
0
      return JsonbContainerTypeName(val->val.binary.data);
179
0
    case jbvObject:
180
0
      return "object";
181
0
    case jbvArray:
182
0
      return "array";
183
0
    case jbvNumeric:
184
0
      return "number";
185
0
    case jbvString:
186
0
      return "string";
187
0
    case jbvBool:
188
0
      return "boolean";
189
0
    case jbvNull:
190
0
      return "null";
191
0
    case jbvDatetime:
192
0
      switch (val->val.datetime.typid)
193
0
      {
194
0
        case DATEOID:
195
0
          return "date";
196
0
        case TIMEOID:
197
0
          return "time without time zone";
198
0
        case TIMETZOID:
199
0
          return "time with time zone";
200
0
        case TIMESTAMPOID:
201
0
          return "timestamp without time zone";
202
0
        case TIMESTAMPTZOID:
203
0
          return "timestamp with time zone";
204
0
        default:
205
0
          elog(ERROR, "unrecognized jsonb value datetime type: %d",
206
0
             val->val.datetime.typid);
207
0
      }
208
0
      return "unknown";
209
0
    default:
210
0
      elog(ERROR, "unrecognized jsonb value type: %d", val->type);
211
0
      return "unknown";
212
0
  }
213
0
}
214
215
/*
216
 * SQL function jsonb_typeof(jsonb) -> text
217
 *
218
 * This function is here because the analog json function is in json.c, since
219
 * it uses the json parser internals not exposed elsewhere.
220
 */
221
Datum
222
jsonb_typeof(PG_FUNCTION_ARGS)
223
0
{
224
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
225
0
  const char *result = JsonbContainerTypeName(&in->root);
226
227
0
  PG_RETURN_TEXT_P(cstring_to_text(result));
228
0
}
229
230
/*
231
 * jsonb_from_cstring
232
 *
233
 * Turns json string into a jsonb Datum.
234
 *
235
 * Uses the json parser (with hooks) to construct a jsonb.
236
 *
237
 * If escontext points to an ErrorSaveContext, errors are reported there
238
 * instead of being thrown.
239
 */
240
static inline Datum
241
jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
242
0
{
243
0
  JsonLexContext lex;
244
0
  JsonbInState state;
245
0
  JsonSemAction sem;
246
247
0
  memset(&state, 0, sizeof(state));
248
0
  memset(&sem, 0, sizeof(sem));
249
0
  makeJsonLexContextCstringLen(&lex, json, len, GetDatabaseEncoding(), true);
250
251
0
  state.unique_keys = unique_keys;
252
0
  state.escontext = escontext;
253
0
  sem.semstate = &state;
254
255
0
  sem.object_start = jsonb_in_object_start;
256
0
  sem.array_start = jsonb_in_array_start;
257
0
  sem.object_end = jsonb_in_object_end;
258
0
  sem.array_end = jsonb_in_array_end;
259
0
  sem.scalar = jsonb_in_scalar;
260
0
  sem.object_field_start = jsonb_in_object_field_start;
261
262
0
  if (!pg_parse_json_or_errsave(&lex, &sem, escontext))
263
0
    return (Datum) 0;
264
265
  /* after parsing, the result field has the composed jsonb structure */
266
0
  PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
267
0
}
268
269
static bool
270
checkStringLen(size_t len, Node *escontext)
271
0
{
272
0
  if (len > JENTRY_OFFLENMASK)
273
0
    ereturn(escontext, false,
274
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
275
0
         errmsg("string too long to represent as jsonb string"),
276
0
         errdetail("Due to an implementation restriction, jsonb strings cannot exceed %d bytes.",
277
0
               JENTRY_OFFLENMASK)));
278
279
0
  return true;
280
0
}
281
282
static JsonParseErrorType
283
jsonb_in_object_start(void *pstate)
284
0
{
285
0
  JsonbInState *_state = (JsonbInState *) pstate;
286
287
0
  pushJsonbValue(_state, WJB_BEGIN_OBJECT, NULL);
288
0
  _state->parseState->unique_keys = _state->unique_keys;
289
290
0
  return JSON_SUCCESS;
291
0
}
292
293
static JsonParseErrorType
294
jsonb_in_object_end(void *pstate)
295
0
{
296
0
  JsonbInState *_state = (JsonbInState *) pstate;
297
298
0
  pushJsonbValue(_state, WJB_END_OBJECT, NULL);
299
300
0
  return JSON_SUCCESS;
301
0
}
302
303
static JsonParseErrorType
304
jsonb_in_array_start(void *pstate)
305
0
{
306
0
  JsonbInState *_state = (JsonbInState *) pstate;
307
308
0
  pushJsonbValue(_state, WJB_BEGIN_ARRAY, NULL);
309
310
0
  return JSON_SUCCESS;
311
0
}
312
313
static JsonParseErrorType
314
jsonb_in_array_end(void *pstate)
315
0
{
316
0
  JsonbInState *_state = (JsonbInState *) pstate;
317
318
0
  pushJsonbValue(_state, WJB_END_ARRAY, NULL);
319
320
0
  return JSON_SUCCESS;
321
0
}
322
323
static JsonParseErrorType
324
jsonb_in_object_field_start(void *pstate, char *fname, bool isnull)
325
0
{
326
0
  JsonbInState *_state = (JsonbInState *) pstate;
327
0
  JsonbValue  v;
328
329
0
  Assert(fname != NULL);
330
0
  v.type = jbvString;
331
0
  v.val.string.len = strlen(fname);
332
0
  if (!checkStringLen(v.val.string.len, _state->escontext))
333
0
    return JSON_SEM_ACTION_FAILED;
334
0
  v.val.string.val = fname;
335
336
0
  pushJsonbValue(_state, WJB_KEY, &v);
337
338
0
  return JSON_SUCCESS;
339
0
}
340
341
static void
342
jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal)
343
0
{
344
0
  switch (scalarVal->type)
345
0
  {
346
0
    case jbvNull:
347
0
      appendBinaryStringInfo(out, "null", 4);
348
0
      break;
349
0
    case jbvString:
350
0
      escape_json_with_len(out, scalarVal->val.string.val, scalarVal->val.string.len);
351
0
      break;
352
0
    case jbvNumeric:
353
0
      appendStringInfoString(out,
354
0
                   DatumGetCString(DirectFunctionCall1(numeric_out,
355
0
                                     PointerGetDatum(scalarVal->val.numeric))));
356
0
      break;
357
0
    case jbvBool:
358
0
      if (scalarVal->val.boolean)
359
0
        appendBinaryStringInfo(out, "true", 4);
360
0
      else
361
0
        appendBinaryStringInfo(out, "false", 5);
362
0
      break;
363
0
    default:
364
0
      elog(ERROR, "unknown jsonb scalar type");
365
0
  }
366
0
}
367
368
/*
369
 * For jsonb we always want the de-escaped value - that's what's in token
370
 */
371
static JsonParseErrorType
372
jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype)
373
0
{
374
0
  JsonbInState *_state = (JsonbInState *) pstate;
375
0
  JsonbValue  v;
376
0
  Datum   numd;
377
378
0
  switch (tokentype)
379
0
  {
380
381
0
    case JSON_TOKEN_STRING:
382
0
      Assert(token != NULL);
383
0
      v.type = jbvString;
384
0
      v.val.string.len = strlen(token);
385
0
      if (!checkStringLen(v.val.string.len, _state->escontext))
386
0
        return JSON_SEM_ACTION_FAILED;
387
0
      v.val.string.val = token;
388
0
      break;
389
0
    case JSON_TOKEN_NUMBER:
390
391
      /*
392
       * No need to check size of numeric values, because maximum
393
       * numeric size is well below the JsonbValue restriction
394
       */
395
0
      Assert(token != NULL);
396
0
      v.type = jbvNumeric;
397
0
      if (!DirectInputFunctionCallSafe(numeric_in, token,
398
0
                       InvalidOid, -1,
399
0
                       _state->escontext,
400
0
                       &numd))
401
0
        return JSON_SEM_ACTION_FAILED;
402
0
      v.val.numeric = DatumGetNumeric(numd);
403
0
      break;
404
0
    case JSON_TOKEN_TRUE:
405
0
      v.type = jbvBool;
406
0
      v.val.boolean = true;
407
0
      break;
408
0
    case JSON_TOKEN_FALSE:
409
0
      v.type = jbvBool;
410
0
      v.val.boolean = false;
411
0
      break;
412
0
    case JSON_TOKEN_NULL:
413
0
      v.type = jbvNull;
414
0
      break;
415
0
    default:
416
      /* should not be possible */
417
0
      elog(ERROR, "invalid json token type");
418
0
      break;
419
0
  }
420
421
0
  if (_state->parseState == NULL)
422
0
  {
423
    /* single scalar */
424
0
    JsonbValue  va;
425
426
0
    va.type = jbvArray;
427
0
    va.val.array.rawScalar = true;
428
0
    va.val.array.nElems = 1;
429
430
0
    pushJsonbValue(_state, WJB_BEGIN_ARRAY, &va);
431
0
    pushJsonbValue(_state, WJB_ELEM, &v);
432
0
    pushJsonbValue(_state, WJB_END_ARRAY, NULL);
433
0
  }
434
0
  else
435
0
  {
436
0
    JsonbValue *o = &_state->parseState->contVal;
437
438
0
    switch (o->type)
439
0
    {
440
0
      case jbvArray:
441
0
        pushJsonbValue(_state, WJB_ELEM, &v);
442
0
        break;
443
0
      case jbvObject:
444
0
        pushJsonbValue(_state, WJB_VALUE, &v);
445
0
        break;
446
0
      default:
447
0
        elog(ERROR, "unexpected parent of nested structure");
448
0
    }
449
0
  }
450
451
0
  return JSON_SUCCESS;
452
0
}
453
454
/*
455
 * JsonbToCString
456
 *     Converts jsonb value to a C-string.
457
 *
458
 * If 'out' argument is non-null, the resulting C-string is stored inside the
459
 * StringBuffer.  The resulting string is always returned.
460
 *
461
 * A typical case for passing the StringInfo in rather than NULL is where the
462
 * caller wants access to the len attribute without having to call strlen, e.g.
463
 * if they are converting it to a text* object.
464
 */
465
char *
466
JsonbToCString(StringInfo out, JsonbContainer *in, int estimated_len)
467
0
{
468
0
  return JsonbToCStringWorker(out, in, estimated_len, false);
469
0
}
470
471
/*
472
 * same thing but with indentation turned on
473
 */
474
char *
475
JsonbToCStringIndent(StringInfo out, JsonbContainer *in, int estimated_len)
476
0
{
477
0
  return JsonbToCStringWorker(out, in, estimated_len, true);
478
0
}
479
480
/*
481
 * common worker for above two functions
482
 */
483
static char *
484
JsonbToCStringWorker(StringInfo out, JsonbContainer *in, int estimated_len, bool indent)
485
0
{
486
0
  bool    first = true;
487
0
  JsonbIterator *it;
488
0
  JsonbValue  v;
489
0
  JsonbIteratorToken type = WJB_DONE;
490
0
  int     level = 0;
491
0
  bool    redo_switch = false;
492
493
  /* If we are indenting, don't add a space after a comma */
494
0
  int     ispaces = indent ? 1 : 2;
495
496
  /*
497
   * Don't indent the very first item. This gets set to the indent flag at
498
   * the bottom of the loop.
499
   */
500
0
  bool    use_indent = false;
501
0
  bool    raw_scalar = false;
502
0
  bool    last_was_key = false;
503
504
0
  if (out == NULL)
505
0
    out = makeStringInfo();
506
507
0
  enlargeStringInfo(out, (estimated_len >= 0) ? estimated_len : 64);
508
509
0
  it = JsonbIteratorInit(in);
510
511
0
  while (redo_switch ||
512
0
       ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE))
513
0
  {
514
0
    redo_switch = false;
515
0
    switch (type)
516
0
    {
517
0
      case WJB_BEGIN_ARRAY:
518
0
        if (!first)
519
0
          appendBinaryStringInfo(out, ", ", ispaces);
520
521
0
        if (!v.val.array.rawScalar)
522
0
        {
523
0
          add_indent(out, use_indent && !last_was_key, level);
524
0
          appendStringInfoCharMacro(out, '[');
525
0
        }
526
0
        else
527
0
          raw_scalar = true;
528
529
0
        first = true;
530
0
        level++;
531
0
        break;
532
0
      case WJB_BEGIN_OBJECT:
533
0
        if (!first)
534
0
          appendBinaryStringInfo(out, ", ", ispaces);
535
536
0
        add_indent(out, use_indent && !last_was_key, level);
537
0
        appendStringInfoCharMacro(out, '{');
538
539
0
        first = true;
540
0
        level++;
541
0
        break;
542
0
      case WJB_KEY:
543
0
        if (!first)
544
0
          appendBinaryStringInfo(out, ", ", ispaces);
545
0
        first = true;
546
547
0
        add_indent(out, use_indent, level);
548
549
        /* json rules guarantee this is a string */
550
0
        jsonb_put_escaped_value(out, &v);
551
0
        appendBinaryStringInfo(out, ": ", 2);
552
553
0
        type = JsonbIteratorNext(&it, &v, false);
554
0
        if (type == WJB_VALUE)
555
0
        {
556
0
          first = false;
557
0
          jsonb_put_escaped_value(out, &v);
558
0
        }
559
0
        else
560
0
        {
561
0
          Assert(type == WJB_BEGIN_OBJECT || type == WJB_BEGIN_ARRAY);
562
563
          /*
564
           * We need to rerun the current switch() since we need to
565
           * output the object which we just got from the iterator
566
           * before calling the iterator again.
567
           */
568
0
          redo_switch = true;
569
0
        }
570
0
        break;
571
0
      case WJB_ELEM:
572
0
        if (!first)
573
0
          appendBinaryStringInfo(out, ", ", ispaces);
574
0
        first = false;
575
576
0
        if (!raw_scalar)
577
0
          add_indent(out, use_indent, level);
578
0
        jsonb_put_escaped_value(out, &v);
579
0
        break;
580
0
      case WJB_END_ARRAY:
581
0
        level--;
582
0
        if (!raw_scalar)
583
0
        {
584
0
          add_indent(out, use_indent, level);
585
0
          appendStringInfoCharMacro(out, ']');
586
0
        }
587
0
        first = false;
588
0
        break;
589
0
      case WJB_END_OBJECT:
590
0
        level--;
591
0
        add_indent(out, use_indent, level);
592
0
        appendStringInfoCharMacro(out, '}');
593
0
        first = false;
594
0
        break;
595
0
      default:
596
0
        elog(ERROR, "unknown jsonb iterator token type");
597
0
    }
598
0
    use_indent = indent;
599
0
    last_was_key = redo_switch;
600
0
  }
601
602
0
  Assert(level == 0);
603
604
0
  return out->data;
605
0
}
606
607
static void
608
add_indent(StringInfo out, bool indent, int level)
609
0
{
610
0
  if (indent)
611
0
  {
612
0
    appendStringInfoCharMacro(out, '\n');
613
0
    appendStringInfoSpaces(out, level * 4);
614
0
  }
615
0
}
616
617
618
/*
619
 * Turn a Datum into jsonb, adding it to the result JsonbInState.
620
 *
621
 * tcategory and outfuncoid are from a previous call to json_categorize_type,
622
 * except that if is_null is true then they can be invalid.
623
 *
624
 * If key_scalar is true, the value is stored as a key, so insist
625
 * it's of an acceptable type, and force it to be a jbvString.
626
 *
627
 * Note: currently, we assume that result->escontext is NULL and errors
628
 * will be thrown.
629
 */
630
static void
631
datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
632
            JsonTypeCategory tcategory, Oid outfuncoid,
633
            bool key_scalar)
634
0
{
635
0
  char     *outputstr;
636
0
  Numeric   numeric_val;
637
0
  bool    numeric_to_string;
638
0
  JsonbValue  jb;
639
0
  bool    scalar_jsonb = false;
640
641
0
  check_stack_depth();
642
643
  /* Convert val to a JsonbValue in jb (in most cases) */
644
0
  if (is_null)
645
0
  {
646
0
    Assert(!key_scalar);
647
0
    jb.type = jbvNull;
648
0
  }
649
0
  else if (key_scalar &&
650
0
       (tcategory == JSONTYPE_ARRAY ||
651
0
        tcategory == JSONTYPE_COMPOSITE ||
652
0
        tcategory == JSONTYPE_JSON ||
653
0
        tcategory == JSONTYPE_JSONB ||
654
0
        tcategory == JSONTYPE_CAST))
655
0
  {
656
0
    ereport(ERROR,
657
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
658
0
         errmsg("key value must be scalar, not array, composite, or json")));
659
0
  }
660
0
  else
661
0
  {
662
0
    switch (tcategory)
663
0
    {
664
0
      case JSONTYPE_ARRAY:
665
0
        array_to_jsonb_internal(val, result);
666
0
        break;
667
0
      case JSONTYPE_COMPOSITE:
668
0
        composite_to_jsonb(val, result);
669
0
        break;
670
0
      case JSONTYPE_BOOL:
671
0
        if (key_scalar)
672
0
        {
673
0
          outputstr = DatumGetBool(val) ? "true" : "false";
674
0
          jb.type = jbvString;
675
0
          jb.val.string.len = strlen(outputstr);
676
0
          jb.val.string.val = outputstr;
677
0
        }
678
0
        else
679
0
        {
680
0
          jb.type = jbvBool;
681
0
          jb.val.boolean = DatumGetBool(val);
682
0
        }
683
0
        break;
684
0
      case JSONTYPE_NUMERIC:
685
0
        if (key_scalar)
686
0
        {
687
          /* always stringify keys */
688
0
          numeric_to_string = true;
689
0
          numeric_val = NULL; /* pacify stupider compilers */
690
0
        }
691
0
        else
692
0
        {
693
0
          Datum   numd;
694
695
0
          switch (outfuncoid)
696
0
          {
697
0
            case F_NUMERIC_OUT:
698
0
              numeric_val = DatumGetNumeric(val);
699
0
              break;
700
0
            case F_INT2OUT:
701
0
              numeric_val = int64_to_numeric(DatumGetInt16(val));
702
0
              break;
703
0
            case F_INT4OUT:
704
0
              numeric_val = int64_to_numeric(DatumGetInt32(val));
705
0
              break;
706
0
            case F_INT8OUT:
707
0
              numeric_val = int64_to_numeric(DatumGetInt64(val));
708
0
              break;
709
#ifdef NOT_USED
710
711
              /*
712
               * Ideally we'd short-circuit these two cases
713
               * using float[48]_numeric.  However, those
714
               * functions are currently slower than the generic
715
               * coerce-via-I/O approach.  And they may round
716
               * off differently.  Until/unless that gets fixed,
717
               * continue to use coerce-via-I/O for floats.
718
               */
719
            case F_FLOAT4OUT:
720
              numd = DirectFunctionCall1(float4_numeric, val);
721
              numeric_val = DatumGetNumeric(numd);
722
              break;
723
            case F_FLOAT8OUT:
724
              numd = DirectFunctionCall1(float8_numeric, val);
725
              numeric_val = DatumGetNumeric(numd);
726
              break;
727
#endif
728
0
            default:
729
0
              outputstr = OidOutputFunctionCall(outfuncoid, val);
730
0
              numd = DirectFunctionCall3(numeric_in,
731
0
                             CStringGetDatum(outputstr),
732
0
                             ObjectIdGetDatum(InvalidOid),
733
0
                             Int32GetDatum(-1));
734
0
              numeric_val = DatumGetNumeric(numd);
735
0
              break;
736
0
          }
737
          /* Must convert to string if it's Inf or NaN */
738
0
          numeric_to_string = (numeric_is_inf(numeric_val) ||
739
0
                     numeric_is_nan(numeric_val));
740
0
        }
741
0
        if (numeric_to_string)
742
0
        {
743
0
          outputstr = OidOutputFunctionCall(outfuncoid, val);
744
0
          jb.type = jbvString;
745
0
          jb.val.string.len = strlen(outputstr);
746
0
          jb.val.string.val = outputstr;
747
0
        }
748
0
        else
749
0
        {
750
0
          jb.type = jbvNumeric;
751
0
          jb.val.numeric = numeric_val;
752
0
        }
753
0
        break;
754
0
      case JSONTYPE_DATE:
755
0
        jb.type = jbvString;
756
0
        jb.val.string.val = JsonEncodeDateTime(NULL, val,
757
0
                             DATEOID, NULL);
758
0
        jb.val.string.len = strlen(jb.val.string.val);
759
0
        break;
760
0
      case JSONTYPE_TIMESTAMP:
761
0
        jb.type = jbvString;
762
0
        jb.val.string.val = JsonEncodeDateTime(NULL, val,
763
0
                             TIMESTAMPOID, NULL);
764
0
        jb.val.string.len = strlen(jb.val.string.val);
765
0
        break;
766
0
      case JSONTYPE_TIMESTAMPTZ:
767
0
        jb.type = jbvString;
768
0
        jb.val.string.val = JsonEncodeDateTime(NULL, val,
769
0
                             TIMESTAMPTZOID, NULL);
770
0
        jb.val.string.len = strlen(jb.val.string.val);
771
0
        break;
772
0
      case JSONTYPE_CAST:
773
        /* cast to JSON, and then process as JSON */
774
0
        val = OidFunctionCall1(outfuncoid, val);
775
0
        pg_fallthrough;
776
0
      case JSONTYPE_JSON:
777
0
        {
778
          /* parse the json right into the existing result object */
779
0
          JsonLexContext lex;
780
0
          JsonSemAction sem;
781
0
          text     *json = DatumGetTextPP(val);
782
783
0
          makeJsonLexContext(&lex, json, true);
784
785
0
          memset(&sem, 0, sizeof(sem));
786
787
0
          sem.semstate = result;
788
789
0
          sem.object_start = jsonb_in_object_start;
790
0
          sem.array_start = jsonb_in_array_start;
791
0
          sem.object_end = jsonb_in_object_end;
792
0
          sem.array_end = jsonb_in_array_end;
793
0
          sem.scalar = jsonb_in_scalar;
794
0
          sem.object_field_start = jsonb_in_object_field_start;
795
796
0
          pg_parse_json_or_ereport(&lex, &sem);
797
0
          freeJsonLexContext(&lex);
798
0
        }
799
0
        break;
800
0
      case JSONTYPE_JSONB:
801
0
        {
802
0
          Jsonb    *jsonb = DatumGetJsonbP(val);
803
0
          JsonbIterator *it;
804
805
0
          it = JsonbIteratorInit(&jsonb->root);
806
807
0
          if (JB_ROOT_IS_SCALAR(jsonb))
808
0
          {
809
0
            (void) JsonbIteratorNext(&it, &jb, true);
810
0
            Assert(jb.type == jbvArray);
811
0
            (void) JsonbIteratorNext(&it, &jb, true);
812
0
            scalar_jsonb = true;
813
0
          }
814
0
          else
815
0
          {
816
0
            JsonbIteratorToken type;
817
818
0
            while ((type = JsonbIteratorNext(&it, &jb, false))
819
0
                 != WJB_DONE)
820
0
            {
821
0
              if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
822
0
                type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
823
0
                pushJsonbValue(result, type, NULL);
824
0
              else
825
0
                pushJsonbValue(result, type, &jb);
826
0
            }
827
0
          }
828
0
        }
829
0
        break;
830
0
      default:
831
        /* special-case text types to save useless palloc/memcpy ops */
832
0
        if (outfuncoid == F_TEXTOUT ||
833
0
          outfuncoid == F_VARCHAROUT ||
834
0
          outfuncoid == F_BPCHAROUT)
835
0
        {
836
0
          text     *txt = DatumGetTextPP(val);
837
838
0
          jb.val.string.len = VARSIZE_ANY_EXHDR(txt);
839
0
          jb.val.string.val = VARDATA_ANY(txt);
840
0
        }
841
0
        else
842
0
        {
843
0
          outputstr = OidOutputFunctionCall(outfuncoid, val);
844
0
          jb.val.string.len = strlen(outputstr);
845
0
          jb.val.string.val = outputstr;
846
0
        }
847
0
        jb.type = jbvString;
848
0
        (void) checkStringLen(jb.val.string.len, NULL);
849
0
        break;
850
0
    }
851
0
  }
852
853
  /* Now insert jb into result, unless we did it recursively */
854
0
  if (!is_null && !scalar_jsonb &&
855
0
    tcategory >= JSONTYPE_JSON && tcategory <= JSONTYPE_CAST)
856
0
  {
857
    /* work has been done recursively */
858
0
    return;
859
0
  }
860
0
  else if (result->parseState == NULL)
861
0
  {
862
    /* single root scalar */
863
0
    JsonbValue  va;
864
865
0
    va.type = jbvArray;
866
0
    va.val.array.rawScalar = true;
867
0
    va.val.array.nElems = 1;
868
869
0
    pushJsonbValue(result, WJB_BEGIN_ARRAY, &va);
870
0
    pushJsonbValue(result, WJB_ELEM, &jb);
871
0
    pushJsonbValue(result, WJB_END_ARRAY, NULL);
872
0
  }
873
0
  else
874
0
  {
875
0
    JsonbValue *o = &result->parseState->contVal;
876
877
0
    switch (o->type)
878
0
    {
879
0
      case jbvArray:
880
0
        pushJsonbValue(result, WJB_ELEM, &jb);
881
0
        break;
882
0
      case jbvObject:
883
0
        pushJsonbValue(result,
884
0
                 key_scalar ? WJB_KEY : WJB_VALUE,
885
0
                 &jb);
886
0
        break;
887
0
      default:
888
0
        elog(ERROR, "unexpected parent of nested structure");
889
0
    }
890
0
  }
891
0
}
892
893
/*
894
 * Process a single dimension of an array.
895
 * If it's the innermost dimension, output the values, otherwise call
896
 * ourselves recursively to process the next dimension.
897
 */
898
static void
899
array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, const Datum *vals,
900
           const bool *nulls, int *valcount, JsonTypeCategory tcategory,
901
           Oid outfuncoid)
902
0
{
903
0
  int     i;
904
905
0
  Assert(dim < ndims);
906
907
0
  pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
908
909
0
  for (i = 1; i <= dims[dim]; i++)
910
0
  {
911
0
    if (dim + 1 == ndims)
912
0
    {
913
0
      datum_to_jsonb_internal(vals[*valcount], nulls[*valcount], result, tcategory,
914
0
                  outfuncoid, false);
915
0
      (*valcount)++;
916
0
    }
917
0
    else
918
0
    {
919
0
      array_dim_to_jsonb(result, dim + 1, ndims, dims, vals, nulls,
920
0
                 valcount, tcategory, outfuncoid);
921
0
    }
922
0
  }
923
924
0
  pushJsonbValue(result, WJB_END_ARRAY, NULL);
925
0
}
926
927
/*
928
 * Turn an array into JSON.
929
 */
930
static void
931
array_to_jsonb_internal(Datum array, JsonbInState *result)
932
0
{
933
0
  ArrayType  *v = DatumGetArrayTypeP(array);
934
0
  Oid     element_type = ARR_ELEMTYPE(v);
935
0
  int      *dim;
936
0
  int     ndim;
937
0
  int     nitems;
938
0
  int     count = 0;
939
0
  Datum    *elements;
940
0
  bool     *nulls;
941
0
  int16   typlen;
942
0
  bool    typbyval;
943
0
  char    typalign;
944
0
  JsonTypeCategory tcategory;
945
0
  Oid     outfuncoid;
946
947
0
  ndim = ARR_NDIM(v);
948
0
  dim = ARR_DIMS(v);
949
0
  nitems = ArrayGetNItems(ndim, dim);
950
951
0
  if (nitems <= 0)
952
0
  {
953
0
    pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
954
0
    pushJsonbValue(result, WJB_END_ARRAY, NULL);
955
0
    return;
956
0
  }
957
958
0
  get_typlenbyvalalign(element_type,
959
0
             &typlen, &typbyval, &typalign);
960
961
0
  json_categorize_type(element_type, true,
962
0
             &tcategory, &outfuncoid);
963
964
0
  deconstruct_array(v, element_type, typlen, typbyval,
965
0
            typalign, &elements, &nulls,
966
0
            &nitems);
967
968
0
  array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory,
969
0
             outfuncoid);
970
971
0
  pfree(elements);
972
0
  pfree(nulls);
973
0
}
974
975
/*
976
 * Turn a composite / record into JSON.
977
 */
978
static void
979
composite_to_jsonb(Datum composite, JsonbInState *result)
980
0
{
981
0
  HeapTupleHeader td;
982
0
  Oid     tupType;
983
0
  int32   tupTypmod;
984
0
  TupleDesc tupdesc;
985
0
  HeapTupleData tmptup,
986
0
         *tuple;
987
0
  int     i;
988
989
0
  td = DatumGetHeapTupleHeader(composite);
990
991
  /* Extract rowtype info and find a tupdesc */
992
0
  tupType = HeapTupleHeaderGetTypeId(td);
993
0
  tupTypmod = HeapTupleHeaderGetTypMod(td);
994
0
  tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
995
996
  /* Build a temporary HeapTuple control structure */
997
0
  tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
998
0
  tmptup.t_data = td;
999
0
  tuple = &tmptup;
1000
1001
0
  pushJsonbValue(result, WJB_BEGIN_OBJECT, NULL);
1002
1003
0
  for (i = 0; i < tupdesc->natts; i++)
1004
0
  {
1005
0
    Datum   val;
1006
0
    bool    isnull;
1007
0
    char     *attname;
1008
0
    JsonTypeCategory tcategory;
1009
0
    Oid     outfuncoid;
1010
0
    JsonbValue  v;
1011
0
    Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1012
1013
0
    if (att->attisdropped)
1014
0
      continue;
1015
1016
0
    attname = NameStr(att->attname);
1017
1018
0
    v.type = jbvString;
1019
    /* don't need checkStringLen here - can't exceed maximum name length */
1020
0
    v.val.string.len = strlen(attname);
1021
0
    v.val.string.val = attname;
1022
1023
0
    pushJsonbValue(result, WJB_KEY, &v);
1024
1025
0
    val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
1026
1027
0
    if (isnull)
1028
0
    {
1029
0
      tcategory = JSONTYPE_NULL;
1030
0
      outfuncoid = InvalidOid;
1031
0
    }
1032
0
    else
1033
0
      json_categorize_type(att->atttypid, true, &tcategory,
1034
0
                 &outfuncoid);
1035
1036
0
    datum_to_jsonb_internal(val, isnull, result, tcategory, outfuncoid,
1037
0
                false);
1038
0
  }
1039
1040
0
  pushJsonbValue(result, WJB_END_OBJECT, NULL);
1041
0
  ReleaseTupleDesc(tupdesc);
1042
0
}
1043
1044
/*
1045
 * Append JSON text for "val" to "result".
1046
 *
1047
 * This is just a thin wrapper around datum_to_jsonb.  If the same type will be
1048
 * printed many times, avoid using this; better to do the json_categorize_type
1049
 * lookups only once.
1050
 */
1051
1052
static void
1053
add_jsonb(Datum val, bool is_null, JsonbInState *result,
1054
      Oid val_type, bool key_scalar)
1055
0
{
1056
0
  JsonTypeCategory tcategory;
1057
0
  Oid     outfuncoid;
1058
1059
0
  if (val_type == InvalidOid)
1060
0
    ereport(ERROR,
1061
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1062
0
         errmsg("could not determine input data type")));
1063
1064
0
  if (is_null)
1065
0
  {
1066
0
    tcategory = JSONTYPE_NULL;
1067
0
    outfuncoid = InvalidOid;
1068
0
  }
1069
0
  else
1070
0
    json_categorize_type(val_type, true,
1071
0
               &tcategory, &outfuncoid);
1072
1073
0
  datum_to_jsonb_internal(val, is_null, result, tcategory, outfuncoid,
1074
0
              key_scalar);
1075
0
}
1076
1077
1078
/*
1079
 * Is the given type immutable when coming out of a JSONB context?
1080
 *
1081
 * At present, datetimes are all considered mutable, because they
1082
 * depend on timezone.  XXX we should also drill down into objects and
1083
 * arrays, but do not.
1084
 */
1085
bool
1086
to_jsonb_is_immutable(Oid typoid)
1087
0
{
1088
0
  JsonTypeCategory tcategory;
1089
0
  Oid     outfuncoid;
1090
1091
0
  json_categorize_type(typoid, true, &tcategory, &outfuncoid);
1092
1093
0
  switch (tcategory)
1094
0
  {
1095
0
    case JSONTYPE_NULL:
1096
0
    case JSONTYPE_BOOL:
1097
0
    case JSONTYPE_JSON:
1098
0
    case JSONTYPE_JSONB:
1099
0
      return true;
1100
1101
0
    case JSONTYPE_DATE:
1102
0
    case JSONTYPE_TIMESTAMP:
1103
0
    case JSONTYPE_TIMESTAMPTZ:
1104
0
      return false;
1105
1106
0
    case JSONTYPE_ARRAY:
1107
0
      return false;   /* TODO recurse into elements */
1108
1109
0
    case JSONTYPE_COMPOSITE:
1110
0
      return false;   /* TODO recurse into fields */
1111
1112
0
    case JSONTYPE_NUMERIC:
1113
0
    case JSONTYPE_CAST:
1114
0
    case JSONTYPE_OTHER:
1115
0
      return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
1116
0
  }
1117
1118
0
  return false;       /* not reached */
1119
0
}
1120
1121
/*
1122
 * SQL function to_jsonb(anyvalue)
1123
 */
1124
Datum
1125
to_jsonb(PG_FUNCTION_ARGS)
1126
0
{
1127
0
  Datum   val = PG_GETARG_DATUM(0);
1128
0
  Oid     val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
1129
0
  JsonTypeCategory tcategory;
1130
0
  Oid     outfuncoid;
1131
1132
0
  if (val_type == InvalidOid)
1133
0
    ereport(ERROR,
1134
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1135
0
         errmsg("could not determine input data type")));
1136
1137
0
  json_categorize_type(val_type, true,
1138
0
             &tcategory, &outfuncoid);
1139
1140
0
  PG_RETURN_DATUM(datum_to_jsonb(val, tcategory, outfuncoid));
1141
0
}
1142
1143
/*
1144
 * Turn a Datum into jsonb.
1145
 *
1146
 * tcategory and outfuncoid are from a previous call to json_categorize_type.
1147
 */
1148
Datum
1149
datum_to_jsonb(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
1150
0
{
1151
0
  JsonbInState result;
1152
1153
0
  memset(&result, 0, sizeof(JsonbInState));
1154
1155
0
  datum_to_jsonb_internal(val, false, &result, tcategory, outfuncoid,
1156
0
              false);
1157
1158
0
  return JsonbPGetDatum(JsonbValueToJsonb(result.result));
1159
0
}
1160
1161
Datum
1162
jsonb_build_object_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1163
              bool absent_on_null, bool unique_keys)
1164
0
{
1165
0
  int     i;
1166
0
  JsonbInState result;
1167
1168
0
  if (nargs % 2 != 0)
1169
0
    ereport(ERROR,
1170
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1171
0
         errmsg("argument list must have even number of elements"),
1172
    /* translator: %s is a SQL function name */
1173
0
         errhint("The arguments of %s must consist of alternating keys and values.",
1174
0
             "jsonb_build_object()")));
1175
1176
0
  memset(&result, 0, sizeof(JsonbInState));
1177
1178
0
  pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
1179
0
  result.parseState->unique_keys = unique_keys;
1180
0
  result.parseState->skip_nulls = absent_on_null;
1181
1182
0
  for (i = 0; i < nargs; i += 2)
1183
0
  {
1184
    /* process key */
1185
0
    bool    skip;
1186
1187
0
    if (nulls[i])
1188
0
      ereport(ERROR,
1189
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1190
0
           errmsg("argument %d: key must not be null", i + 1)));
1191
1192
    /* skip null values if absent_on_null */
1193
0
    skip = absent_on_null && nulls[i + 1];
1194
1195
    /* we need to save skipped keys for the key uniqueness check */
1196
0
    if (skip && !unique_keys)
1197
0
      continue;
1198
1199
0
    add_jsonb(args[i], false, &result, types[i], true);
1200
1201
    /* process value */
1202
0
    add_jsonb(args[i + 1], nulls[i + 1], &result, types[i + 1], false);
1203
0
  }
1204
1205
0
  pushJsonbValue(&result, WJB_END_OBJECT, NULL);
1206
1207
0
  return JsonbPGetDatum(JsonbValueToJsonb(result.result));
1208
0
}
1209
1210
/*
1211
 * SQL function jsonb_build_object(variadic "any")
1212
 */
1213
Datum
1214
jsonb_build_object(PG_FUNCTION_ARGS)
1215
0
{
1216
0
  Datum    *args;
1217
0
  bool     *nulls;
1218
0
  Oid      *types;
1219
1220
  /* build argument values to build the object */
1221
0
  int     nargs = extract_variadic_args(fcinfo, 0, true,
1222
0
                        &args, &types, &nulls);
1223
1224
0
  if (nargs < 0)
1225
0
    PG_RETURN_NULL();
1226
1227
0
  PG_RETURN_DATUM(jsonb_build_object_worker(nargs, args, nulls, types, false, false));
1228
0
}
1229
1230
/*
1231
 * degenerate case of jsonb_build_object where it gets 0 arguments.
1232
 */
1233
Datum
1234
jsonb_build_object_noargs(PG_FUNCTION_ARGS)
1235
0
{
1236
0
  JsonbInState result;
1237
1238
0
  memset(&result, 0, sizeof(JsonbInState));
1239
1240
0
  pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
1241
0
  pushJsonbValue(&result, WJB_END_OBJECT, NULL);
1242
1243
0
  PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
1244
0
}
1245
1246
Datum
1247
jsonb_build_array_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1248
             bool absent_on_null)
1249
0
{
1250
0
  int     i;
1251
0
  JsonbInState result;
1252
1253
0
  memset(&result, 0, sizeof(JsonbInState));
1254
1255
0
  pushJsonbValue(&result, WJB_BEGIN_ARRAY, NULL);
1256
1257
0
  for (i = 0; i < nargs; i++)
1258
0
  {
1259
0
    if (absent_on_null && nulls[i])
1260
0
      continue;
1261
1262
0
    add_jsonb(args[i], nulls[i], &result, types[i], false);
1263
0
  }
1264
1265
0
  pushJsonbValue(&result, WJB_END_ARRAY, NULL);
1266
1267
0
  return JsonbPGetDatum(JsonbValueToJsonb(result.result));
1268
0
}
1269
1270
/*
1271
 * SQL function jsonb_build_array(variadic "any")
1272
 */
1273
Datum
1274
jsonb_build_array(PG_FUNCTION_ARGS)
1275
0
{
1276
0
  Datum    *args;
1277
0
  bool     *nulls;
1278
0
  Oid      *types;
1279
1280
  /* build argument values to build the object */
1281
0
  int     nargs = extract_variadic_args(fcinfo, 0, true,
1282
0
                        &args, &types, &nulls);
1283
1284
0
  if (nargs < 0)
1285
0
    PG_RETURN_NULL();
1286
1287
0
  PG_RETURN_DATUM(jsonb_build_array_worker(nargs, args, nulls, types, false));
1288
0
}
1289
1290
1291
/*
1292
 * degenerate case of jsonb_build_array where it gets 0 arguments.
1293
 */
1294
Datum
1295
jsonb_build_array_noargs(PG_FUNCTION_ARGS)
1296
0
{
1297
0
  JsonbInState result;
1298
1299
0
  memset(&result, 0, sizeof(JsonbInState));
1300
1301
0
  pushJsonbValue(&result, WJB_BEGIN_ARRAY, NULL);
1302
0
  pushJsonbValue(&result, WJB_END_ARRAY, NULL);
1303
1304
0
  PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
1305
0
}
1306
1307
1308
/*
1309
 * SQL function jsonb_object(text[])
1310
 *
1311
 * take a one or two dimensional array of text as name value pairs
1312
 * for a jsonb object.
1313
 *
1314
 */
1315
Datum
1316
jsonb_object(PG_FUNCTION_ARGS)
1317
0
{
1318
0
  ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
1319
0
  int     ndims = ARR_NDIM(in_array);
1320
0
  Datum    *in_datums;
1321
0
  bool     *in_nulls;
1322
0
  int     in_count,
1323
0
        count,
1324
0
        i;
1325
0
  JsonbInState result;
1326
1327
0
  memset(&result, 0, sizeof(JsonbInState));
1328
1329
0
  pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
1330
1331
0
  switch (ndims)
1332
0
  {
1333
0
    case 0:
1334
0
      goto close_object;
1335
0
      break;
1336
1337
0
    case 1:
1338
0
      if ((ARR_DIMS(in_array)[0]) % 2)
1339
0
        ereport(ERROR,
1340
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1341
0
             errmsg("array must have even number of elements")));
1342
0
      break;
1343
1344
0
    case 2:
1345
0
      if ((ARR_DIMS(in_array)[1]) != 2)
1346
0
        ereport(ERROR,
1347
0
            (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1348
0
             errmsg("array must have two columns")));
1349
0
      break;
1350
1351
0
    default:
1352
0
      ereport(ERROR,
1353
0
          (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1354
0
           errmsg("wrong number of array subscripts")));
1355
0
  }
1356
1357
0
  deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
1358
1359
0
  count = in_count / 2;
1360
1361
0
  for (i = 0; i < count; ++i)
1362
0
  {
1363
0
    JsonbValue  v;
1364
0
    char     *str;
1365
0
    int     len;
1366
1367
0
    if (in_nulls[i * 2])
1368
0
      ereport(ERROR,
1369
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1370
0
           errmsg("null value not allowed for object key")));
1371
1372
0
    str = TextDatumGetCString(in_datums[i * 2]);
1373
0
    len = strlen(str);
1374
1375
0
    v.type = jbvString;
1376
1377
0
    v.val.string.len = len;
1378
0
    v.val.string.val = str;
1379
1380
0
    pushJsonbValue(&result, WJB_KEY, &v);
1381
1382
0
    if (in_nulls[i * 2 + 1])
1383
0
    {
1384
0
      v.type = jbvNull;
1385
0
    }
1386
0
    else
1387
0
    {
1388
0
      str = TextDatumGetCString(in_datums[i * 2 + 1]);
1389
0
      len = strlen(str);
1390
1391
0
      v.type = jbvString;
1392
1393
0
      v.val.string.len = len;
1394
0
      v.val.string.val = str;
1395
0
    }
1396
1397
0
    pushJsonbValue(&result, WJB_VALUE, &v);
1398
0
  }
1399
1400
0
  pfree(in_datums);
1401
0
  pfree(in_nulls);
1402
1403
0
close_object:
1404
0
  pushJsonbValue(&result, WJB_END_OBJECT, NULL);
1405
1406
0
  PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
1407
0
}
1408
1409
/*
1410
 * SQL function jsonb_object(text[], text[])
1411
 *
1412
 * take separate name and value arrays of text to construct a jsonb object
1413
 * pairwise.
1414
 */
1415
Datum
1416
jsonb_object_two_arg(PG_FUNCTION_ARGS)
1417
0
{
1418
0
  ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
1419
0
  ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
1420
0
  int     nkdims = ARR_NDIM(key_array);
1421
0
  int     nvdims = ARR_NDIM(val_array);
1422
0
  Datum    *key_datums,
1423
0
         *val_datums;
1424
0
  bool     *key_nulls,
1425
0
         *val_nulls;
1426
0
  int     key_count,
1427
0
        val_count,
1428
0
        i;
1429
0
  JsonbInState result;
1430
1431
0
  memset(&result, 0, sizeof(JsonbInState));
1432
1433
0
  pushJsonbValue(&result, WJB_BEGIN_OBJECT, NULL);
1434
1435
0
  if (nkdims > 1 || nkdims != nvdims)
1436
0
    ereport(ERROR,
1437
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1438
0
         errmsg("wrong number of array subscripts")));
1439
1440
0
  if (nkdims == 0)
1441
0
    goto close_object;
1442
1443
0
  deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
1444
0
  deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
1445
1446
0
  if (key_count != val_count)
1447
0
    ereport(ERROR,
1448
0
        (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1449
0
         errmsg("mismatched array dimensions")));
1450
1451
0
  for (i = 0; i < key_count; ++i)
1452
0
  {
1453
0
    JsonbValue  v;
1454
0
    char     *str;
1455
0
    int     len;
1456
1457
0
    if (key_nulls[i])
1458
0
      ereport(ERROR,
1459
0
          (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1460
0
           errmsg("null value not allowed for object key")));
1461
1462
0
    str = TextDatumGetCString(key_datums[i]);
1463
0
    len = strlen(str);
1464
1465
0
    v.type = jbvString;
1466
1467
0
    v.val.string.len = len;
1468
0
    v.val.string.val = str;
1469
1470
0
    pushJsonbValue(&result, WJB_KEY, &v);
1471
1472
0
    if (val_nulls[i])
1473
0
    {
1474
0
      v.type = jbvNull;
1475
0
    }
1476
0
    else
1477
0
    {
1478
0
      str = TextDatumGetCString(val_datums[i]);
1479
0
      len = strlen(str);
1480
1481
0
      v.type = jbvString;
1482
1483
0
      v.val.string.len = len;
1484
0
      v.val.string.val = str;
1485
0
    }
1486
1487
0
    pushJsonbValue(&result, WJB_VALUE, &v);
1488
0
  }
1489
1490
0
  pfree(key_datums);
1491
0
  pfree(key_nulls);
1492
0
  pfree(val_datums);
1493
0
  pfree(val_nulls);
1494
1495
0
close_object:
1496
0
  pushJsonbValue(&result, WJB_END_OBJECT, NULL);
1497
1498
0
  PG_RETURN_POINTER(JsonbValueToJsonb(result.result));
1499
0
}
1500
1501
1502
/*
1503
 * Functions for jsonb_agg, jsonb_object_agg, and variants
1504
 */
1505
1506
static Datum
1507
jsonb_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
1508
0
{
1509
0
  MemoryContext aggcontext;
1510
0
  JsonbAggState *state;
1511
0
  Datum   val;
1512
0
  JsonbInState *result;
1513
1514
0
  if (!AggCheckCallContext(fcinfo, &aggcontext))
1515
0
  {
1516
    /* cannot be called directly because of internal-type argument */
1517
0
    elog(ERROR, "jsonb_agg_transfn called in non-aggregate context");
1518
0
  }
1519
1520
  /* set up the accumulator on the first go round */
1521
1522
0
  if (PG_ARGISNULL(0))
1523
0
  {
1524
0
    Oid     arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1525
1526
0
    if (arg_type == InvalidOid)
1527
0
      ereport(ERROR,
1528
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1529
0
           errmsg("could not determine input data type")));
1530
1531
0
    state = MemoryContextAllocZero(aggcontext, sizeof(JsonbAggState));
1532
0
    result = &state->pstate;
1533
0
    result->outcontext = aggcontext;
1534
0
    pushJsonbValue(result, WJB_BEGIN_ARRAY, NULL);
1535
1536
0
    json_categorize_type(arg_type, true, &state->val_category,
1537
0
               &state->val_output_func);
1538
0
  }
1539
0
  else
1540
0
  {
1541
0
    state = (JsonbAggState *) PG_GETARG_POINTER(0);
1542
0
    result = &state->pstate;
1543
0
  }
1544
1545
0
  if (absent_on_null && PG_ARGISNULL(1))
1546
0
    PG_RETURN_POINTER(state);
1547
1548
  /*
1549
   * We run this code in the normal function context, so that we don't leak
1550
   * any cruft from datatype output functions and such into the aggcontext.
1551
   * But the "result" JsonbValue will be constructed in aggcontext, so that
1552
   * it remains available across calls.
1553
   */
1554
0
  val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
1555
1556
0
  datum_to_jsonb_internal(val, PG_ARGISNULL(1), result, state->val_category,
1557
0
              state->val_output_func, false);
1558
1559
0
  PG_RETURN_POINTER(state);
1560
0
}
1561
1562
/*
1563
 * jsonb_agg aggregate function
1564
 */
1565
Datum
1566
jsonb_agg_transfn(PG_FUNCTION_ARGS)
1567
0
{
1568
0
  return jsonb_agg_transfn_worker(fcinfo, false);
1569
0
}
1570
1571
/*
1572
 * jsonb_agg_strict aggregate function
1573
 */
1574
Datum
1575
jsonb_agg_strict_transfn(PG_FUNCTION_ARGS)
1576
0
{
1577
0
  return jsonb_agg_transfn_worker(fcinfo, true);
1578
0
}
1579
1580
Datum
1581
jsonb_agg_finalfn(PG_FUNCTION_ARGS)
1582
0
{
1583
0
  JsonbAggState *arg;
1584
0
  JsonbInState result;
1585
0
  Jsonb    *out;
1586
1587
  /* cannot be called directly because of internal-type argument */
1588
0
  Assert(AggCheckCallContext(fcinfo, NULL));
1589
1590
0
  if (PG_ARGISNULL(0))
1591
0
    PG_RETURN_NULL();   /* returns null iff no input values */
1592
1593
0
  arg = (JsonbAggState *) PG_GETARG_POINTER(0);
1594
1595
  /*
1596
   * The final function can be called more than once, so we must not change
1597
   * the stored JsonbValue data structure.  Fortunately, the WJB_END_ARRAY
1598
   * action will only change fields in the JsonbInState struct itself, so we
1599
   * can simply invoke pushJsonbValue on a local copy of that.
1600
   */
1601
0
  result = arg->pstate;
1602
1603
0
  pushJsonbValue(&result, WJB_END_ARRAY, NULL);
1604
1605
  /* We expect result.parseState == NULL after closing the array */
1606
0
  Assert(result.parseState == NULL);
1607
1608
0
  out = JsonbValueToJsonb(result.result);
1609
1610
0
  PG_RETURN_POINTER(out);
1611
0
}
1612
1613
static Datum
1614
jsonb_object_agg_transfn_worker(FunctionCallInfo fcinfo,
1615
                bool absent_on_null, bool unique_keys)
1616
0
{
1617
0
  MemoryContext aggcontext;
1618
0
  JsonbAggState *state;
1619
0
  Datum   val;
1620
0
  JsonbInState *result;
1621
0
  bool    skip;
1622
1623
0
  if (!AggCheckCallContext(fcinfo, &aggcontext))
1624
0
  {
1625
    /* cannot be called directly because of internal-type argument */
1626
0
    elog(ERROR, "jsonb_object_agg_transfn called in non-aggregate context");
1627
0
  }
1628
1629
  /* set up the accumulator on the first go round */
1630
1631
0
  if (PG_ARGISNULL(0))
1632
0
  {
1633
0
    Oid     arg_type;
1634
1635
0
    state = MemoryContextAllocZero(aggcontext, sizeof(JsonbAggState));
1636
0
    result = &state->pstate;
1637
0
    result->outcontext = aggcontext;
1638
0
    pushJsonbValue(result, WJB_BEGIN_OBJECT, NULL);
1639
0
    result->parseState->unique_keys = unique_keys;
1640
0
    result->parseState->skip_nulls = absent_on_null;
1641
1642
0
    arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1643
1644
0
    if (arg_type == InvalidOid)
1645
0
      ereport(ERROR,
1646
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1647
0
           errmsg("could not determine input data type")));
1648
1649
0
    json_categorize_type(arg_type, true, &state->key_category,
1650
0
               &state->key_output_func);
1651
1652
0
    arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
1653
1654
0
    if (arg_type == InvalidOid)
1655
0
      ereport(ERROR,
1656
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1657
0
           errmsg("could not determine input data type")));
1658
1659
0
    json_categorize_type(arg_type, true, &state->val_category,
1660
0
               &state->val_output_func);
1661
0
  }
1662
0
  else
1663
0
  {
1664
0
    state = (JsonbAggState *) PG_GETARG_POINTER(0);
1665
0
    result = &state->pstate;
1666
0
  }
1667
1668
0
  if (PG_ARGISNULL(1))
1669
0
    ereport(ERROR,
1670
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1671
0
         errmsg("field name must not be null")));
1672
1673
  /*
1674
   * Skip null values if absent_on_null unless key uniqueness check is
1675
   * needed (because we must save keys in this case).
1676
   */
1677
0
  skip = absent_on_null && PG_ARGISNULL(2);
1678
1679
0
  if (skip && !unique_keys)
1680
0
    PG_RETURN_POINTER(state);
1681
1682
  /*
1683
   * We run this code in the normal function context, so that we don't leak
1684
   * any cruft from datatype output functions and such into the aggcontext.
1685
   * But the "result" JsonbValue will be constructed in aggcontext, so that
1686
   * it remains available across calls.
1687
   */
1688
0
  val = PG_GETARG_DATUM(1);
1689
1690
0
  datum_to_jsonb_internal(val, false, result, state->key_category,
1691
0
              state->key_output_func, true);
1692
1693
0
  val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
1694
1695
0
  datum_to_jsonb_internal(val, PG_ARGISNULL(2), result, state->val_category,
1696
0
              state->val_output_func, false);
1697
1698
0
  PG_RETURN_POINTER(state);
1699
0
}
1700
1701
/*
1702
 * jsonb_object_agg aggregate function
1703
 */
1704
Datum
1705
jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
1706
0
{
1707
0
  return jsonb_object_agg_transfn_worker(fcinfo, false, false);
1708
0
}
1709
1710
1711
/*
1712
 * jsonb_object_agg_strict aggregate function
1713
 */
1714
Datum
1715
jsonb_object_agg_strict_transfn(PG_FUNCTION_ARGS)
1716
0
{
1717
0
  return jsonb_object_agg_transfn_worker(fcinfo, true, false);
1718
0
}
1719
1720
/*
1721
 * jsonb_object_agg_unique aggregate function
1722
 */
1723
Datum
1724
jsonb_object_agg_unique_transfn(PG_FUNCTION_ARGS)
1725
0
{
1726
0
  return jsonb_object_agg_transfn_worker(fcinfo, false, true);
1727
0
}
1728
1729
/*
1730
 * jsonb_object_agg_unique_strict aggregate function
1731
 */
1732
Datum
1733
jsonb_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
1734
0
{
1735
0
  return jsonb_object_agg_transfn_worker(fcinfo, true, true);
1736
0
}
1737
1738
Datum
1739
jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
1740
0
{
1741
0
  JsonbAggState *arg;
1742
0
  JsonbInState result;
1743
0
  JsonbParseState copyps;
1744
0
  Jsonb    *out;
1745
1746
  /* cannot be called directly because of internal-type argument */
1747
0
  Assert(AggCheckCallContext(fcinfo, NULL));
1748
1749
0
  if (PG_ARGISNULL(0))
1750
0
    PG_RETURN_NULL();   /* returns null iff no input values */
1751
1752
0
  arg = (JsonbAggState *) PG_GETARG_POINTER(0);
1753
1754
  /*
1755
   * The final function can be called more than once, so we must not change
1756
   * the stored JsonbValue data structure.  Fortunately, the WJB_END_OBJECT
1757
   * action will only destructively change fields in the JsonbInState struct
1758
   * itself, so we can simply invoke pushJsonbValue on a local copy of that.
1759
   * Note that this will run uniqueifyJsonbObject each time; that's hard to
1760
   * avoid, since duplicate pairs may have been added since the previous
1761
   * finalization.  uniqueifyJsonbObject can be applied repeatedly (with the
1762
   * same unique_keys/skip_nulls options) without breaking the data
1763
   * structure invariants, except that it removes null values if told to.
1764
   * That's problematic when unique_keys is also set, since that might
1765
   * prevent detection of a later-added key that duplicates an existing one
1766
   * with null value.  Therefore, when both options are set, make a copy of
1767
   * the pairs array and uniqueify that, keeping the original unmodified.
1768
   */
1769
0
  result = arg->pstate;
1770
0
  if (result.parseState->unique_keys && result.parseState->skip_nulls)
1771
0
  {
1772
0
    JsonbParseState *oldps = result.parseState;
1773
0
    int     npairs = oldps->contVal.val.object.nPairs;
1774
1775
0
    Assert(oldps->next == NULL);
1776
0
    copyps = *oldps;
1777
0
    copyps.contVal.val.object.pairs = palloc_array(JsonbPair, npairs);
1778
0
    memcpy(copyps.contVal.val.object.pairs,
1779
0
         oldps->contVal.val.object.pairs,
1780
0
         npairs * sizeof(JsonbPair));
1781
0
    result.parseState = &copyps;
1782
0
  }
1783
1784
0
  pushJsonbValue(&result, WJB_END_OBJECT, NULL);
1785
1786
  /* We expect result.parseState == NULL after closing the object */
1787
0
  Assert(result.parseState == NULL);
1788
1789
0
  out = JsonbValueToJsonb(result.result);
1790
1791
0
  PG_RETURN_POINTER(out);
1792
0
}
1793
1794
1795
/*
1796
 * Extract scalar value from raw-scalar pseudo-array jsonb.
1797
 */
1798
bool
1799
JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
1800
0
{
1801
0
  JsonbIterator *it;
1802
0
  JsonbIteratorToken tok PG_USED_FOR_ASSERTS_ONLY;
1803
0
  JsonbValue  tmp;
1804
1805
0
  if (!JsonContainerIsArray(jbc) || !JsonContainerIsScalar(jbc))
1806
0
  {
1807
    /* inform caller about actual type of container */
1808
0
    res->type = (JsonContainerIsArray(jbc)) ? jbvArray : jbvObject;
1809
0
    return false;
1810
0
  }
1811
1812
  /*
1813
   * A root scalar is stored as an array of one element, so we get the array
1814
   * and then its first (and only) member.
1815
   */
1816
0
  it = JsonbIteratorInit(jbc);
1817
1818
0
  tok = JsonbIteratorNext(&it, &tmp, true);
1819
0
  Assert(tok == WJB_BEGIN_ARRAY);
1820
0
  Assert(tmp.val.array.nElems == 1 && tmp.val.array.rawScalar);
1821
1822
0
  tok = JsonbIteratorNext(&it, res, true);
1823
0
  Assert(tok == WJB_ELEM);
1824
0
  Assert(IsAJsonbScalar(res));
1825
1826
0
  tok = JsonbIteratorNext(&it, &tmp, true);
1827
0
  Assert(tok == WJB_END_ARRAY);
1828
1829
0
  tok = JsonbIteratorNext(&it, &tmp, true);
1830
0
  Assert(tok == WJB_DONE);
1831
1832
0
  return true;
1833
0
}
1834
1835
/*
1836
 * Emit correct, translatable cast error message
1837
 */
1838
static Datum
1839
cannotCastJsonbValue(enum jbvType type, const char *sqltype, Node *escontext)
1840
0
{
1841
0
  static const struct
1842
0
  {
1843
0
    enum jbvType type;
1844
0
    const char *msg;
1845
0
  }
1846
0
        messages[] =
1847
0
  {
1848
0
    {jbvNull, gettext_noop("cannot cast jsonb null to type %s")},
1849
0
    {jbvString, gettext_noop("cannot cast jsonb string to type %s")},
1850
0
    {jbvNumeric, gettext_noop("cannot cast jsonb numeric to type %s")},
1851
0
    {jbvBool, gettext_noop("cannot cast jsonb boolean to type %s")},
1852
0
    {jbvArray, gettext_noop("cannot cast jsonb array to type %s")},
1853
0
    {jbvObject, gettext_noop("cannot cast jsonb object to type %s")},
1854
0
    {jbvBinary, gettext_noop("cannot cast jsonb array or object to type %s")}
1855
0
  };
1856
1857
0
  for (size_t i = 0; i < lengthof(messages); i++)
1858
0
    if (messages[i].type == type)
1859
0
      ereturn(escontext, (Datum) 0,
1860
0
          (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1861
0
           errmsg(messages[i].msg, sqltype)));
1862
1863
  /* should be unreachable */
1864
0
  elog(ERROR, "unknown jsonb type: %d", (int) type);
1865
0
  return (Datum) 0;
1866
0
}
1867
1868
Datum
1869
jsonb_bool(PG_FUNCTION_ARGS)
1870
0
{
1871
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
1872
0
  JsonbValue  v;
1873
1874
0
  if (!JsonbExtractScalar(&in->root, &v))
1875
0
    return cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
1876
1877
0
  if (v.type == jbvNull)
1878
0
  {
1879
0
    PG_FREE_IF_COPY(in, 0);
1880
0
    PG_RETURN_NULL();
1881
0
  }
1882
1883
0
  if (v.type != jbvBool)
1884
0
    return cannotCastJsonbValue(v.type, "boolean", fcinfo->context);
1885
1886
0
  PG_FREE_IF_COPY(in, 0);
1887
1888
0
  PG_RETURN_BOOL(v.val.boolean);
1889
0
}
1890
1891
Datum
1892
jsonb_numeric(PG_FUNCTION_ARGS)
1893
0
{
1894
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
1895
0
  JsonbValue  v;
1896
0
  Numeric   retValue;
1897
1898
0
  if (!JsonbExtractScalar(&in->root, &v))
1899
0
    return cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
1900
1901
0
  if (v.type == jbvNull)
1902
0
  {
1903
0
    PG_FREE_IF_COPY(in, 0);
1904
0
    PG_RETURN_NULL();
1905
0
  }
1906
1907
0
  if (v.type != jbvNumeric)
1908
0
    return cannotCastJsonbValue(v.type, "numeric", fcinfo->context);
1909
1910
  /*
1911
   * v.val.numeric points into jsonb body, so we need to make a copy to
1912
   * return
1913
   */
1914
0
  retValue = DatumGetNumericCopy(NumericGetDatum(v.val.numeric));
1915
1916
0
  PG_FREE_IF_COPY(in, 0);
1917
1918
0
  PG_RETURN_NUMERIC(retValue);
1919
0
}
1920
1921
Datum
1922
jsonb_int2(PG_FUNCTION_ARGS)
1923
0
{
1924
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
1925
0
  JsonbValue  v;
1926
0
  Datum   retValue;
1927
1928
0
  if (!JsonbExtractScalar(&in->root, &v))
1929
0
    return cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
1930
1931
0
  if (v.type == jbvNull)
1932
0
  {
1933
0
    PG_FREE_IF_COPY(in, 0);
1934
0
    PG_RETURN_NULL();
1935
0
  }
1936
1937
0
  if (v.type != jbvNumeric)
1938
0
    return cannotCastJsonbValue(v.type, "smallint", fcinfo->context);
1939
1940
0
  retValue = DirectFunctionCall1(numeric_int2,
1941
0
                   NumericGetDatum(v.val.numeric));
1942
1943
0
  PG_FREE_IF_COPY(in, 0);
1944
1945
0
  PG_RETURN_DATUM(retValue);
1946
0
}
1947
1948
Datum
1949
jsonb_int4(PG_FUNCTION_ARGS)
1950
0
{
1951
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
1952
0
  JsonbValue  v;
1953
0
  Datum   retValue;
1954
1955
0
  if (!JsonbExtractScalar(&in->root, &v))
1956
0
    return cannotCastJsonbValue(v.type, "integer", fcinfo->context);
1957
1958
0
  if (v.type == jbvNull)
1959
0
  {
1960
0
    PG_FREE_IF_COPY(in, 0);
1961
0
    PG_RETURN_NULL();
1962
0
  }
1963
1964
0
  if (v.type != jbvNumeric)
1965
0
    return cannotCastJsonbValue(v.type, "integer", fcinfo->context);
1966
1967
0
  retValue = DirectFunctionCall1(numeric_int4,
1968
0
                   NumericGetDatum(v.val.numeric));
1969
1970
0
  PG_FREE_IF_COPY(in, 0);
1971
1972
0
  PG_RETURN_DATUM(retValue);
1973
0
}
1974
1975
Datum
1976
jsonb_int8(PG_FUNCTION_ARGS)
1977
0
{
1978
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
1979
0
  JsonbValue  v;
1980
0
  Datum   retValue;
1981
1982
0
  if (!JsonbExtractScalar(&in->root, &v))
1983
0
    return cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
1984
1985
0
  if (v.type == jbvNull)
1986
0
  {
1987
0
    PG_FREE_IF_COPY(in, 0);
1988
0
    PG_RETURN_NULL();
1989
0
  }
1990
1991
0
  if (v.type != jbvNumeric)
1992
0
    return cannotCastJsonbValue(v.type, "bigint", fcinfo->context);
1993
1994
0
  retValue = DirectFunctionCall1(numeric_int8,
1995
0
                   NumericGetDatum(v.val.numeric));
1996
1997
0
  PG_FREE_IF_COPY(in, 0);
1998
1999
0
  PG_RETURN_DATUM(retValue);
2000
0
}
2001
2002
Datum
2003
jsonb_float4(PG_FUNCTION_ARGS)
2004
0
{
2005
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
2006
0
  JsonbValue  v;
2007
0
  Datum   retValue;
2008
2009
0
  if (!JsonbExtractScalar(&in->root, &v))
2010
0
    return cannotCastJsonbValue(v.type, "real", fcinfo->context);
2011
2012
0
  if (v.type == jbvNull)
2013
0
  {
2014
0
    PG_FREE_IF_COPY(in, 0);
2015
0
    PG_RETURN_NULL();
2016
0
  }
2017
2018
0
  if (v.type != jbvNumeric)
2019
0
    return cannotCastJsonbValue(v.type, "real", fcinfo->context);
2020
2021
0
  retValue = DirectFunctionCall1(numeric_float4,
2022
0
                   NumericGetDatum(v.val.numeric));
2023
2024
0
  PG_FREE_IF_COPY(in, 0);
2025
2026
0
  PG_RETURN_DATUM(retValue);
2027
0
}
2028
2029
Datum
2030
jsonb_float8(PG_FUNCTION_ARGS)
2031
0
{
2032
0
  Jsonb    *in = PG_GETARG_JSONB_P(0);
2033
0
  JsonbValue  v;
2034
0
  Datum   retValue;
2035
2036
0
  if (!JsonbExtractScalar(&in->root, &v))
2037
0
    return cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
2038
2039
0
  if (v.type == jbvNull)
2040
0
  {
2041
0
    PG_FREE_IF_COPY(in, 0);
2042
0
    PG_RETURN_NULL();
2043
0
  }
2044
2045
0
  if (v.type != jbvNumeric)
2046
0
    return cannotCastJsonbValue(v.type, "double precision", fcinfo->context);
2047
2048
0
  retValue = DirectFunctionCall1(numeric_float8,
2049
0
                   NumericGetDatum(v.val.numeric));
2050
2051
0
  PG_FREE_IF_COPY(in, 0);
2052
2053
0
  PG_RETURN_DATUM(retValue);
2054
0
}
2055
2056
/*
2057
 * Convert jsonb to a C-string stripping quotes from scalar strings.
2058
 */
2059
char *
2060
JsonbUnquote(Jsonb *jb)
2061
0
{
2062
0
  if (JB_ROOT_IS_SCALAR(jb))
2063
0
  {
2064
0
    JsonbValue  v;
2065
2066
0
    (void) JsonbExtractScalar(&jb->root, &v);
2067
2068
0
    if (v.type == jbvString)
2069
0
      return pnstrdup(v.val.string.val, v.val.string.len);
2070
0
    else if (v.type == jbvBool)
2071
0
      return pstrdup(v.val.boolean ? "true" : "false");
2072
0
    else if (v.type == jbvNumeric)
2073
0
      return DatumGetCString(DirectFunctionCall1(numeric_out,
2074
0
                             PointerGetDatum(v.val.numeric)));
2075
0
    else if (v.type == jbvNull)
2076
0
      return pstrdup("null");
2077
0
    else
2078
0
    {
2079
0
      elog(ERROR, "unrecognized jsonb value type %d", v.type);
2080
0
      return NULL;
2081
0
    }
2082
0
  }
2083
0
  else
2084
0
    return JsonbToCString(NULL, &jb->root, VARSIZE(jb));
2085
0
}