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