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