Coverage Report

Created: 2026-09-28 06:55

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