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