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