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