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