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