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