Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * jsonfuncs.c
4 : * Functions to process JSON data types.
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : * Portions Copyright (c) 1994, Regents of the University of California
8 : *
9 : * IDENTIFICATION
10 : * src/backend/utils/adt/jsonfuncs.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include <limits.h>
18 :
19 : #include "access/htup_details.h"
20 : #include "catalog/pg_type.h"
21 : #include "fmgr.h"
22 : #include "funcapi.h"
23 : #include "lib/stringinfo.h"
24 : #include "mb/pg_wchar.h"
25 : #include "miscadmin.h"
26 : #include "utils/array.h"
27 : #include "utils/builtins.h"
28 : #include "utils/hsearch.h"
29 : #include "utils/json.h"
30 : #include "utils/jsonapi.h"
31 : #include "utils/jsonb.h"
32 : #include "utils/lsyscache.h"
33 : #include "utils/memutils.h"
34 : #include "utils/syscache.h"
35 : #include "utils/typcache.h"
36 :
37 : /* Operations available for setPath */
38 : #define JB_PATH_CREATE 0x0001
39 : #define JB_PATH_DELETE 0x0002
40 : #define JB_PATH_REPLACE 0x0004
41 : #define JB_PATH_INSERT_BEFORE 0x0008
42 : #define JB_PATH_INSERT_AFTER 0x0010
43 : #define JB_PATH_CREATE_OR_INSERT \
44 : (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE)
45 :
46 : /* state for json_object_keys */
47 : typedef struct OkeysState
48 : {
49 : JsonLexContext *lex;
50 : char **result;
51 : int result_size;
52 : int result_count;
53 : int sent_count;
54 : } OkeysState;
55 :
56 : /* state for iterate_json_string_values function */
57 : typedef struct IterateJsonStringValuesState
58 : {
59 : JsonLexContext *lex;
60 : JsonIterateStringValuesAction action; /* an action that will be applied
61 : * to each json value */
62 : void *action_state; /* any necessary context for iteration */
63 : } IterateJsonStringValuesState;
64 :
65 : /* state for transform_json_string_values function */
66 : typedef struct TransformJsonStringValuesState
67 : {
68 : JsonLexContext *lex;
69 : StringInfo strval; /* resulting json */
70 : JsonTransformStringValuesAction action; /* an action that will be applied
71 : * to each json value */
72 : void *action_state; /* any necessary context for transformation */
73 : } TransformJsonStringValuesState;
74 :
75 : /* state for json_get* functions */
76 : typedef struct GetState
77 : {
78 : JsonLexContext *lex;
79 : text *tresult;
80 : char *result_start;
81 : bool normalize_results;
82 : bool next_scalar;
83 : int npath; /* length of each path-related array */
84 : char **path_names; /* field name(s) being sought */
85 : int *path_indexes; /* array index(es) being sought */
86 : bool *pathok; /* is path matched to current depth? */
87 : int *array_cur_index; /* current element index at each path
88 : * level */
89 : } GetState;
90 :
91 : /* state for json_array_length */
92 : typedef struct AlenState
93 : {
94 : JsonLexContext *lex;
95 : int count;
96 : } AlenState;
97 :
98 : /* state for json_each */
99 : typedef struct EachState
100 : {
101 : JsonLexContext *lex;
102 : Tuplestorestate *tuple_store;
103 : TupleDesc ret_tdesc;
104 : MemoryContext tmp_cxt;
105 : char *result_start;
106 : bool normalize_results;
107 : bool next_scalar;
108 : char *normalized_scalar;
109 : } EachState;
110 :
111 : /* state for json_array_elements */
112 : typedef struct ElementsState
113 : {
114 : JsonLexContext *lex;
115 : const char *function_name;
116 : Tuplestorestate *tuple_store;
117 : TupleDesc ret_tdesc;
118 : MemoryContext tmp_cxt;
119 : char *result_start;
120 : bool normalize_results;
121 : bool next_scalar;
122 : char *normalized_scalar;
123 : } ElementsState;
124 :
125 : /* state for get_json_object_as_hash */
126 : typedef struct JHashState
127 : {
128 : JsonLexContext *lex;
129 : const char *function_name;
130 : HTAB *hash;
131 : char *saved_scalar;
132 : char *save_json_start;
133 : JsonTokenType saved_token_type;
134 : } JHashState;
135 :
136 : /* hashtable element */
137 : typedef struct JsonHashEntry
138 : {
139 : char fname[NAMEDATALEN]; /* hash key (MUST BE FIRST) */
140 : char *val;
141 : JsonTokenType type;
142 : } JsonHashEntry;
143 :
144 : /* structure to cache type I/O metadata needed for populate_scalar() */
145 : typedef struct ScalarIOData
146 : {
147 : Oid typioparam;
148 : FmgrInfo typiofunc;
149 : } ScalarIOData;
150 :
151 : /* these two structures are used recursively */
152 : typedef struct ColumnIOData ColumnIOData;
153 : typedef struct RecordIOData RecordIOData;
154 :
155 : /* structure to cache metadata needed for populate_array() */
156 : typedef struct ArrayIOData
157 : {
158 : ColumnIOData *element_info; /* metadata cache */
159 : Oid element_type; /* array element type id */
160 : int32 element_typmod; /* array element type modifier */
161 : } ArrayIOData;
162 :
163 : /* structure to cache metadata needed for populate_composite() */
164 : typedef struct CompositeIOData
165 : {
166 : /*
167 : * We use pointer to a RecordIOData here because variable-length struct
168 : * RecordIOData can't be used directly in ColumnIOData.io union
169 : */
170 : RecordIOData *record_io; /* metadata cache for populate_record() */
171 : TupleDesc tupdesc; /* cached tuple descriptor */
172 : } CompositeIOData;
173 :
174 : /* structure to cache metadata needed for populate_domain() */
175 : typedef struct DomainIOData
176 : {
177 : ColumnIOData *base_io; /* metadata cache */
178 : Oid base_typid; /* base type id */
179 : int32 base_typmod; /* base type modifier */
180 : void *domain_info; /* opaque cache for domain checks */
181 : } DomainIOData;
182 :
183 : /* enumeration type categories */
184 : typedef enum TypeCat
185 : {
186 : TYPECAT_SCALAR = 's',
187 : TYPECAT_ARRAY = 'a',
188 : TYPECAT_COMPOSITE = 'c',
189 : TYPECAT_DOMAIN = 'd'
190 : } TypeCat;
191 :
192 : /* these two are stolen from hstore / record_out, used in populate_record* */
193 :
194 : /* structure to cache record metadata needed for populate_record_field() */
195 : struct ColumnIOData
196 : {
197 : Oid typid; /* column type id */
198 : int32 typmod; /* column type modifier */
199 : TypeCat typcat; /* column type category */
200 : ScalarIOData scalar_io; /* metadata cache for directi conversion
201 : * through input function */
202 : union
203 : {
204 : ArrayIOData array;
205 : CompositeIOData composite;
206 : DomainIOData domain;
207 : } io; /* metadata cache for various column type
208 : * categories */
209 : };
210 :
211 : /* structure to cache record metadata needed for populate_record() */
212 : struct RecordIOData
213 : {
214 : Oid record_type;
215 : int32 record_typmod;
216 : int ncolumns;
217 : ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
218 : };
219 :
220 : /* state for populate_recordset */
221 : typedef struct PopulateRecordsetState
222 : {
223 : JsonLexContext *lex;
224 : const char *function_name;
225 : HTAB *json_hash;
226 : char *saved_scalar;
227 : char *save_json_start;
228 : JsonTokenType saved_token_type;
229 : Tuplestorestate *tuple_store;
230 : TupleDesc ret_tdesc;
231 : HeapTupleHeader rec;
232 : RecordIOData **my_extra;
233 : MemoryContext fn_mcxt; /* used to stash IO funcs */
234 : } PopulateRecordsetState;
235 :
236 : /* structure to cache metadata needed for populate_record_worker() */
237 : typedef struct PopulateRecordCache
238 : {
239 : Oid argtype; /* verified row type of the first argument */
240 : CompositeIOData io; /* metadata cache for populate_composite() */
241 : } PopulateRecordCache;
242 :
243 : /* common data for populate_array_json() and populate_array_dim_jsonb() */
244 : typedef struct PopulateArrayContext
245 : {
246 : ArrayBuildState *astate; /* array build state */
247 : ArrayIOData *aio; /* metadata cache */
248 : MemoryContext acxt; /* array build memory context */
249 : MemoryContext mcxt; /* cache memory context */
250 : const char *colname; /* for diagnostics only */
251 : int *dims; /* dimensions */
252 : int *sizes; /* current dimension counters */
253 : int ndims; /* number of dimensions */
254 : } PopulateArrayContext;
255 :
256 : /* state for populate_array_json() */
257 : typedef struct PopulateArrayState
258 : {
259 : JsonLexContext *lex; /* json lexer */
260 : PopulateArrayContext *ctx; /* context */
261 : char *element_start; /* start of the current array element */
262 : char *element_scalar; /* current array element token if it is a
263 : * scalar */
264 : JsonTokenType element_type; /* current array element type */
265 : } PopulateArrayState;
266 :
267 : /* state for json_strip_nulls */
268 : typedef struct StripnullState
269 : {
270 : JsonLexContext *lex;
271 : StringInfo strval;
272 : bool skip_next_null;
273 : } StripnullState;
274 :
275 : /* structure for generalized json/jsonb value passing */
276 : typedef struct JsValue
277 : {
278 : bool is_json; /* json/jsonb */
279 : union
280 : {
281 : struct
282 : {
283 : char *str; /* json string */
284 : int len; /* json string length or -1 if null-terminated */
285 : JsonTokenType type; /* json type */
286 : } json; /* json value */
287 :
288 : JsonbValue *jsonb; /* jsonb value */
289 : } val;
290 : } JsValue;
291 :
292 : typedef struct JsObject
293 : {
294 : bool is_json; /* json/jsonb */
295 : union
296 : {
297 : HTAB *json_hash;
298 : JsonbContainer *jsonb_cont;
299 : } val;
300 : } JsObject;
301 :
302 : /* useful macros for testing JsValue properties */
303 : #define JsValueIsNull(jsv) \
304 : ((jsv)->is_json ? \
305 : (!(jsv)->val.json.str || (jsv)->val.json.type == JSON_TOKEN_NULL) : \
306 : (!(jsv)->val.jsonb || (jsv)->val.jsonb->type == jbvNull))
307 :
308 : #define JsValueIsString(jsv) \
309 : ((jsv)->is_json ? (jsv)->val.json.type == JSON_TOKEN_STRING \
310 : : ((jsv)->val.jsonb && (jsv)->val.jsonb->type == jbvString))
311 :
312 : #define JsObjectIsEmpty(jso) \
313 : ((jso)->is_json \
314 : ? hash_get_num_entries((jso)->val.json_hash) == 0 \
315 : : ((jso)->val.jsonb_cont == NULL || \
316 : JsonContainerSize((jso)->val.jsonb_cont) == 0))
317 :
318 : #define JsObjectFree(jso) \
319 : do { \
320 : if ((jso)->is_json) \
321 : hash_destroy((jso)->val.json_hash); \
322 : } while (0)
323 :
324 : /* semantic action functions for json_object_keys */
325 : static void okeys_object_field_start(void *state, char *fname, bool isnull);
326 : static void okeys_array_start(void *state);
327 : static void okeys_scalar(void *state, char *token, JsonTokenType tokentype);
328 :
329 : /* semantic action functions for json_get* functions */
330 : static void get_object_start(void *state);
331 : static void get_object_end(void *state);
332 : static void get_object_field_start(void *state, char *fname, bool isnull);
333 : static void get_object_field_end(void *state, char *fname, bool isnull);
334 : static void get_array_start(void *state);
335 : static void get_array_end(void *state);
336 : static void get_array_element_start(void *state, bool isnull);
337 : static void get_array_element_end(void *state, bool isnull);
338 : static void get_scalar(void *state, char *token, JsonTokenType tokentype);
339 :
340 : /* common worker function for json getter functions */
341 : static Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
342 : static text *get_worker(text *json, char **tpath, int *ipath, int npath,
343 : bool normalize_results);
344 : static Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
345 :
346 : /* semantic action functions for json_array_length */
347 : static void alen_object_start(void *state);
348 : static void alen_scalar(void *state, char *token, JsonTokenType tokentype);
349 : static void alen_array_element_start(void *state, bool isnull);
350 :
351 : /* common workers for json{b}_each* functions */
352 : static Datum each_worker(FunctionCallInfo fcinfo, bool as_text);
353 : static Datum each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
354 : bool as_text);
355 :
356 : /* semantic action functions for json_each */
357 : static void each_object_field_start(void *state, char *fname, bool isnull);
358 : static void each_object_field_end(void *state, char *fname, bool isnull);
359 : static void each_array_start(void *state);
360 : static void each_scalar(void *state, char *token, JsonTokenType tokentype);
361 :
362 : /* common workers for json{b}_array_elements_* functions */
363 : static Datum elements_worker(FunctionCallInfo fcinfo, const char *funcname,
364 : bool as_text);
365 : static Datum elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
366 : bool as_text);
367 :
368 : /* semantic action functions for json_array_elements */
369 : static void elements_object_start(void *state);
370 : static void elements_array_element_start(void *state, bool isnull);
371 : static void elements_array_element_end(void *state, bool isnull);
372 : static void elements_scalar(void *state, char *token, JsonTokenType tokentype);
373 :
374 : /* turn a json object into a hash table */
375 : static HTAB *get_json_object_as_hash(char *json, int len, const char *funcname);
376 :
377 : /* semantic actions for populate_array_json */
378 : static void populate_array_object_start(void *_state);
379 : static void populate_array_array_end(void *_state);
380 : static void populate_array_element_start(void *_state, bool isnull);
381 : static void populate_array_element_end(void *_state, bool isnull);
382 : static void populate_array_scalar(void *_state, char *token, JsonTokenType tokentype);
383 :
384 : /* semantic action functions for get_json_object_as_hash */
385 : static void hash_object_field_start(void *state, char *fname, bool isnull);
386 : static void hash_object_field_end(void *state, char *fname, bool isnull);
387 : static void hash_array_start(void *state);
388 : static void hash_scalar(void *state, char *token, JsonTokenType tokentype);
389 :
390 : /* semantic action functions for populate_recordset */
391 : static void populate_recordset_object_field_start(void *state, char *fname, bool isnull);
392 : static void populate_recordset_object_field_end(void *state, char *fname, bool isnull);
393 : static void populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype);
394 : static void populate_recordset_object_start(void *state);
395 : static void populate_recordset_object_end(void *state);
396 : static void populate_recordset_array_start(void *state);
397 : static void populate_recordset_array_element_start(void *state, bool isnull);
398 :
399 : /* semantic action functions for json_strip_nulls */
400 : static void sn_object_start(void *state);
401 : static void sn_object_end(void *state);
402 : static void sn_array_start(void *state);
403 : static void sn_array_end(void *state);
404 : static void sn_object_field_start(void *state, char *fname, bool isnull);
405 : static void sn_array_element_start(void *state, bool isnull);
406 : static void sn_scalar(void *state, char *token, JsonTokenType tokentype);
407 :
408 : /* worker functions for populate_record, to_record, populate_recordset and to_recordset */
409 : static Datum populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
410 : bool have_record_arg);
411 : static Datum populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
412 : bool have_record_arg);
413 :
414 : /* helper functions for populate_record[set] */
415 : static HeapTupleHeader populate_record(TupleDesc tupdesc, RecordIOData **record_p,
416 : HeapTupleHeader defaultval, MemoryContext mcxt,
417 : JsObject *obj);
418 : static Datum populate_record_field(ColumnIOData *col, Oid typid, int32 typmod,
419 : const char *colname, MemoryContext mcxt,
420 : Datum defaultval, JsValue *jsv, bool *isnull);
421 : static void JsValueToJsObject(JsValue *jsv, JsObject *jso);
422 : static Datum populate_composite(CompositeIOData *io, Oid typid, int32 typmod,
423 : const char *colname, MemoryContext mcxt,
424 : HeapTupleHeader defaultval, JsValue *jsv);
425 : static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv);
426 : static void prepare_column_cache(ColumnIOData *column, Oid typid, int32 typmod,
427 : MemoryContext mcxt, bool json);
428 : static Datum populate_record_field(ColumnIOData *col, Oid typid, int32 typmod,
429 : const char *colname, MemoryContext mcxt, Datum defaultval,
430 : JsValue *jsv, bool *isnull);
431 : static RecordIOData *allocate_record_info(MemoryContext mcxt, int ncolumns);
432 : static bool JsObjectGetField(JsObject *obj, char *field, JsValue *jsv);
433 : static void populate_recordset_record(PopulateRecordsetState *state, JsObject *obj);
434 : static void populate_array_json(PopulateArrayContext *ctx, char *json, int len);
435 : static void populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv,
436 : int ndim);
437 : static void populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim);
438 : static void populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims);
439 : static void populate_array_check_dimension(PopulateArrayContext *ctx, int ndim);
440 : static void populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv);
441 : static Datum populate_array(ArrayIOData *aio, const char *colname,
442 : MemoryContext mcxt, JsValue *jsv);
443 : static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
444 : MemoryContext mcxt, JsValue *jsv, bool isnull);
445 :
446 : /* Worker that takes care of common setup for us */
447 : static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
448 : uint32 flags,
449 : char *key,
450 : uint32 keylen);
451 :
452 : /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
453 : static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
454 : JsonbParseState **state);
455 : static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
456 : bool *path_nulls, int path_len,
457 : JsonbParseState **st, int level, Jsonb *newval,
458 : int op_type);
459 : static void setPathObject(JsonbIterator **it, Datum *path_elems,
460 : bool *path_nulls, int path_len, JsonbParseState **st,
461 : int level,
462 : Jsonb *newval, uint32 npairs, int op_type);
463 : static void setPathArray(JsonbIterator **it, Datum *path_elems,
464 : bool *path_nulls, int path_len, JsonbParseState **st,
465 : int level, Jsonb *newval, uint32 nelems, int op_type);
466 : static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
467 :
468 : /* function supporting iterate_json_string_values */
469 : static void iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
470 :
471 : /* functions supporting transform_json_string_values */
472 : static void transform_string_values_object_start(void *state);
473 : static void transform_string_values_object_end(void *state);
474 : static void transform_string_values_array_start(void *state);
475 : static void transform_string_values_array_end(void *state);
476 : static void transform_string_values_object_field_start(void *state, char *fname, bool isnull);
477 : static void transform_string_values_array_element_start(void *state, bool isnull);
478 : static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
479 :
480 : /*
481 : * SQL function json_object_keys
482 : *
483 : * Returns the set of keys for the object argument.
484 : *
485 : * This SRF operates in value-per-call mode. It processes the
486 : * object during the first call, and the keys are simply stashed
487 : * in an array, whose size is expanded as necessary. This is probably
488 : * safe enough for a list of keys of a single object, since they are
489 : * limited in size to NAMEDATALEN and the number of keys is unlikely to
490 : * be so huge that it has major memory implications.
491 : */
492 : Datum
493 9 : jsonb_object_keys(PG_FUNCTION_ARGS)
494 : {
495 : FuncCallContext *funcctx;
496 : OkeysState *state;
497 : int i;
498 :
499 9 : if (SRF_IS_FIRSTCALL())
500 : {
501 : MemoryContext oldcontext;
502 3 : Jsonb *jb = PG_GETARG_JSONB(0);
503 3 : bool skipNested = false;
504 : JsonbIterator *it;
505 : JsonbValue v;
506 : JsonbIteratorToken r;
507 :
508 3 : if (JB_ROOT_IS_SCALAR(jb))
509 1 : ereport(ERROR,
510 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
511 : errmsg("cannot call %s on a scalar",
512 : "jsonb_object_keys")));
513 2 : else if (JB_ROOT_IS_ARRAY(jb))
514 1 : ereport(ERROR,
515 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
516 : errmsg("cannot call %s on an array",
517 : "jsonb_object_keys")));
518 :
519 1 : funcctx = SRF_FIRSTCALL_INIT();
520 1 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
521 :
522 1 : state = palloc(sizeof(OkeysState));
523 :
524 1 : state->result_size = JB_ROOT_COUNT(jb);
525 1 : state->result_count = 0;
526 1 : state->sent_count = 0;
527 1 : state->result = palloc(state->result_size * sizeof(char *));
528 :
529 1 : it = JsonbIteratorInit(&jb->root);
530 :
531 16 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
532 : {
533 14 : skipNested = true;
534 :
535 14 : if (r == WJB_KEY)
536 : {
537 : char *cstr;
538 :
539 6 : cstr = palloc(v.val.string.len + 1 * sizeof(char));
540 6 : memcpy(cstr, v.val.string.val, v.val.string.len);
541 6 : cstr[v.val.string.len] = '\0';
542 6 : state->result[state->result_count++] = cstr;
543 : }
544 : }
545 :
546 1 : MemoryContextSwitchTo(oldcontext);
547 1 : funcctx->user_fctx = (void *) state;
548 : }
549 :
550 7 : funcctx = SRF_PERCALL_SETUP();
551 7 : state = (OkeysState *) funcctx->user_fctx;
552 :
553 7 : if (state->sent_count < state->result_count)
554 : {
555 6 : char *nxt = state->result[state->sent_count++];
556 :
557 6 : SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
558 : }
559 :
560 : /* cleanup to reduce or eliminate memory leaks */
561 7 : for (i = 0; i < state->result_count; i++)
562 6 : pfree(state->result[i]);
563 1 : pfree(state->result);
564 1 : pfree(state);
565 :
566 1 : SRF_RETURN_DONE(funcctx);
567 : }
568 :
569 :
570 : Datum
571 310 : json_object_keys(PG_FUNCTION_ARGS)
572 : {
573 : FuncCallContext *funcctx;
574 : OkeysState *state;
575 : int i;
576 :
577 310 : if (SRF_IS_FIRSTCALL())
578 : {
579 4 : text *json = PG_GETARG_TEXT_PP(0);
580 4 : JsonLexContext *lex = makeJsonLexContext(json, true);
581 : JsonSemAction *sem;
582 : MemoryContext oldcontext;
583 :
584 4 : funcctx = SRF_FIRSTCALL_INIT();
585 4 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
586 :
587 4 : state = palloc(sizeof(OkeysState));
588 4 : sem = palloc0(sizeof(JsonSemAction));
589 :
590 4 : state->lex = lex;
591 4 : state->result_size = 256;
592 4 : state->result_count = 0;
593 4 : state->sent_count = 0;
594 4 : state->result = palloc(256 * sizeof(char *));
595 :
596 4 : sem->semstate = (void *) state;
597 4 : sem->array_start = okeys_array_start;
598 4 : sem->scalar = okeys_scalar;
599 4 : sem->object_field_start = okeys_object_field_start;
600 : /* remainder are all NULL, courtesy of palloc0 above */
601 :
602 4 : pg_parse_json(lex, sem);
603 : /* keys are now in state->result */
604 :
605 2 : pfree(lex->strval->data);
606 2 : pfree(lex->strval);
607 2 : pfree(lex);
608 2 : pfree(sem);
609 :
610 2 : MemoryContextSwitchTo(oldcontext);
611 2 : funcctx->user_fctx = (void *) state;
612 : }
613 :
614 308 : funcctx = SRF_PERCALL_SETUP();
615 308 : state = (OkeysState *) funcctx->user_fctx;
616 :
617 308 : if (state->sent_count < state->result_count)
618 : {
619 306 : char *nxt = state->result[state->sent_count++];
620 :
621 306 : SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
622 : }
623 :
624 : /* cleanup to reduce or eliminate memory leaks */
625 308 : for (i = 0; i < state->result_count; i++)
626 306 : pfree(state->result[i]);
627 2 : pfree(state->result);
628 2 : pfree(state);
629 :
630 2 : SRF_RETURN_DONE(funcctx);
631 : }
632 :
633 : static void
634 307 : okeys_object_field_start(void *state, char *fname, bool isnull)
635 : {
636 307 : OkeysState *_state = (OkeysState *) state;
637 :
638 : /* only collecting keys for the top level object */
639 307 : if (_state->lex->lex_level != 1)
640 308 : return;
641 :
642 : /* enlarge result array if necessary */
643 306 : if (_state->result_count >= _state->result_size)
644 : {
645 1 : _state->result_size *= 2;
646 1 : _state->result = (char **)
647 1 : repalloc(_state->result, sizeof(char *) * _state->result_size);
648 : }
649 :
650 : /* save a copy of the field name */
651 306 : _state->result[_state->result_count++] = pstrdup(fname);
652 : }
653 :
654 : static void
655 2 : okeys_array_start(void *state)
656 : {
657 2 : OkeysState *_state = (OkeysState *) state;
658 :
659 : /* top level must be a json object */
660 2 : if (_state->lex->lex_level == 0)
661 1 : ereport(ERROR,
662 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
663 : errmsg("cannot call %s on an array",
664 : "json_object_keys")));
665 1 : }
666 :
667 : static void
668 309 : okeys_scalar(void *state, char *token, JsonTokenType tokentype)
669 : {
670 309 : OkeysState *_state = (OkeysState *) state;
671 :
672 : /* top level must be a json object */
673 309 : if (_state->lex->lex_level == 0)
674 1 : ereport(ERROR,
675 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
676 : errmsg("cannot call %s on a scalar",
677 : "json_object_keys")));
678 308 : }
679 :
680 : /*
681 : * json and jsonb getter functions
682 : * these implement the -> ->> #> and #>> operators
683 : * and the json{b?}_extract_path*(json, text, ...) functions
684 : */
685 :
686 :
687 : Datum
688 15 : json_object_field(PG_FUNCTION_ARGS)
689 : {
690 15 : text *json = PG_GETARG_TEXT_PP(0);
691 15 : text *fname = PG_GETARG_TEXT_PP(1);
692 15 : char *fnamestr = text_to_cstring(fname);
693 : text *result;
694 :
695 15 : result = get_worker(json, &fnamestr, NULL, 1, false);
696 :
697 11 : if (result != NULL)
698 4 : PG_RETURN_TEXT_P(result);
699 : else
700 7 : PG_RETURN_NULL();
701 : }
702 :
703 : Datum
704 4075 : jsonb_object_field(PG_FUNCTION_ARGS)
705 : {
706 4075 : Jsonb *jb = PG_GETARG_JSONB(0);
707 4075 : text *key = PG_GETARG_TEXT_PP(1);
708 : JsonbValue *v;
709 :
710 4075 : if (!JB_ROOT_IS_OBJECT(jb))
711 4 : PG_RETURN_NULL();
712 :
713 12213 : v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
714 4071 : VARDATA_ANY(key),
715 8142 : VARSIZE_ANY_EXHDR(key));
716 :
717 4071 : if (v != NULL)
718 39 : PG_RETURN_JSONB(JsonbValueToJsonb(v));
719 :
720 4032 : PG_RETURN_NULL();
721 : }
722 :
723 : Datum
724 15 : json_object_field_text(PG_FUNCTION_ARGS)
725 : {
726 15 : text *json = PG_GETARG_TEXT_PP(0);
727 15 : text *fname = PG_GETARG_TEXT_PP(1);
728 15 : char *fnamestr = text_to_cstring(fname);
729 : text *result;
730 :
731 15 : result = get_worker(json, &fnamestr, NULL, 1, true);
732 :
733 14 : if (result != NULL)
734 8 : PG_RETURN_TEXT_P(result);
735 : else
736 6 : PG_RETURN_NULL();
737 : }
738 :
739 : Datum
740 17 : jsonb_object_field_text(PG_FUNCTION_ARGS)
741 : {
742 17 : Jsonb *jb = PG_GETARG_JSONB(0);
743 17 : text *key = PG_GETARG_TEXT_PP(1);
744 : JsonbValue *v;
745 :
746 17 : if (!JB_ROOT_IS_OBJECT(jb))
747 4 : PG_RETURN_NULL();
748 :
749 39 : v = findJsonbValueFromContainerLen(&jb->root, JB_FOBJECT,
750 13 : VARDATA_ANY(key),
751 26 : VARSIZE_ANY_EXHDR(key));
752 :
753 13 : if (v != NULL)
754 : {
755 11 : text *result = NULL;
756 :
757 11 : switch (v->type)
758 : {
759 : case jbvNull:
760 3 : break;
761 : case jbvBool:
762 0 : result = cstring_to_text(v->val.boolean ? "true" : "false");
763 0 : break;
764 : case jbvString:
765 5 : result = cstring_to_text_with_len(v->val.string.val, v->val.string.len);
766 5 : break;
767 : case jbvNumeric:
768 1 : result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
769 : PointerGetDatum(v->val.numeric))));
770 1 : break;
771 : case jbvBinary:
772 : {
773 2 : StringInfo jtext = makeStringInfo();
774 :
775 2 : (void) JsonbToCString(jtext, v->val.binary.data, -1);
776 2 : result = cstring_to_text_with_len(jtext->data, jtext->len);
777 : }
778 2 : break;
779 : default:
780 0 : elog(ERROR, "unrecognized jsonb type: %d", (int) v->type);
781 : }
782 :
783 11 : if (result)
784 8 : PG_RETURN_TEXT_P(result);
785 : }
786 :
787 5 : PG_RETURN_NULL();
788 : }
789 :
790 : Datum
791 10 : json_array_element(PG_FUNCTION_ARGS)
792 : {
793 10 : text *json = PG_GETARG_TEXT_PP(0);
794 10 : int element = PG_GETARG_INT32(1);
795 : text *result;
796 :
797 10 : result = get_worker(json, NULL, &element, 1, false);
798 :
799 10 : if (result != NULL)
800 4 : PG_RETURN_TEXT_P(result);
801 : else
802 6 : PG_RETURN_NULL();
803 : }
804 :
805 : Datum
806 21 : jsonb_array_element(PG_FUNCTION_ARGS)
807 : {
808 21 : Jsonb *jb = PG_GETARG_JSONB(0);
809 21 : int element = PG_GETARG_INT32(1);
810 : JsonbValue *v;
811 :
812 21 : if (!JB_ROOT_IS_ARRAY(jb))
813 3 : PG_RETURN_NULL();
814 :
815 : /* Handle negative subscript */
816 18 : if (element < 0)
817 : {
818 3 : uint32 nelements = JB_ROOT_COUNT(jb);
819 :
820 3 : if (-element > nelements)
821 1 : PG_RETURN_NULL();
822 : else
823 2 : element += nelements;
824 : }
825 :
826 17 : v = getIthJsonbValueFromContainer(&jb->root, element);
827 17 : if (v != NULL)
828 12 : PG_RETURN_JSONB(JsonbValueToJsonb(v));
829 :
830 5 : PG_RETURN_NULL();
831 : }
832 :
833 : Datum
834 8 : json_array_element_text(PG_FUNCTION_ARGS)
835 : {
836 8 : text *json = PG_GETARG_TEXT_PP(0);
837 8 : int element = PG_GETARG_INT32(1);
838 : text *result;
839 :
840 8 : result = get_worker(json, NULL, &element, 1, true);
841 :
842 8 : if (result != NULL)
843 4 : PG_RETURN_TEXT_P(result);
844 : else
845 4 : PG_RETURN_NULL();
846 : }
847 :
848 : Datum
849 10 : jsonb_array_element_text(PG_FUNCTION_ARGS)
850 : {
851 10 : Jsonb *jb = PG_GETARG_JSONB(0);
852 10 : int element = PG_GETARG_INT32(1);
853 : JsonbValue *v;
854 :
855 10 : if (!JB_ROOT_IS_ARRAY(jb))
856 2 : PG_RETURN_NULL();
857 :
858 : /* Handle negative subscript */
859 8 : if (element < 0)
860 : {
861 0 : uint32 nelements = JB_ROOT_COUNT(jb);
862 :
863 0 : if (-element > nelements)
864 0 : PG_RETURN_NULL();
865 : else
866 0 : element += nelements;
867 : }
868 :
869 8 : v = getIthJsonbValueFromContainer(&jb->root, element);
870 8 : if (v != NULL)
871 : {
872 5 : text *result = NULL;
873 :
874 5 : switch (v->type)
875 : {
876 : case jbvNull:
877 1 : break;
878 : case jbvBool:
879 0 : result = cstring_to_text(v->val.boolean ? "true" : "false");
880 0 : break;
881 : case jbvString:
882 1 : result = cstring_to_text_with_len(v->val.string.val, v->val.string.len);
883 1 : break;
884 : case jbvNumeric:
885 0 : result = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out,
886 : PointerGetDatum(v->val.numeric))));
887 0 : break;
888 : case jbvBinary:
889 : {
890 3 : StringInfo jtext = makeStringInfo();
891 :
892 3 : (void) JsonbToCString(jtext, v->val.binary.data, -1);
893 3 : result = cstring_to_text_with_len(jtext->data, jtext->len);
894 : }
895 3 : break;
896 : default:
897 0 : elog(ERROR, "unrecognized jsonb type: %d", (int) v->type);
898 : }
899 :
900 5 : if (result)
901 4 : PG_RETURN_TEXT_P(result);
902 : }
903 :
904 4 : PG_RETURN_NULL();
905 : }
906 :
907 : Datum
908 30 : json_extract_path(PG_FUNCTION_ARGS)
909 : {
910 30 : return get_path_all(fcinfo, false);
911 : }
912 :
913 : Datum
914 30 : json_extract_path_text(PG_FUNCTION_ARGS)
915 : {
916 30 : return get_path_all(fcinfo, true);
917 : }
918 :
919 : /*
920 : * common routine for extract_path functions
921 : */
922 : static Datum
923 60 : get_path_all(FunctionCallInfo fcinfo, bool as_text)
924 : {
925 60 : text *json = PG_GETARG_TEXT_PP(0);
926 60 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
927 : text *result;
928 : Datum *pathtext;
929 : bool *pathnulls;
930 : int npath;
931 : char **tpath;
932 : int *ipath;
933 : int i;
934 :
935 : /*
936 : * If the array contains any null elements, return NULL, on the grounds
937 : * that you'd have gotten NULL if any RHS value were NULL in a nested
938 : * series of applications of the -> operator. (Note: because we also
939 : * return NULL for error cases such as no-such-field, this is true
940 : * regardless of the contents of the rest of the array.)
941 : */
942 60 : if (array_contains_nulls(path))
943 2 : PG_RETURN_NULL();
944 :
945 58 : deconstruct_array(path, TEXTOID, -1, false, 'i',
946 : &pathtext, &pathnulls, &npath);
947 :
948 58 : tpath = palloc(npath * sizeof(char *));
949 58 : ipath = palloc(npath * sizeof(int));
950 :
951 154 : for (i = 0; i < npath; i++)
952 : {
953 96 : Assert(!pathnulls[i]);
954 96 : tpath[i] = TextDatumGetCString(pathtext[i]);
955 :
956 : /*
957 : * we have no idea at this stage what structure the document is so
958 : * just convert anything in the path that we can to an integer and set
959 : * all the other integers to INT_MIN which will never match.
960 : */
961 96 : if (*tpath[i] != '\0')
962 : {
963 : long ind;
964 : char *endptr;
965 :
966 94 : errno = 0;
967 94 : ind = strtol(tpath[i], &endptr, 10);
968 94 : if (*endptr == '\0' && errno == 0 && ind <= INT_MAX && ind >= INT_MIN)
969 18 : ipath[i] = (int) ind;
970 : else
971 76 : ipath[i] = INT_MIN;
972 : }
973 : else
974 2 : ipath[i] = INT_MIN;
975 : }
976 :
977 58 : result = get_worker(json, tpath, ipath, npath, as_text);
978 :
979 58 : if (result != NULL)
980 38 : PG_RETURN_TEXT_P(result);
981 : else
982 20 : PG_RETURN_NULL();
983 : }
984 :
985 : /*
986 : * get_worker
987 : *
988 : * common worker for all the json getter functions
989 : *
990 : * json: JSON object (in text form)
991 : * tpath[]: field name(s) to extract
992 : * ipath[]: array index(es) (zero-based) to extract, accepts negatives
993 : * npath: length of tpath[] and/or ipath[]
994 : * normalize_results: true to de-escape string and null scalars
995 : *
996 : * tpath can be NULL, or any one tpath[] entry can be NULL, if an object
997 : * field is not to be matched at that nesting level. Similarly, ipath can
998 : * be NULL, or any one ipath[] entry can be INT_MIN if an array element is
999 : * not to be matched at that nesting level (a json datum should never be
1000 : * large enough to have -INT_MIN elements due to MaxAllocSize restriction).
1001 : */
1002 : static text *
1003 106 : get_worker(text *json,
1004 : char **tpath,
1005 : int *ipath,
1006 : int npath,
1007 : bool normalize_results)
1008 : {
1009 106 : JsonLexContext *lex = makeJsonLexContext(json, true);
1010 106 : JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
1011 106 : GetState *state = palloc0(sizeof(GetState));
1012 :
1013 106 : Assert(npath >= 0);
1014 :
1015 106 : state->lex = lex;
1016 : /* is it "_as_text" variant? */
1017 106 : state->normalize_results = normalize_results;
1018 106 : state->npath = npath;
1019 106 : state->path_names = tpath;
1020 106 : state->path_indexes = ipath;
1021 106 : state->pathok = palloc0(sizeof(bool) * npath);
1022 106 : state->array_cur_index = palloc(sizeof(int) * npath);
1023 :
1024 106 : if (npath > 0)
1025 96 : state->pathok[0] = true;
1026 :
1027 106 : sem->semstate = (void *) state;
1028 :
1029 : /*
1030 : * Not all variants need all the semantic routines. Only set the ones that
1031 : * are actually needed for maximum efficiency.
1032 : */
1033 106 : sem->scalar = get_scalar;
1034 106 : if (npath == 0)
1035 : {
1036 10 : sem->object_start = get_object_start;
1037 10 : sem->object_end = get_object_end;
1038 10 : sem->array_start = get_array_start;
1039 10 : sem->array_end = get_array_end;
1040 : }
1041 106 : if (tpath != NULL)
1042 : {
1043 88 : sem->object_field_start = get_object_field_start;
1044 88 : sem->object_field_end = get_object_field_end;
1045 : }
1046 106 : if (ipath != NULL)
1047 : {
1048 76 : sem->array_start = get_array_start;
1049 76 : sem->array_element_start = get_array_element_start;
1050 76 : sem->array_element_end = get_array_element_end;
1051 : }
1052 :
1053 106 : pg_parse_json(lex, sem);
1054 :
1055 101 : return state->tresult;
1056 : }
1057 :
1058 : static void
1059 6 : get_object_start(void *state)
1060 : {
1061 6 : GetState *_state = (GetState *) state;
1062 6 : int lex_level = _state->lex->lex_level;
1063 :
1064 6 : if (lex_level == 0 && _state->npath == 0)
1065 : {
1066 : /*
1067 : * Special case: we should match the entire object. We only need this
1068 : * at outermost level because at nested levels the match will have
1069 : * been started by the outer field or array element callback.
1070 : */
1071 2 : _state->result_start = _state->lex->token_start;
1072 : }
1073 6 : }
1074 :
1075 : static void
1076 6 : get_object_end(void *state)
1077 : {
1078 6 : GetState *_state = (GetState *) state;
1079 6 : int lex_level = _state->lex->lex_level;
1080 :
1081 6 : if (lex_level == 0 && _state->npath == 0)
1082 : {
1083 : /* Special case: return the entire object */
1084 2 : char *start = _state->result_start;
1085 2 : int len = _state->lex->prev_token_terminator - start;
1086 :
1087 2 : _state->tresult = cstring_to_text_with_len(start, len);
1088 : }
1089 6 : }
1090 :
1091 : static void
1092 236 : get_object_field_start(void *state, char *fname, bool isnull)
1093 : {
1094 236 : GetState *_state = (GetState *) state;
1095 236 : bool get_next = false;
1096 236 : int lex_level = _state->lex->lex_level;
1097 :
1098 425 : if (lex_level <= _state->npath &&
1099 338 : _state->pathok[lex_level - 1] &&
1100 298 : _state->path_names != NULL &&
1101 298 : _state->path_names[lex_level - 1] != NULL &&
1102 149 : strcmp(fname, _state->path_names[lex_level - 1]) == 0)
1103 : {
1104 72 : if (lex_level < _state->npath)
1105 : {
1106 : /* if not at end of path just mark path ok */
1107 36 : _state->pathok[lex_level] = true;
1108 : }
1109 : else
1110 : {
1111 : /* end of path, so we want this value */
1112 36 : get_next = true;
1113 : }
1114 : }
1115 :
1116 236 : if (get_next)
1117 : {
1118 : /* this object overrides any previous matching object */
1119 36 : _state->tresult = NULL;
1120 36 : _state->result_start = NULL;
1121 :
1122 57 : if (_state->normalize_results &&
1123 21 : _state->lex->token_type == JSON_TOKEN_STRING)
1124 : {
1125 : /* for as_text variants, tell get_scalar to set it for us */
1126 10 : _state->next_scalar = true;
1127 : }
1128 : else
1129 : {
1130 : /* for non-as_text variants, just note the json starting point */
1131 26 : _state->result_start = _state->lex->token_start;
1132 : }
1133 : }
1134 236 : }
1135 :
1136 : static void
1137 236 : get_object_field_end(void *state, char *fname, bool isnull)
1138 : {
1139 236 : GetState *_state = (GetState *) state;
1140 236 : bool get_last = false;
1141 236 : int lex_level = _state->lex->lex_level;
1142 :
1143 : /* same tests as in get_object_field_start */
1144 425 : if (lex_level <= _state->npath &&
1145 338 : _state->pathok[lex_level - 1] &&
1146 298 : _state->path_names != NULL &&
1147 298 : _state->path_names[lex_level - 1] != NULL &&
1148 149 : strcmp(fname, _state->path_names[lex_level - 1]) == 0)
1149 : {
1150 72 : if (lex_level < _state->npath)
1151 : {
1152 : /* done with this field so reset pathok */
1153 36 : _state->pathok[lex_level] = false;
1154 : }
1155 : else
1156 : {
1157 : /* end of path, so we want this value */
1158 36 : get_last = true;
1159 : }
1160 : }
1161 :
1162 : /* for as_text scalar case, our work is already done */
1163 236 : if (get_last && _state->result_start != NULL)
1164 : {
1165 : /*
1166 : * make a text object from the string from the prevously noted json
1167 : * start up to the end of the previous token (the lexer is by now
1168 : * ahead of us on whatever came after what we're interested in).
1169 : */
1170 26 : if (isnull && _state->normalize_results)
1171 4 : _state->tresult = (text *) NULL;
1172 : else
1173 : {
1174 22 : char *start = _state->result_start;
1175 22 : int len = _state->lex->prev_token_terminator - start;
1176 :
1177 22 : _state->tresult = cstring_to_text_with_len(start, len);
1178 : }
1179 :
1180 : /* this should be unnecessary but let's do it for cleanliness: */
1181 26 : _state->result_start = NULL;
1182 : }
1183 236 : }
1184 :
1185 : static void
1186 44 : get_array_start(void *state)
1187 : {
1188 44 : GetState *_state = (GetState *) state;
1189 44 : int lex_level = _state->lex->lex_level;
1190 :
1191 44 : if (lex_level < _state->npath)
1192 : {
1193 : /* Initialize counting of elements in this array */
1194 31 : _state->array_cur_index[lex_level] = -1;
1195 :
1196 : /* INT_MIN value is reserved to represent invalid subscript */
1197 36 : if (_state->path_indexes[lex_level] < 0 &&
1198 5 : _state->path_indexes[lex_level] != INT_MIN)
1199 : {
1200 : /* Negative subscript -- convert to positive-wise subscript */
1201 1 : int nelements = json_count_array_elements(_state->lex);
1202 :
1203 1 : if (-_state->path_indexes[lex_level] <= nelements)
1204 1 : _state->path_indexes[lex_level] += nelements;
1205 : }
1206 : }
1207 13 : else if (lex_level == 0 && _state->npath == 0)
1208 : {
1209 : /*
1210 : * Special case: we should match the entire array. We only need this
1211 : * at the outermost level because at nested levels the match will have
1212 : * been started by the outer field or array element callback.
1213 : */
1214 2 : _state->result_start = _state->lex->token_start;
1215 : }
1216 44 : }
1217 :
1218 : static void
1219 2 : get_array_end(void *state)
1220 : {
1221 2 : GetState *_state = (GetState *) state;
1222 2 : int lex_level = _state->lex->lex_level;
1223 :
1224 2 : if (lex_level == 0 && _state->npath == 0)
1225 : {
1226 : /* Special case: return the entire array */
1227 2 : char *start = _state->result_start;
1228 2 : int len = _state->lex->prev_token_terminator - start;
1229 :
1230 2 : _state->tresult = cstring_to_text_with_len(start, len);
1231 : }
1232 2 : }
1233 :
1234 : static void
1235 144 : get_array_element_start(void *state, bool isnull)
1236 : {
1237 144 : GetState *_state = (GetState *) state;
1238 144 : bool get_next = false;
1239 144 : int lex_level = _state->lex->lex_level;
1240 :
1241 : /* Update array element counter */
1242 144 : if (lex_level <= _state->npath)
1243 108 : _state->array_cur_index[lex_level - 1]++;
1244 :
1245 252 : if (lex_level <= _state->npath &&
1246 216 : _state->pathok[lex_level - 1] &&
1247 216 : _state->path_indexes != NULL &&
1248 108 : _state->array_cur_index[lex_level - 1] == _state->path_indexes[lex_level - 1])
1249 : {
1250 25 : if (lex_level < _state->npath)
1251 : {
1252 : /* if not at end of path just mark path ok */
1253 6 : _state->pathok[lex_level] = true;
1254 : }
1255 : else
1256 : {
1257 : /* end of path, so we want this value */
1258 19 : get_next = true;
1259 : }
1260 : }
1261 :
1262 : /* same logic as for objects */
1263 144 : if (get_next)
1264 : {
1265 19 : _state->tresult = NULL;
1266 19 : _state->result_start = NULL;
1267 :
1268 29 : if (_state->normalize_results &&
1269 10 : _state->lex->token_type == JSON_TOKEN_STRING)
1270 : {
1271 3 : _state->next_scalar = true;
1272 : }
1273 : else
1274 : {
1275 16 : _state->result_start = _state->lex->token_start;
1276 : }
1277 : }
1278 144 : }
1279 :
1280 : static void
1281 144 : get_array_element_end(void *state, bool isnull)
1282 : {
1283 144 : GetState *_state = (GetState *) state;
1284 144 : bool get_last = false;
1285 144 : int lex_level = _state->lex->lex_level;
1286 :
1287 : /* same tests as in get_array_element_start */
1288 252 : if (lex_level <= _state->npath &&
1289 216 : _state->pathok[lex_level - 1] &&
1290 216 : _state->path_indexes != NULL &&
1291 108 : _state->array_cur_index[lex_level - 1] == _state->path_indexes[lex_level - 1])
1292 : {
1293 25 : if (lex_level < _state->npath)
1294 : {
1295 : /* done with this element so reset pathok */
1296 6 : _state->pathok[lex_level] = false;
1297 : }
1298 : else
1299 : {
1300 : /* end of path, so we want this value */
1301 19 : get_last = true;
1302 : }
1303 : }
1304 :
1305 : /* same logic as for objects */
1306 144 : if (get_last && _state->result_start != NULL)
1307 : {
1308 16 : if (isnull && _state->normalize_results)
1309 2 : _state->tresult = (text *) NULL;
1310 : else
1311 : {
1312 14 : char *start = _state->result_start;
1313 14 : int len = _state->lex->prev_token_terminator - start;
1314 :
1315 14 : _state->tresult = cstring_to_text_with_len(start, len);
1316 : }
1317 :
1318 16 : _state->result_start = NULL;
1319 : }
1320 144 : }
1321 :
1322 : static void
1323 317 : get_scalar(void *state, char *token, JsonTokenType tokentype)
1324 : {
1325 317 : GetState *_state = (GetState *) state;
1326 317 : int lex_level = _state->lex->lex_level;
1327 :
1328 : /* Check for whole-object match */
1329 317 : if (lex_level == 0 && _state->npath == 0)
1330 : {
1331 6 : if (_state->normalize_results && tokentype == JSON_TOKEN_STRING)
1332 : {
1333 : /* we want the de-escaped string */
1334 1 : _state->next_scalar = true;
1335 : }
1336 5 : else if (_state->normalize_results && tokentype == JSON_TOKEN_NULL)
1337 : {
1338 1 : _state->tresult = (text *) NULL;
1339 : }
1340 : else
1341 : {
1342 : /*
1343 : * This is a bit hokey: we will suppress whitespace after the
1344 : * scalar token, but not whitespace before it. Probably not worth
1345 : * doing our own space-skipping to avoid that.
1346 : */
1347 4 : char *start = _state->lex->input;
1348 4 : int len = _state->lex->prev_token_terminator - start;
1349 :
1350 4 : _state->tresult = cstring_to_text_with_len(start, len);
1351 : }
1352 : }
1353 :
1354 317 : if (_state->next_scalar)
1355 : {
1356 : /* a de-escaped text value is wanted, so supply it */
1357 14 : _state->tresult = cstring_to_text(token);
1358 : /* make sure the next call to get_scalar doesn't overwrite it */
1359 14 : _state->next_scalar = false;
1360 : }
1361 317 : }
1362 :
1363 : Datum
1364 44 : jsonb_extract_path(PG_FUNCTION_ARGS)
1365 : {
1366 44 : return get_jsonb_path_all(fcinfo, false);
1367 : }
1368 :
1369 : Datum
1370 30 : jsonb_extract_path_text(PG_FUNCTION_ARGS)
1371 : {
1372 30 : return get_jsonb_path_all(fcinfo, true);
1373 : }
1374 :
1375 : static Datum
1376 74 : get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
1377 : {
1378 74 : Jsonb *jb = PG_GETARG_JSONB(0);
1379 74 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
1380 : Jsonb *res;
1381 : Datum *pathtext;
1382 : bool *pathnulls;
1383 : int npath;
1384 : int i;
1385 74 : bool have_object = false,
1386 74 : have_array = false;
1387 74 : JsonbValue *jbvp = NULL;
1388 : JsonbValue tv;
1389 : JsonbContainer *container;
1390 :
1391 : /*
1392 : * If the array contains any null elements, return NULL, on the grounds
1393 : * that you'd have gotten NULL if any RHS value were NULL in a nested
1394 : * series of applications of the -> operator. (Note: because we also
1395 : * return NULL for error cases such as no-such-field, this is true
1396 : * regardless of the contents of the rest of the array.)
1397 : */
1398 74 : if (array_contains_nulls(path))
1399 2 : PG_RETURN_NULL();
1400 :
1401 72 : deconstruct_array(path, TEXTOID, -1, false, 'i',
1402 : &pathtext, &pathnulls, &npath);
1403 :
1404 : /* Identify whether we have object, array, or scalar at top-level */
1405 72 : container = &jb->root;
1406 :
1407 72 : if (JB_ROOT_IS_OBJECT(jb))
1408 48 : have_object = true;
1409 24 : else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
1410 12 : have_array = true;
1411 : else
1412 : {
1413 12 : Assert(JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb));
1414 : /* Extract the scalar value, if it is what we'll return */
1415 12 : if (npath <= 0)
1416 6 : jbvp = getIthJsonbValueFromContainer(container, 0);
1417 : }
1418 :
1419 : /*
1420 : * If the array is empty, return the entire LHS object, on the grounds
1421 : * that we should do zero field or element extractions. For the
1422 : * non-scalar case we can just hand back the object without much work. For
1423 : * the scalar case, fall through and deal with the value below the loop.
1424 : * (This inconsistency arises because there's no easy way to generate a
1425 : * JsonbValue directly for root-level containers.)
1426 : */
1427 72 : if (npath <= 0 && jbvp == NULL)
1428 : {
1429 4 : if (as_text)
1430 : {
1431 2 : PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
1432 : container,
1433 : VARSIZE(jb))));
1434 : }
1435 : else
1436 : {
1437 : /* not text mode - just hand back the jsonb */
1438 2 : PG_RETURN_JSONB(jb);
1439 : }
1440 : }
1441 :
1442 118 : for (i = 0; i < npath; i++)
1443 : {
1444 112 : if (have_object)
1445 : {
1446 146 : jbvp = findJsonbValueFromContainerLen(container,
1447 : JB_FOBJECT,
1448 73 : VARDATA(pathtext[i]),
1449 73 : VARSIZE(pathtext[i]) - VARHDRSZ);
1450 : }
1451 39 : else if (have_array)
1452 : {
1453 : long lindex;
1454 : uint32 index;
1455 31 : char *indextext = TextDatumGetCString(pathtext[i]);
1456 : char *endptr;
1457 :
1458 31 : errno = 0;
1459 31 : lindex = strtol(indextext, &endptr, 10);
1460 31 : if (endptr == indextext || *endptr != '\0' || errno != 0 ||
1461 27 : lindex > INT_MAX || lindex < INT_MIN)
1462 9 : PG_RETURN_NULL();
1463 :
1464 27 : if (lindex >= 0)
1465 : {
1466 24 : index = (uint32) lindex;
1467 : }
1468 : else
1469 : {
1470 : /* Handle negative subscript */
1471 : uint32 nelements;
1472 :
1473 : /* Container must be array, but make sure */
1474 3 : if (!JsonContainerIsArray(container))
1475 0 : elog(ERROR, "not a jsonb array");
1476 :
1477 3 : nelements = JsonContainerSize(container);
1478 :
1479 3 : if (-lindex > nelements)
1480 1 : PG_RETURN_NULL();
1481 : else
1482 2 : index = nelements + lindex;
1483 : }
1484 :
1485 26 : jbvp = getIthJsonbValueFromContainer(container, index);
1486 : }
1487 : else
1488 : {
1489 : /* scalar, extraction yields a null */
1490 8 : PG_RETURN_NULL();
1491 : }
1492 :
1493 99 : if (jbvp == NULL)
1494 6 : PG_RETURN_NULL();
1495 93 : else if (i == npath - 1)
1496 43 : break;
1497 :
1498 50 : if (jbvp->type == jbvBinary)
1499 : {
1500 48 : JsonbIterator *it = JsonbIteratorInit((JsonbContainer *) jbvp->val.binary.data);
1501 : JsonbIteratorToken r;
1502 :
1503 48 : r = JsonbIteratorNext(&it, &tv, true);
1504 48 : container = (JsonbContainer *) jbvp->val.binary.data;
1505 48 : have_object = r == WJB_BEGIN_OBJECT;
1506 48 : have_array = r == WJB_BEGIN_ARRAY;
1507 : }
1508 : else
1509 : {
1510 2 : have_object = jbvp->type == jbvObject;
1511 2 : have_array = jbvp->type == jbvArray;
1512 : }
1513 : }
1514 :
1515 49 : if (as_text)
1516 : {
1517 : /* special-case outputs for string and null values */
1518 19 : if (jbvp->type == jbvString)
1519 8 : PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
1520 : jbvp->val.string.len));
1521 11 : if (jbvp->type == jbvNull)
1522 4 : PG_RETURN_NULL();
1523 : }
1524 :
1525 37 : res = JsonbValueToJsonb(jbvp);
1526 :
1527 37 : if (as_text)
1528 : {
1529 7 : PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
1530 : &res->root,
1531 : VARSIZE(res))));
1532 : }
1533 : else
1534 : {
1535 : /* not text mode - just hand back the jsonb */
1536 30 : PG_RETURN_JSONB(res);
1537 : }
1538 : }
1539 :
1540 : /*
1541 : * SQL function json_array_length(json) -> int
1542 : */
1543 : Datum
1544 4 : json_array_length(PG_FUNCTION_ARGS)
1545 : {
1546 4 : text *json = PG_GETARG_TEXT_PP(0);
1547 : AlenState *state;
1548 : JsonLexContext *lex;
1549 : JsonSemAction *sem;
1550 :
1551 4 : lex = makeJsonLexContext(json, false);
1552 4 : state = palloc0(sizeof(AlenState));
1553 4 : sem = palloc0(sizeof(JsonSemAction));
1554 :
1555 : /* palloc0 does this for us */
1556 : #if 0
1557 : state->count = 0;
1558 : #endif
1559 4 : state->lex = lex;
1560 :
1561 4 : sem->semstate = (void *) state;
1562 4 : sem->object_start = alen_object_start;
1563 4 : sem->scalar = alen_scalar;
1564 4 : sem->array_element_start = alen_array_element_start;
1565 :
1566 4 : pg_parse_json(lex, sem);
1567 :
1568 2 : PG_RETURN_INT32(state->count);
1569 : }
1570 :
1571 : Datum
1572 4 : jsonb_array_length(PG_FUNCTION_ARGS)
1573 : {
1574 4 : Jsonb *jb = PG_GETARG_JSONB(0);
1575 :
1576 4 : if (JB_ROOT_IS_SCALAR(jb))
1577 1 : ereport(ERROR,
1578 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1579 : errmsg("cannot get array length of a scalar")));
1580 3 : else if (!JB_ROOT_IS_ARRAY(jb))
1581 1 : ereport(ERROR,
1582 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1583 : errmsg("cannot get array length of a non-array")));
1584 :
1585 2 : PG_RETURN_INT32(JB_ROOT_COUNT(jb));
1586 : }
1587 :
1588 : /*
1589 : * These next two checks ensure that the json is an array (since it can't be
1590 : * a scalar or an object).
1591 : */
1592 :
1593 : static void
1594 2 : alen_object_start(void *state)
1595 : {
1596 2 : AlenState *_state = (AlenState *) state;
1597 :
1598 : /* json structure check */
1599 2 : if (_state->lex->lex_level == 0)
1600 1 : ereport(ERROR,
1601 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1602 : errmsg("cannot get array length of a non-array")));
1603 1 : }
1604 :
1605 : static void
1606 8 : alen_scalar(void *state, char *token, JsonTokenType tokentype)
1607 : {
1608 8 : AlenState *_state = (AlenState *) state;
1609 :
1610 : /* json structure check */
1611 8 : if (_state->lex->lex_level == 0)
1612 1 : ereport(ERROR,
1613 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1614 : errmsg("cannot get array length of a scalar")));
1615 7 : }
1616 :
1617 : static void
1618 7 : alen_array_element_start(void *state, bool isnull)
1619 : {
1620 7 : AlenState *_state = (AlenState *) state;
1621 :
1622 : /* just count up all the level 1 elements */
1623 7 : if (_state->lex->lex_level == 1)
1624 5 : _state->count++;
1625 7 : }
1626 :
1627 : /*
1628 : * SQL function json_each and json_each_text
1629 : *
1630 : * decompose a json object into key value pairs.
1631 : *
1632 : * Unlike json_object_keys() these SRFs operate in materialize mode,
1633 : * stashing results into a Tuplestore object as they go.
1634 : * The construction of tuples is done using a temporary memory context
1635 : * that is cleared out after each tuple is built.
1636 : */
1637 : Datum
1638 2 : json_each(PG_FUNCTION_ARGS)
1639 : {
1640 2 : return each_worker(fcinfo, false);
1641 : }
1642 :
1643 : Datum
1644 2028 : jsonb_each(PG_FUNCTION_ARGS)
1645 : {
1646 2028 : return each_worker_jsonb(fcinfo, "jsonb_each", false);
1647 : }
1648 :
1649 : Datum
1650 2 : json_each_text(PG_FUNCTION_ARGS)
1651 : {
1652 2 : return each_worker(fcinfo, true);
1653 : }
1654 :
1655 : Datum
1656 4 : jsonb_each_text(PG_FUNCTION_ARGS)
1657 : {
1658 4 : return each_worker_jsonb(fcinfo, "jsonb_each_text", true);
1659 : }
1660 :
1661 : static Datum
1662 2032 : each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
1663 : {
1664 2032 : Jsonb *jb = PG_GETARG_JSONB(0);
1665 : ReturnSetInfo *rsi;
1666 : Tuplestorestate *tuple_store;
1667 : TupleDesc tupdesc;
1668 : TupleDesc ret_tdesc;
1669 : MemoryContext old_cxt,
1670 : tmp_cxt;
1671 2032 : bool skipNested = false;
1672 : JsonbIterator *it;
1673 : JsonbValue v;
1674 : JsonbIteratorToken r;
1675 :
1676 2032 : if (!JB_ROOT_IS_OBJECT(jb))
1677 0 : ereport(ERROR,
1678 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1679 : errmsg("cannot call %s on a non-object",
1680 : funcname)));
1681 :
1682 2032 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
1683 :
1684 4064 : if (!rsi || !IsA(rsi, ReturnSetInfo) ||
1685 4064 : (rsi->allowedModes & SFRM_Materialize) == 0 ||
1686 2032 : rsi->expectedDesc == NULL)
1687 0 : ereport(ERROR,
1688 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1689 : errmsg("set-valued function called in context that "
1690 : "cannot accept a set")));
1691 :
1692 2032 : rsi->returnMode = SFRM_Materialize;
1693 :
1694 2032 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
1695 0 : ereport(ERROR,
1696 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1697 : errmsg("function returning record called in context "
1698 : "that cannot accept type record")));
1699 :
1700 2032 : old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
1701 :
1702 2032 : ret_tdesc = CreateTupleDescCopy(tupdesc);
1703 2032 : BlessTupleDesc(ret_tdesc);
1704 2032 : tuple_store =
1705 2032 : tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
1706 : false, work_mem);
1707 :
1708 2032 : MemoryContextSwitchTo(old_cxt);
1709 :
1710 2032 : tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
1711 : "jsonb_each temporary cxt",
1712 : ALLOCSET_DEFAULT_SIZES);
1713 :
1714 2032 : it = JsonbIteratorInit(&jb->root);
1715 :
1716 17747 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
1717 : {
1718 13683 : skipNested = true;
1719 :
1720 13683 : if (r == WJB_KEY)
1721 : {
1722 : text *key;
1723 : HeapTuple tuple;
1724 : Datum values[2];
1725 9619 : bool nulls[2] = {false, false};
1726 :
1727 : /* Use the tmp context so we can clean up after each tuple is done */
1728 9619 : old_cxt = MemoryContextSwitchTo(tmp_cxt);
1729 :
1730 9619 : key = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
1731 :
1732 : /*
1733 : * The next thing the iterator fetches should be the value, no
1734 : * matter what shape it is.
1735 : */
1736 9619 : r = JsonbIteratorNext(&it, &v, skipNested);
1737 :
1738 9619 : values[0] = PointerGetDatum(key);
1739 :
1740 9619 : if (as_text)
1741 : {
1742 19 : if (v.type == jbvNull)
1743 : {
1744 : /* a json null is an sql null in text mode */
1745 4 : nulls[1] = true;
1746 4 : values[1] = (Datum) NULL;
1747 : }
1748 : else
1749 : {
1750 : text *sv;
1751 :
1752 15 : if (v.type == jbvString)
1753 : {
1754 : /* In text mode, scalar strings should be dequoted */
1755 6 : sv = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
1756 : }
1757 : else
1758 : {
1759 : /* Turn anything else into a json string */
1760 9 : StringInfo jtext = makeStringInfo();
1761 9 : Jsonb *jb = JsonbValueToJsonb(&v);
1762 :
1763 9 : (void) JsonbToCString(jtext, &jb->root, 0);
1764 9 : sv = cstring_to_text_with_len(jtext->data, jtext->len);
1765 : }
1766 :
1767 15 : values[1] = PointerGetDatum(sv);
1768 : }
1769 : }
1770 : else
1771 : {
1772 : /* Not in text mode, just return the Jsonb */
1773 9600 : Jsonb *val = JsonbValueToJsonb(&v);
1774 :
1775 9600 : values[1] = PointerGetDatum(val);
1776 : }
1777 :
1778 9619 : tuple = heap_form_tuple(ret_tdesc, values, nulls);
1779 :
1780 9619 : tuplestore_puttuple(tuple_store, tuple);
1781 :
1782 : /* clean up and switch back */
1783 9619 : MemoryContextSwitchTo(old_cxt);
1784 9619 : MemoryContextReset(tmp_cxt);
1785 : }
1786 : }
1787 :
1788 2032 : MemoryContextDelete(tmp_cxt);
1789 :
1790 2032 : rsi->setResult = tuple_store;
1791 2032 : rsi->setDesc = ret_tdesc;
1792 :
1793 2032 : PG_RETURN_NULL();
1794 : }
1795 :
1796 :
1797 : static Datum
1798 4 : each_worker(FunctionCallInfo fcinfo, bool as_text)
1799 : {
1800 4 : text *json = PG_GETARG_TEXT_PP(0);
1801 : JsonLexContext *lex;
1802 : JsonSemAction *sem;
1803 : ReturnSetInfo *rsi;
1804 : MemoryContext old_cxt;
1805 : TupleDesc tupdesc;
1806 : EachState *state;
1807 :
1808 4 : lex = makeJsonLexContext(json, true);
1809 4 : state = palloc0(sizeof(EachState));
1810 4 : sem = palloc0(sizeof(JsonSemAction));
1811 :
1812 4 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
1813 :
1814 8 : if (!rsi || !IsA(rsi, ReturnSetInfo) ||
1815 8 : (rsi->allowedModes & SFRM_Materialize) == 0 ||
1816 4 : rsi->expectedDesc == NULL)
1817 0 : ereport(ERROR,
1818 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1819 : errmsg("set-valued function called in context that "
1820 : "cannot accept a set")));
1821 :
1822 4 : rsi->returnMode = SFRM_Materialize;
1823 :
1824 4 : (void) get_call_result_type(fcinfo, NULL, &tupdesc);
1825 :
1826 : /* make these in a sufficiently long-lived memory context */
1827 4 : old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
1828 :
1829 4 : state->ret_tdesc = CreateTupleDescCopy(tupdesc);
1830 4 : BlessTupleDesc(state->ret_tdesc);
1831 4 : state->tuple_store =
1832 4 : tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
1833 : false, work_mem);
1834 :
1835 4 : MemoryContextSwitchTo(old_cxt);
1836 :
1837 4 : sem->semstate = (void *) state;
1838 4 : sem->array_start = each_array_start;
1839 4 : sem->scalar = each_scalar;
1840 4 : sem->object_field_start = each_object_field_start;
1841 4 : sem->object_field_end = each_object_field_end;
1842 :
1843 4 : state->normalize_results = as_text;
1844 4 : state->next_scalar = false;
1845 4 : state->lex = lex;
1846 4 : state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
1847 : "json_each temporary cxt",
1848 : ALLOCSET_DEFAULT_SIZES);
1849 :
1850 4 : pg_parse_json(lex, sem);
1851 :
1852 4 : MemoryContextDelete(state->tmp_cxt);
1853 :
1854 4 : rsi->setResult = state->tuple_store;
1855 4 : rsi->setDesc = state->ret_tdesc;
1856 :
1857 4 : PG_RETURN_NULL();
1858 : }
1859 :
1860 :
1861 : static void
1862 21 : each_object_field_start(void *state, char *fname, bool isnull)
1863 : {
1864 21 : EachState *_state = (EachState *) state;
1865 :
1866 : /* save a pointer to where the value starts */
1867 21 : if (_state->lex->lex_level == 1)
1868 : {
1869 : /*
1870 : * next_scalar will be reset in the object_field_end handler, and
1871 : * since we know the value is a scalar there is no danger of it being
1872 : * on while recursing down the tree.
1873 : */
1874 17 : if (_state->normalize_results && _state->lex->token_type == JSON_TOKEN_STRING)
1875 2 : _state->next_scalar = true;
1876 : else
1877 15 : _state->result_start = _state->lex->token_start;
1878 : }
1879 21 : }
1880 :
1881 : static void
1882 21 : each_object_field_end(void *state, char *fname, bool isnull)
1883 : {
1884 21 : EachState *_state = (EachState *) state;
1885 : MemoryContext old_cxt;
1886 : int len;
1887 : text *val;
1888 : HeapTuple tuple;
1889 : Datum values[2];
1890 21 : bool nulls[2] = {false, false};
1891 :
1892 : /* skip over nested objects */
1893 21 : if (_state->lex->lex_level != 1)
1894 25 : return;
1895 :
1896 : /* use the tmp context so we can clean up after each tuple is done */
1897 17 : old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
1898 :
1899 17 : values[0] = CStringGetTextDatum(fname);
1900 :
1901 17 : if (isnull && _state->normalize_results)
1902 : {
1903 2 : nulls[1] = true;
1904 2 : values[1] = (Datum) 0;
1905 : }
1906 15 : else if (_state->next_scalar)
1907 : {
1908 2 : values[1] = CStringGetTextDatum(_state->normalized_scalar);
1909 2 : _state->next_scalar = false;
1910 : }
1911 : else
1912 : {
1913 13 : len = _state->lex->prev_token_terminator - _state->result_start;
1914 13 : val = cstring_to_text_with_len(_state->result_start, len);
1915 13 : values[1] = PointerGetDatum(val);
1916 : }
1917 :
1918 17 : tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
1919 :
1920 17 : tuplestore_puttuple(_state->tuple_store, tuple);
1921 :
1922 : /* clean up and switch back */
1923 17 : MemoryContextSwitchTo(old_cxt);
1924 17 : MemoryContextReset(_state->tmp_cxt);
1925 : }
1926 :
1927 : static void
1928 4 : each_array_start(void *state)
1929 : {
1930 4 : EachState *_state = (EachState *) state;
1931 :
1932 : /* json structure check */
1933 4 : if (_state->lex->lex_level == 0)
1934 0 : ereport(ERROR,
1935 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1936 : errmsg("cannot deconstruct an array as an object")));
1937 4 : }
1938 :
1939 : static void
1940 25 : each_scalar(void *state, char *token, JsonTokenType tokentype)
1941 : {
1942 25 : EachState *_state = (EachState *) state;
1943 :
1944 : /* json structure check */
1945 25 : if (_state->lex->lex_level == 0)
1946 0 : ereport(ERROR,
1947 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1948 : errmsg("cannot deconstruct a scalar")));
1949 :
1950 : /* supply de-escaped value if required */
1951 25 : if (_state->next_scalar)
1952 2 : _state->normalized_scalar = token;
1953 25 : }
1954 :
1955 : /*
1956 : * SQL functions json_array_elements and json_array_elements_text
1957 : *
1958 : * get the elements from a json array
1959 : *
1960 : * a lot of this processing is similar to the json_each* functions
1961 : */
1962 :
1963 : Datum
1964 2 : jsonb_array_elements(PG_FUNCTION_ARGS)
1965 : {
1966 2 : return elements_worker_jsonb(fcinfo, "jsonb_array_elements", false);
1967 : }
1968 :
1969 : Datum
1970 2 : jsonb_array_elements_text(PG_FUNCTION_ARGS)
1971 : {
1972 2 : return elements_worker_jsonb(fcinfo, "jsonb_array_elements_text", true);
1973 : }
1974 :
1975 : static Datum
1976 4 : elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
1977 : bool as_text)
1978 : {
1979 4 : Jsonb *jb = PG_GETARG_JSONB(0);
1980 : ReturnSetInfo *rsi;
1981 : Tuplestorestate *tuple_store;
1982 : TupleDesc tupdesc;
1983 : TupleDesc ret_tdesc;
1984 : MemoryContext old_cxt,
1985 : tmp_cxt;
1986 4 : bool skipNested = false;
1987 : JsonbIterator *it;
1988 : JsonbValue v;
1989 : JsonbIteratorToken r;
1990 :
1991 4 : if (JB_ROOT_IS_SCALAR(jb))
1992 0 : ereport(ERROR,
1993 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1994 : errmsg("cannot extract elements from a scalar")));
1995 4 : else if (!JB_ROOT_IS_ARRAY(jb))
1996 0 : ereport(ERROR,
1997 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1998 : errmsg("cannot extract elements from an object")));
1999 :
2000 4 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
2001 :
2002 8 : if (!rsi || !IsA(rsi, ReturnSetInfo) ||
2003 8 : (rsi->allowedModes & SFRM_Materialize) == 0 ||
2004 4 : rsi->expectedDesc == NULL)
2005 0 : ereport(ERROR,
2006 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2007 : errmsg("set-valued function called in context that "
2008 : "cannot accept a set")));
2009 :
2010 4 : rsi->returnMode = SFRM_Materialize;
2011 :
2012 : /* it's a simple type, so don't use get_call_result_type() */
2013 4 : tupdesc = rsi->expectedDesc;
2014 :
2015 4 : old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
2016 :
2017 4 : ret_tdesc = CreateTupleDescCopy(tupdesc);
2018 4 : BlessTupleDesc(ret_tdesc);
2019 4 : tuple_store =
2020 4 : tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
2021 : false, work_mem);
2022 :
2023 4 : MemoryContextSwitchTo(old_cxt);
2024 :
2025 4 : tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
2026 : "jsonb_array_elements temporary cxt",
2027 : ALLOCSET_DEFAULT_SIZES);
2028 :
2029 4 : it = JsonbIteratorInit(&jb->root);
2030 :
2031 42 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
2032 : {
2033 34 : skipNested = true;
2034 :
2035 34 : if (r == WJB_ELEM)
2036 : {
2037 : HeapTuple tuple;
2038 : Datum values[1];
2039 26 : bool nulls[1] = {false};
2040 :
2041 : /* use the tmp context so we can clean up after each tuple is done */
2042 26 : old_cxt = MemoryContextSwitchTo(tmp_cxt);
2043 :
2044 26 : if (!as_text)
2045 : {
2046 12 : Jsonb *val = JsonbValueToJsonb(&v);
2047 :
2048 12 : values[0] = PointerGetDatum(val);
2049 : }
2050 : else
2051 : {
2052 14 : if (v.type == jbvNull)
2053 : {
2054 : /* a json null is an sql null in text mode */
2055 2 : nulls[0] = true;
2056 2 : values[0] = (Datum) NULL;
2057 : }
2058 : else
2059 : {
2060 : text *sv;
2061 :
2062 12 : if (v.type == jbvString)
2063 : {
2064 : /* in text mode scalar strings should be dequoted */
2065 2 : sv = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
2066 : }
2067 : else
2068 : {
2069 : /* turn anything else into a json string */
2070 10 : StringInfo jtext = makeStringInfo();
2071 10 : Jsonb *jb = JsonbValueToJsonb(&v);
2072 :
2073 10 : (void) JsonbToCString(jtext, &jb->root, 0);
2074 10 : sv = cstring_to_text_with_len(jtext->data, jtext->len);
2075 : }
2076 :
2077 12 : values[0] = PointerGetDatum(sv);
2078 : }
2079 : }
2080 :
2081 26 : tuple = heap_form_tuple(ret_tdesc, values, nulls);
2082 :
2083 26 : tuplestore_puttuple(tuple_store, tuple);
2084 :
2085 : /* clean up and switch back */
2086 26 : MemoryContextSwitchTo(old_cxt);
2087 26 : MemoryContextReset(tmp_cxt);
2088 : }
2089 : }
2090 :
2091 4 : MemoryContextDelete(tmp_cxt);
2092 :
2093 4 : rsi->setResult = tuple_store;
2094 4 : rsi->setDesc = ret_tdesc;
2095 :
2096 4 : PG_RETURN_NULL();
2097 : }
2098 :
2099 : Datum
2100 2 : json_array_elements(PG_FUNCTION_ARGS)
2101 : {
2102 2 : return elements_worker(fcinfo, "json_array_elements", false);
2103 : }
2104 :
2105 : Datum
2106 2 : json_array_elements_text(PG_FUNCTION_ARGS)
2107 : {
2108 2 : return elements_worker(fcinfo, "json_array_elements_text", true);
2109 : }
2110 :
2111 : static Datum
2112 4 : elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
2113 : {
2114 4 : text *json = PG_GETARG_TEXT_PP(0);
2115 :
2116 : /* elements only needs escaped strings when as_text */
2117 4 : JsonLexContext *lex = makeJsonLexContext(json, as_text);
2118 : JsonSemAction *sem;
2119 : ReturnSetInfo *rsi;
2120 : MemoryContext old_cxt;
2121 : TupleDesc tupdesc;
2122 : ElementsState *state;
2123 :
2124 4 : state = palloc0(sizeof(ElementsState));
2125 4 : sem = palloc0(sizeof(JsonSemAction));
2126 :
2127 4 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
2128 :
2129 8 : if (!rsi || !IsA(rsi, ReturnSetInfo) ||
2130 8 : (rsi->allowedModes & SFRM_Materialize) == 0 ||
2131 4 : rsi->expectedDesc == NULL)
2132 0 : ereport(ERROR,
2133 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2134 : errmsg("set-valued function called in context that "
2135 : "cannot accept a set")));
2136 :
2137 4 : rsi->returnMode = SFRM_Materialize;
2138 :
2139 : /* it's a simple type, so don't use get_call_result_type() */
2140 4 : tupdesc = rsi->expectedDesc;
2141 :
2142 : /* make these in a sufficiently long-lived memory context */
2143 4 : old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
2144 :
2145 4 : state->ret_tdesc = CreateTupleDescCopy(tupdesc);
2146 4 : BlessTupleDesc(state->ret_tdesc);
2147 4 : state->tuple_store =
2148 4 : tuplestore_begin_heap(rsi->allowedModes & SFRM_Materialize_Random,
2149 : false, work_mem);
2150 :
2151 4 : MemoryContextSwitchTo(old_cxt);
2152 :
2153 4 : sem->semstate = (void *) state;
2154 4 : sem->object_start = elements_object_start;
2155 4 : sem->scalar = elements_scalar;
2156 4 : sem->array_element_start = elements_array_element_start;
2157 4 : sem->array_element_end = elements_array_element_end;
2158 :
2159 4 : state->function_name = funcname;
2160 4 : state->normalize_results = as_text;
2161 4 : state->next_scalar = false;
2162 4 : state->lex = lex;
2163 4 : state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
2164 : "json_array_elements temporary cxt",
2165 : ALLOCSET_DEFAULT_SIZES);
2166 :
2167 4 : pg_parse_json(lex, sem);
2168 :
2169 4 : MemoryContextDelete(state->tmp_cxt);
2170 :
2171 4 : rsi->setResult = state->tuple_store;
2172 4 : rsi->setDesc = state->ret_tdesc;
2173 :
2174 4 : PG_RETURN_NULL();
2175 : }
2176 :
2177 : static void
2178 56 : elements_array_element_start(void *state, bool isnull)
2179 : {
2180 56 : ElementsState *_state = (ElementsState *) state;
2181 :
2182 : /* save a pointer to where the value starts */
2183 56 : if (_state->lex->lex_level == 1)
2184 : {
2185 : /*
2186 : * next_scalar will be reset in the array_element_end handler, and
2187 : * since we know the value is a scalar there is no danger of it being
2188 : * on while recursing down the tree.
2189 : */
2190 28 : if (_state->normalize_results && _state->lex->token_type == JSON_TOKEN_STRING)
2191 2 : _state->next_scalar = true;
2192 : else
2193 26 : _state->result_start = _state->lex->token_start;
2194 : }
2195 56 : }
2196 :
2197 : static void
2198 56 : elements_array_element_end(void *state, bool isnull)
2199 : {
2200 56 : ElementsState *_state = (ElementsState *) state;
2201 : MemoryContext old_cxt;
2202 : int len;
2203 : text *val;
2204 : HeapTuple tuple;
2205 : Datum values[1];
2206 56 : bool nulls[1] = {false};
2207 :
2208 : /* skip over nested objects */
2209 56 : if (_state->lex->lex_level != 1)
2210 84 : return;
2211 :
2212 : /* use the tmp context so we can clean up after each tuple is done */
2213 28 : old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
2214 :
2215 28 : if (isnull && _state->normalize_results)
2216 : {
2217 2 : nulls[0] = true;
2218 2 : values[0] = (Datum) NULL;
2219 : }
2220 26 : else if (_state->next_scalar)
2221 : {
2222 2 : values[0] = CStringGetTextDatum(_state->normalized_scalar);
2223 2 : _state->next_scalar = false;
2224 : }
2225 : else
2226 : {
2227 24 : len = _state->lex->prev_token_terminator - _state->result_start;
2228 24 : val = cstring_to_text_with_len(_state->result_start, len);
2229 24 : values[0] = PointerGetDatum(val);
2230 : }
2231 :
2232 28 : tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
2233 :
2234 28 : tuplestore_puttuple(_state->tuple_store, tuple);
2235 :
2236 : /* clean up and switch back */
2237 28 : MemoryContextSwitchTo(old_cxt);
2238 28 : MemoryContextReset(_state->tmp_cxt);
2239 : }
2240 :
2241 : static void
2242 4 : elements_object_start(void *state)
2243 : {
2244 4 : ElementsState *_state = (ElementsState *) state;
2245 :
2246 : /* json structure check */
2247 4 : if (_state->lex->lex_level == 0)
2248 0 : ereport(ERROR,
2249 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2250 : errmsg("cannot call %s on a non-array",
2251 : _state->function_name)));
2252 4 : }
2253 :
2254 : static void
2255 48 : elements_scalar(void *state, char *token, JsonTokenType tokentype)
2256 : {
2257 48 : ElementsState *_state = (ElementsState *) state;
2258 :
2259 : /* json structure check */
2260 48 : if (_state->lex->lex_level == 0)
2261 0 : ereport(ERROR,
2262 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2263 : errmsg("cannot call %s on a scalar",
2264 : _state->function_name)));
2265 :
2266 : /* supply de-escaped value if required */
2267 48 : if (_state->next_scalar)
2268 2 : _state->normalized_scalar = token;
2269 48 : }
2270 :
2271 : /*
2272 : * SQL function json_populate_record
2273 : *
2274 : * set fields in a record from the argument json
2275 : *
2276 : * Code adapted shamelessly from hstore's populate_record
2277 : * which is in turn partly adapted from record_out.
2278 : *
2279 : * The json is decomposed into a hash table, in which each
2280 : * field in the record is then looked up by name. For jsonb
2281 : * we fetch the values direct from the object.
2282 : */
2283 : Datum
2284 131 : jsonb_populate_record(PG_FUNCTION_ARGS)
2285 : {
2286 131 : return populate_record_worker(fcinfo, "jsonb_populate_record", true);
2287 : }
2288 :
2289 : Datum
2290 11 : jsonb_to_record(PG_FUNCTION_ARGS)
2291 : {
2292 11 : return populate_record_worker(fcinfo, "jsonb_to_record", false);
2293 : }
2294 :
2295 : Datum
2296 131 : json_populate_record(PG_FUNCTION_ARGS)
2297 : {
2298 131 : return populate_record_worker(fcinfo, "json_populate_record", true);
2299 : }
2300 :
2301 : Datum
2302 11 : json_to_record(PG_FUNCTION_ARGS)
2303 : {
2304 11 : return populate_record_worker(fcinfo, "json_to_record", false);
2305 : }
2306 :
2307 : /* helper function for diagnostics */
2308 : static void
2309 26 : populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
2310 : {
2311 26 : if (ndim <= 0)
2312 : {
2313 16 : if (ctx->colname)
2314 16 : ereport(ERROR,
2315 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2316 : errmsg("expected json array"),
2317 : errhint("see the value of key \"%s\"", ctx->colname)));
2318 : else
2319 0 : ereport(ERROR,
2320 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2321 : errmsg("expected json array")));
2322 : }
2323 : else
2324 : {
2325 : StringInfoData indices;
2326 : int i;
2327 :
2328 10 : initStringInfo(&indices);
2329 :
2330 10 : Assert(ctx->ndims > 0 && ndim < ctx->ndims);
2331 :
2332 20 : for (i = 0; i < ndim; i++)
2333 10 : appendStringInfo(&indices, "[%d]", ctx->sizes[i]);
2334 :
2335 10 : if (ctx->colname)
2336 10 : ereport(ERROR,
2337 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2338 : errmsg("expected json array"),
2339 : errhint("see the array element %s of key \"%s\"",
2340 : indices.data, ctx->colname)));
2341 : else
2342 0 : ereport(ERROR,
2343 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2344 : errmsg("expected json array"),
2345 : errhint("see the array element %s",
2346 : indices.data)));
2347 : }
2348 : }
2349 :
2350 : /* set the number of dimensions of the populated array when it becomes known */
2351 : static void
2352 292 : populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
2353 : {
2354 : int i;
2355 :
2356 292 : Assert(ctx->ndims <= 0);
2357 :
2358 292 : if (ndims <= 0)
2359 8 : populate_array_report_expected_array(ctx, ndims);
2360 :
2361 284 : ctx->ndims = ndims;
2362 284 : ctx->dims = palloc(sizeof(int) * ndims);
2363 284 : ctx->sizes = palloc0(sizeof(int) * ndims);
2364 :
2365 624 : for (i = 0; i < ndims; i++)
2366 340 : ctx->dims[i] = -1; /* dimensions are unknown yet */
2367 284 : }
2368 :
2369 : /* check the populated subarray dimension */
2370 : static void
2371 251 : populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
2372 : {
2373 251 : int dim = ctx->sizes[ndim]; /* current dimension counter */
2374 :
2375 251 : if (ctx->dims[ndim] == -1)
2376 187 : ctx->dims[ndim] = dim; /* assign dimension if not yet known */
2377 64 : else if (ctx->dims[ndim] != dim)
2378 8 : ereport(ERROR,
2379 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2380 : errmsg("malformed json array"),
2381 : errdetail("Multidimensional arrays must have "
2382 : "sub-arrays with matching dimensions.")));
2383 :
2384 : /* reset the current array dimension size counter */
2385 243 : ctx->sizes[ndim] = 0;
2386 :
2387 : /* increment the parent dimension counter if it is a nested sub-array */
2388 243 : if (ndim > 0)
2389 112 : ctx->sizes[ndim - 1]++;
2390 243 : }
2391 :
2392 : static void
2393 984 : populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
2394 : {
2395 : Datum element;
2396 : bool element_isnull;
2397 :
2398 : /* populate the array element */
2399 2952 : element = populate_record_field(ctx->aio->element_info,
2400 984 : ctx->aio->element_type,
2401 984 : ctx->aio->element_typmod,
2402 : NULL, ctx->mcxt, PointerGetDatum(NULL),
2403 : jsv, &element_isnull);
2404 :
2405 1960 : accumArrayResult(ctx->astate, element, element_isnull,
2406 980 : ctx->aio->element_type, ctx->acxt);
2407 :
2408 980 : Assert(ndim > 0);
2409 980 : ctx->sizes[ndim - 1]++; /* increment current dimension counter */
2410 980 : }
2411 :
2412 : /* json object start handler for populate_array_json() */
2413 : static void
2414 108 : populate_array_object_start(void *_state)
2415 : {
2416 108 : PopulateArrayState *state = (PopulateArrayState *) _state;
2417 108 : int ndim = state->lex->lex_level;
2418 :
2419 108 : if (state->ctx->ndims <= 0)
2420 52 : populate_array_assign_ndims(state->ctx, ndim);
2421 56 : else if (ndim < state->ctx->ndims)
2422 2 : populate_array_report_expected_array(state->ctx, ndim);
2423 106 : }
2424 :
2425 : /* json array end handler for populate_array_json() */
2426 : static void
2427 192 : populate_array_array_end(void *_state)
2428 : {
2429 192 : PopulateArrayState *state = (PopulateArrayState *) _state;
2430 192 : PopulateArrayContext *ctx = state->ctx;
2431 192 : int ndim = state->lex->lex_level;
2432 :
2433 192 : if (ctx->ndims <= 0)
2434 2 : populate_array_assign_ndims(ctx, ndim + 1);
2435 :
2436 192 : if (ndim < ctx->ndims)
2437 191 : populate_array_check_dimension(ctx, ndim);
2438 188 : }
2439 :
2440 : /* json array element start handler for populate_array_json() */
2441 : static void
2442 561 : populate_array_element_start(void *_state, bool isnull)
2443 : {
2444 561 : PopulateArrayState *state = (PopulateArrayState *) _state;
2445 561 : int ndim = state->lex->lex_level;
2446 :
2447 561 : if (state->ctx->ndims <= 0 || ndim == state->ctx->ndims)
2448 : {
2449 : /* remember current array element start */
2450 520 : state->element_start = state->lex->token_start;
2451 520 : state->element_type = state->lex->token_type;
2452 520 : state->element_scalar = NULL;
2453 : }
2454 561 : }
2455 :
2456 : /* json array element end handler for populate_array_json() */
2457 : static void
2458 552 : populate_array_element_end(void *_state, bool isnull)
2459 : {
2460 552 : PopulateArrayState *state = (PopulateArrayState *) _state;
2461 552 : PopulateArrayContext *ctx = state->ctx;
2462 552 : int ndim = state->lex->lex_level;
2463 :
2464 552 : Assert(ctx->ndims > 0);
2465 :
2466 552 : if (ndim == ctx->ndims)
2467 : {
2468 : JsValue jsv;
2469 :
2470 492 : jsv.is_json = true;
2471 492 : jsv.val.json.type = state->element_type;
2472 :
2473 492 : if (isnull)
2474 : {
2475 118 : Assert(jsv.val.json.type == JSON_TOKEN_NULL);
2476 118 : jsv.val.json.str = NULL;
2477 118 : jsv.val.json.len = 0;
2478 : }
2479 374 : else if (state->element_scalar)
2480 : {
2481 268 : jsv.val.json.str = state->element_scalar;
2482 268 : jsv.val.json.len = -1; /* null-terminated */
2483 : }
2484 : else
2485 : {
2486 106 : jsv.val.json.str = state->element_start;
2487 212 : jsv.val.json.len = (state->lex->prev_token_terminator -
2488 106 : state->element_start) * sizeof(char);
2489 : }
2490 :
2491 492 : populate_array_element(ctx, ndim, &jsv);
2492 : }
2493 550 : }
2494 :
2495 : /* json scalar handler for populate_array_json() */
2496 : static void
2497 609 : populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
2498 : {
2499 609 : PopulateArrayState *state = (PopulateArrayState *) _state;
2500 609 : PopulateArrayContext *ctx = state->ctx;
2501 609 : int ndim = state->lex->lex_level;
2502 :
2503 609 : if (ctx->ndims <= 0)
2504 96 : populate_array_assign_ndims(ctx, ndim);
2505 513 : else if (ndim < ctx->ndims)
2506 3 : populate_array_report_expected_array(ctx, ndim);
2507 :
2508 598 : if (ndim == ctx->ndims)
2509 : {
2510 : /* remember the scalar element token */
2511 386 : state->element_scalar = token;
2512 : /* element_type must already be set in populate_array_element_start() */
2513 386 : Assert(state->element_type == tokentype);
2514 : }
2515 598 : }
2516 :
2517 : /* parse a json array and populate array */
2518 : static void
2519 150 : populate_array_json(PopulateArrayContext *ctx, char *json, int len)
2520 : {
2521 : PopulateArrayState state;
2522 : JsonSemAction sem;
2523 :
2524 150 : state.lex = makeJsonLexContextCstringLen(json, len, true);
2525 150 : state.ctx = ctx;
2526 :
2527 150 : memset(&sem, 0, sizeof(sem));
2528 150 : sem.semstate = (void *) &state;
2529 150 : sem.object_start = populate_array_object_start;
2530 150 : sem.array_end = populate_array_array_end;
2531 150 : sem.array_element_start = populate_array_element_start;
2532 150 : sem.array_element_end = populate_array_element_end;
2533 150 : sem.scalar = populate_array_scalar;
2534 :
2535 150 : pg_parse_json(state.lex, &sem);
2536 :
2537 : /* number of dimensions should be already known */
2538 131 : Assert(ctx->ndims > 0 && ctx->dims);
2539 :
2540 131 : pfree(state.lex);
2541 131 : }
2542 :
2543 : /*
2544 : * populate_array_dim_jsonb() -- Iterate recursively through jsonb sub-array
2545 : * elements and accumulate result using given ArrayBuildState.
2546 : */
2547 : static void
2548 215 : populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
2549 : JsonbValue *jbv, /* jsonb sub-array */
2550 : int ndim) /* current dimension */
2551 : {
2552 215 : JsonbContainer *jbc = jbv->val.binary.data;
2553 : JsonbIterator *it;
2554 : JsonbIteratorToken tok;
2555 : JsonbValue val;
2556 : JsValue jsv;
2557 :
2558 215 : check_stack_depth();
2559 :
2560 215 : if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc))
2561 13 : populate_array_report_expected_array(ctx, ndim - 1);
2562 :
2563 202 : Assert(!JsonContainerIsScalar(jbc));
2564 :
2565 202 : it = JsonbIteratorInit(jbc);
2566 :
2567 202 : tok = JsonbIteratorNext(&it, &val, true);
2568 202 : Assert(tok == WJB_BEGIN_ARRAY);
2569 :
2570 202 : tok = JsonbIteratorNext(&it, &val, true);
2571 :
2572 : /*
2573 : * If the number of dimensions is not yet known and we have found end of
2574 : * the array, or the first child element is not an array, then assign the
2575 : * number of dimensions now.
2576 : */
2577 202 : if (ctx->ndims <= 0 &&
2578 168 : (tok == WJB_END_ARRAY ||
2579 168 : (tok == WJB_ELEM &&
2580 248 : (val.type != jbvBinary ||
2581 80 : !JsonContainerIsArray(val.val.binary.data)))))
2582 142 : populate_array_assign_ndims(ctx, ndim);
2583 :
2584 202 : jsv.is_json = false;
2585 202 : jsv.val.jsonb = &val;
2586 :
2587 : /* process all the array elements */
2588 950 : while (tok == WJB_ELEM)
2589 : {
2590 : /*
2591 : * Recurse only if the dimensions of dimensions is still unknown or if
2592 : * it is not the innermost dimension.
2593 : */
2594 557 : if (ctx->ndims > 0 && ndim >= ctx->ndims)
2595 492 : populate_array_element(ctx, ndim, &jsv);
2596 : else
2597 : {
2598 : /* populate child sub-array */
2599 65 : populate_array_dim_jsonb(ctx, &val, ndim + 1);
2600 :
2601 : /* number of dimensions should be already known */
2602 60 : Assert(ctx->ndims > 0 && ctx->dims);
2603 :
2604 60 : populate_array_check_dimension(ctx, ndim);
2605 : }
2606 :
2607 546 : tok = JsonbIteratorNext(&it, &val, true);
2608 : }
2609 :
2610 191 : Assert(tok == WJB_END_ARRAY);
2611 :
2612 : /* free iterator, iterating until WJB_DONE */
2613 191 : tok = JsonbIteratorNext(&it, &val, true);
2614 191 : Assert(tok == WJB_DONE && !it);
2615 191 : }
2616 :
2617 : /* recursively populate an array from json/jsonb */
2618 : static Datum
2619 300 : populate_array(ArrayIOData *aio,
2620 : const char *colname,
2621 : MemoryContext mcxt,
2622 : JsValue *jsv)
2623 : {
2624 : PopulateArrayContext ctx;
2625 : Datum result;
2626 : int *lbs;
2627 : int i;
2628 :
2629 300 : ctx.aio = aio;
2630 300 : ctx.mcxt = mcxt;
2631 300 : ctx.acxt = CurrentMemoryContext;
2632 300 : ctx.astate = initArrayResult(aio->element_type, ctx.acxt, true);
2633 300 : ctx.colname = colname;
2634 300 : ctx.ndims = 0; /* unknown yet */
2635 300 : ctx.dims = NULL;
2636 300 : ctx.sizes = NULL;
2637 :
2638 300 : if (jsv->is_json)
2639 300 : populate_array_json(&ctx, jsv->val.json.str,
2640 150 : jsv->val.json.len >= 0 ? jsv->val.json.len
2641 150 : : strlen(jsv->val.json.str));
2642 : else
2643 : {
2644 150 : populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1);
2645 131 : ctx.dims[0] = ctx.sizes[0];
2646 : }
2647 :
2648 262 : Assert(ctx.ndims > 0);
2649 :
2650 262 : lbs = palloc(sizeof(int) * ctx.ndims);
2651 :
2652 560 : for (i = 0; i < ctx.ndims; i++)
2653 298 : lbs[i] = 1;
2654 :
2655 262 : result = makeMdArrayResult(ctx.astate, ctx.ndims, ctx.dims, lbs,
2656 : ctx.acxt, true);
2657 :
2658 262 : pfree(ctx.dims);
2659 262 : pfree(ctx.sizes);
2660 262 : pfree(lbs);
2661 :
2662 262 : return result;
2663 : }
2664 :
2665 : static void
2666 606 : JsValueToJsObject(JsValue *jsv, JsObject *jso)
2667 : {
2668 606 : jso->is_json = jsv->is_json;
2669 :
2670 606 : if (jsv->is_json)
2671 : {
2672 : /* convert plain-text json into a hash table */
2673 300 : jso->val.json_hash =
2674 360 : get_json_object_as_hash(jsv->val.json.str,
2675 303 : jsv->val.json.len >= 0
2676 : ? jsv->val.json.len
2677 57 : : strlen(jsv->val.json.str),
2678 : "populate_composite");
2679 : }
2680 : else
2681 : {
2682 303 : JsonbValue *jbv = jsv->val.jsonb;
2683 :
2684 604 : if (jbv->type == jbvBinary &&
2685 301 : JsonContainerIsObject(jbv->val.binary.data))
2686 : {
2687 300 : jso->val.jsonb_cont = jbv->val.binary.data;
2688 : }
2689 : else
2690 : {
2691 : bool is_scalar;
2692 :
2693 6 : is_scalar = IsAJsonbScalar(jbv) ||
2694 2 : (jbv->type == jbvBinary &&
2695 1 : JsonContainerIsScalar(jbv->val.binary.data));
2696 3 : ereport(ERROR,
2697 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2698 : is_scalar
2699 : ? errmsg("cannot call %s on a scalar",
2700 : "populate_composite")
2701 : : errmsg("cannot call %s on an array",
2702 : "populate_composite")));
2703 : }
2704 : }
2705 600 : }
2706 :
2707 : /* recursively populate a composite (row type) value from json/jsonb */
2708 : static Datum
2709 606 : populate_composite(CompositeIOData *io,
2710 : Oid typid,
2711 : int32 typmod,
2712 : const char *colname,
2713 : MemoryContext mcxt,
2714 : HeapTupleHeader defaultval,
2715 : JsValue *jsv)
2716 : {
2717 : HeapTupleHeader tuple;
2718 : JsObject jso;
2719 :
2720 : /* acquire cached tuple descriptor */
2721 936 : if (!io->tupdesc ||
2722 660 : io->tupdesc->tdtypeid != typid ||
2723 330 : io->tupdesc->tdtypmod != typmod)
2724 : {
2725 276 : TupleDesc tupdesc = lookup_rowtype_tupdesc(typid, typmod);
2726 : MemoryContext oldcxt;
2727 :
2728 276 : if (io->tupdesc)
2729 0 : FreeTupleDesc(io->tupdesc);
2730 :
2731 : /* copy tuple desc without constraints into cache memory context */
2732 276 : oldcxt = MemoryContextSwitchTo(mcxt);
2733 276 : io->tupdesc = CreateTupleDescCopy(tupdesc);
2734 276 : MemoryContextSwitchTo(oldcxt);
2735 :
2736 276 : ReleaseTupleDesc(tupdesc);
2737 : }
2738 :
2739 : /* prepare input value */
2740 606 : JsValueToJsObject(jsv, &jso);
2741 :
2742 : /* populate resulting record tuple */
2743 600 : tuple = populate_record(io->tupdesc, &io->record_io,
2744 : defaultval, mcxt, &jso);
2745 :
2746 546 : JsObjectFree(&jso);
2747 :
2748 546 : return HeapTupleHeaderGetDatum(tuple);
2749 : }
2750 :
2751 : /* populate non-null scalar value from json/jsonb value */
2752 : static Datum
2753 1208 : populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv)
2754 : {
2755 : Datum res;
2756 1208 : char *str = NULL;
2757 1208 : char *json = NULL;
2758 :
2759 1208 : if (jsv->is_json)
2760 : {
2761 609 : int len = jsv->val.json.len;
2762 :
2763 609 : json = jsv->val.json.str;
2764 609 : Assert(json);
2765 :
2766 : /* already done the hard work in the json case */
2767 785 : if ((typid == JSONOID || typid == JSONBOID) &&
2768 176 : jsv->val.json.type == JSON_TOKEN_STRING)
2769 : {
2770 : /*
2771 : * Add quotes around string value (should be already escaped) if
2772 : * converting to json/jsonb.
2773 : */
2774 :
2775 57 : if (len < 0)
2776 57 : len = strlen(json);
2777 :
2778 57 : str = palloc(len + sizeof(char) * 3);
2779 57 : str[0] = '"';
2780 57 : memcpy(&str[1], json, len);
2781 57 : str[len + 1] = '"';
2782 57 : str[len + 2] = '\0';
2783 : }
2784 552 : else if (len >= 0)
2785 : {
2786 : /* Need to copy non-null-terminated string */
2787 2 : str = palloc(len + 1 * sizeof(char));
2788 2 : memcpy(str, json, len);
2789 2 : str[len] = '\0';
2790 : }
2791 : else
2792 550 : str = json; /* null-terminated string */
2793 : }
2794 : else
2795 : {
2796 599 : JsonbValue *jbv = jsv->val.jsonb;
2797 :
2798 599 : if (typid == JSONBOID)
2799 : {
2800 8 : Jsonb *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */
2801 :
2802 8 : return JsonbGetDatum(jsonb);
2803 : }
2804 : /* convert jsonb to string for typio call */
2805 591 : else if (typid == JSONOID && jbv->type != jbvBinary)
2806 161 : {
2807 : /*
2808 : * Convert scalar jsonb (non-scalars are passed here as jbvBinary)
2809 : * to json string, preserving quotes around top-level strings.
2810 : */
2811 161 : Jsonb *jsonb = JsonbValueToJsonb(jbv);
2812 :
2813 161 : str = JsonbToCString(NULL, &jsonb->root, VARSIZE(jsonb));
2814 : }
2815 430 : else if (jbv->type == jbvString) /* quotes are stripped */
2816 252 : str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
2817 178 : else if (jbv->type == jbvBool)
2818 1 : str = pstrdup(jbv->val.boolean ? "true" : "false");
2819 177 : else if (jbv->type == jbvNumeric)
2820 165 : str = DatumGetCString(DirectFunctionCall1(numeric_out,
2821 : PointerGetDatum(jbv->val.numeric)));
2822 12 : else if (jbv->type == jbvBinary)
2823 12 : str = JsonbToCString(NULL, jbv->val.binary.data,
2824 : jbv->val.binary.len);
2825 : else
2826 0 : elog(ERROR, "unrecognized jsonb type: %d", (int) jbv->type);
2827 : }
2828 :
2829 1200 : res = InputFunctionCall(&io->typiofunc, str, io->typioparam, typmod);
2830 :
2831 : /* free temporary buffer */
2832 1192 : if (str != json)
2833 646 : pfree(str);
2834 :
2835 1192 : return res;
2836 : }
2837 :
2838 : static Datum
2839 450 : populate_domain(DomainIOData *io,
2840 : Oid typid,
2841 : const char *colname,
2842 : MemoryContext mcxt,
2843 : JsValue *jsv,
2844 : bool isnull)
2845 : {
2846 : Datum res;
2847 :
2848 450 : if (isnull)
2849 438 : res = (Datum) 0;
2850 : else
2851 : {
2852 12 : res = populate_record_field(io->base_io,
2853 : io->base_typid, io->base_typmod,
2854 : colname, mcxt, PointerGetDatum(NULL),
2855 : jsv, &isnull);
2856 10 : Assert(!isnull);
2857 : }
2858 :
2859 448 : domain_check(res, isnull, typid, &io->domain_info, mcxt);
2860 :
2861 440 : return res;
2862 : }
2863 :
2864 : /* prepare column metadata cache for the given type */
2865 : static void
2866 3067 : prepare_column_cache(ColumnIOData *column,
2867 : Oid typid,
2868 : int32 typmod,
2869 : MemoryContext mcxt,
2870 : bool json)
2871 : {
2872 : HeapTuple tup;
2873 : Form_pg_type type;
2874 :
2875 3067 : column->typid = typid;
2876 3067 : column->typmod = typmod;
2877 :
2878 3067 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
2879 3067 : if (!HeapTupleIsValid(tup))
2880 0 : elog(ERROR, "cache lookup failed for type %u", typid);
2881 :
2882 3067 : type = (Form_pg_type) GETSTRUCT(tup);
2883 :
2884 3067 : if (type->typtype == TYPTYPE_DOMAIN)
2885 : {
2886 314 : column->typcat = TYPECAT_DOMAIN;
2887 314 : column->io.domain.base_typid = type->typbasetype;
2888 314 : column->io.domain.base_typmod = type->typtypmod;
2889 314 : column->io.domain.base_io = MemoryContextAllocZero(mcxt,
2890 : sizeof(ColumnIOData));
2891 314 : column->io.domain.domain_info = NULL;
2892 : }
2893 2753 : else if (type->typtype == TYPTYPE_COMPOSITE || typid == RECORDOID)
2894 : {
2895 180 : column->typcat = TYPECAT_COMPOSITE;
2896 180 : column->io.composite.record_io = NULL;
2897 180 : column->io.composite.tupdesc = NULL;
2898 : }
2899 2573 : else if (type->typlen == -1 && OidIsValid(type->typelem))
2900 : {
2901 1250 : column->typcat = TYPECAT_ARRAY;
2902 1250 : column->io.array.element_info = MemoryContextAllocZero(mcxt,
2903 : sizeof(ColumnIOData));
2904 1250 : column->io.array.element_type = type->typelem;
2905 : /* array element typemod stored in attribute's typmod */
2906 1250 : column->io.array.element_typmod = typmod;
2907 : }
2908 : else
2909 1323 : column->typcat = TYPECAT_SCALAR;
2910 :
2911 : /* don't need input function when converting from jsonb to jsonb */
2912 3067 : if (json || typid != JSONBOID)
2913 : {
2914 : Oid typioproc;
2915 :
2916 2996 : getTypeInputInfo(typid, &typioproc, &column->scalar_io.typioparam);
2917 2996 : fmgr_info_cxt(typioproc, &column->scalar_io.typiofunc, mcxt);
2918 : }
2919 :
2920 3067 : ReleaseSysCache(tup);
2921 3067 : }
2922 :
2923 : /* recursively populate a record field or an array element from a json/jsonb value */
2924 : static Datum
2925 5814 : populate_record_field(ColumnIOData *col,
2926 : Oid typid,
2927 : int32 typmod,
2928 : const char *colname,
2929 : MemoryContext mcxt,
2930 : Datum defaultval,
2931 : JsValue *jsv,
2932 : bool *isnull)
2933 : {
2934 : TypeCat typcat;
2935 :
2936 5814 : check_stack_depth();
2937 :
2938 : /* prepare column metadata cache for the given type */
2939 5814 : if (col->typid != typid || col->typmod != typmod)
2940 3067 : prepare_column_cache(col, typid, typmod, mcxt, jsv->is_json);
2941 :
2942 5814 : *isnull = JsValueIsNull(jsv);
2943 :
2944 5814 : typcat = col->typcat;
2945 :
2946 : /* try to convert json string to a non-scalar type through input function */
2947 5814 : if (JsValueIsString(jsv) &&
2948 614 : (typcat == TYPECAT_ARRAY || typcat == TYPECAT_COMPOSITE))
2949 8 : typcat = TYPECAT_SCALAR;
2950 :
2951 : /* we must perform domain checks for NULLs */
2952 5814 : if (*isnull && typcat != TYPECAT_DOMAIN)
2953 3534 : return (Datum) 0;
2954 :
2955 2280 : switch (typcat)
2956 : {
2957 : case TYPECAT_SCALAR:
2958 1208 : return populate_scalar(&col->scalar_io, typid, typmod, jsv);
2959 :
2960 : case TYPECAT_ARRAY:
2961 300 : return populate_array(&col->io.array, colname, mcxt, jsv);
2962 :
2963 : case TYPECAT_COMPOSITE:
2964 324 : return populate_composite(&col->io.composite, typid, typmod,
2965 : colname, mcxt,
2966 : DatumGetPointer(defaultval)
2967 2 : ? DatumGetHeapTupleHeader(defaultval)
2968 : : NULL,
2969 : jsv);
2970 :
2971 : case TYPECAT_DOMAIN:
2972 450 : return populate_domain(&col->io.domain, typid, colname, mcxt,
2973 450 : jsv, *isnull);
2974 :
2975 : default:
2976 0 : elog(ERROR, "unrecognized type category '%c'", typcat);
2977 : return (Datum) 0;
2978 : }
2979 : }
2980 :
2981 : static RecordIOData *
2982 314 : allocate_record_info(MemoryContext mcxt, int ncolumns)
2983 : {
2984 314 : RecordIOData *data = (RecordIOData *)
2985 314 : MemoryContextAlloc(mcxt,
2986 : offsetof(RecordIOData, columns) +
2987 314 : ncolumns * sizeof(ColumnIOData));
2988 :
2989 314 : data->record_type = InvalidOid;
2990 314 : data->record_typmod = 0;
2991 314 : data->ncolumns = ncolumns;
2992 314 : MemSet(data->columns, 0, sizeof(ColumnIOData) * ncolumns);
2993 :
2994 314 : return data;
2995 : }
2996 :
2997 : static bool
2998 4898 : JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
2999 : {
3000 4898 : jsv->is_json = obj->is_json;
3001 :
3002 4898 : if (jsv->is_json)
3003 : {
3004 2454 : JsonHashEntry *hashentry = hash_search(obj->val.json_hash, field,
3005 : HASH_FIND, NULL);
3006 :
3007 2454 : jsv->val.json.type = hashentry ? hashentry->type : JSON_TOKEN_NULL;
3008 2454 : jsv->val.json.str = jsv->val.json.type == JSON_TOKEN_NULL ? NULL :
3009 : hashentry->val;
3010 2454 : jsv->val.json.len = jsv->val.json.str ? -1 : 0; /* null-terminated */
3011 :
3012 2454 : return hashentry != NULL;
3013 : }
3014 : else
3015 : {
3016 4888 : jsv->val.jsonb = !obj->val.jsonb_cont ? NULL :
3017 2444 : findJsonbValueFromContainerLen(obj->val.jsonb_cont, JB_FOBJECT,
3018 : field, strlen(field));
3019 :
3020 2444 : return jsv->val.jsonb != NULL;
3021 : }
3022 : }
3023 :
3024 : /* populate a record tuple from json/jsonb value */
3025 : static HeapTupleHeader
3026 643 : populate_record(TupleDesc tupdesc,
3027 : RecordIOData **record_p,
3028 : HeapTupleHeader defaultval,
3029 : MemoryContext mcxt,
3030 : JsObject *obj)
3031 : {
3032 643 : RecordIOData *record = *record_p;
3033 : Datum *values;
3034 : bool *nulls;
3035 : HeapTuple res;
3036 643 : int ncolumns = tupdesc->natts;
3037 : int i;
3038 :
3039 : /*
3040 : * if the input json is empty, we can only skip the rest if we were passed
3041 : * in a non-null record, since otherwise there may be issues with domain
3042 : * nulls.
3043 : */
3044 643 : if (defaultval && JsObjectIsEmpty(obj))
3045 2 : return defaultval;
3046 :
3047 : /* (re)allocate metadata cache */
3048 968 : if (record == NULL ||
3049 327 : record->ncolumns != ncolumns)
3050 314 : *record_p = record = allocate_record_info(mcxt, ncolumns);
3051 :
3052 : /* invalidate metadata cache if the record type has changed */
3053 968 : if (record->record_type != tupdesc->tdtypeid ||
3054 327 : record->record_typmod != tupdesc->tdtypmod)
3055 : {
3056 314 : MemSet(record, 0, offsetof(RecordIOData, columns) +
3057 : ncolumns * sizeof(ColumnIOData));
3058 314 : record->record_type = tupdesc->tdtypeid;
3059 314 : record->record_typmod = tupdesc->tdtypmod;
3060 314 : record->ncolumns = ncolumns;
3061 : }
3062 :
3063 641 : values = (Datum *) palloc(ncolumns * sizeof(Datum));
3064 641 : nulls = (bool *) palloc(ncolumns * sizeof(bool));
3065 :
3066 641 : if (defaultval)
3067 : {
3068 : HeapTupleData tuple;
3069 :
3070 : /* Build a temporary HeapTuple control structure */
3071 34 : tuple.t_len = HeapTupleHeaderGetDatumLength(defaultval);
3072 34 : ItemPointerSetInvalid(&(tuple.t_self));
3073 34 : tuple.t_tableOid = InvalidOid;
3074 34 : tuple.t_data = defaultval;
3075 :
3076 : /* Break down the tuple into fields */
3077 34 : heap_deform_tuple(&tuple, tupdesc, values, nulls);
3078 : }
3079 : else
3080 : {
3081 5723 : for (i = 0; i < ncolumns; ++i)
3082 : {
3083 5116 : values[i] = (Datum) 0;
3084 5116 : nulls[i] = true;
3085 : }
3086 : }
3087 :
3088 5483 : for (i = 0; i < ncolumns; ++i)
3089 : {
3090 4898 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3091 4898 : char *colname = NameStr(att->attname);
3092 4898 : JsValue field = {0};
3093 : bool found;
3094 :
3095 : /* Ignore dropped columns in datatype */
3096 4898 : if (att->attisdropped)
3097 : {
3098 0 : nulls[i] = true;
3099 80 : continue;
3100 : }
3101 :
3102 4898 : found = JsObjectGetField(obj, colname, &field);
3103 :
3104 : /*
3105 : * we can't just skip here if the key wasn't found since we might have
3106 : * a domain to deal with. If we were passed in a non-null record
3107 : * datum, we assume that the existing values are valid (if they're
3108 : * not, then it's not our fault), but if we were passed in a null,
3109 : * then every field which we don't populate needs to be run through
3110 : * the input function just in case it's a domain type.
3111 : */
3112 4898 : if (defaultval && !found)
3113 80 : continue;
3114 :
3115 9674 : values[i] = populate_record_field(&record->columns[i],
3116 : att->atttypid,
3117 : att->atttypmod,
3118 : colname,
3119 : mcxt,
3120 4856 : nulls[i] ? (Datum) 0 : values[i],
3121 : &field,
3122 : &nulls[i]);
3123 : }
3124 :
3125 585 : res = heap_form_tuple(tupdesc, values, nulls);
3126 :
3127 585 : pfree(values);
3128 585 : pfree(nulls);
3129 :
3130 585 : return res->t_data;
3131 : }
3132 :
3133 : static Datum
3134 284 : populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
3135 : bool have_record_arg)
3136 : {
3137 284 : int json_arg_num = have_record_arg ? 1 : 0;
3138 284 : Oid jtype = get_fn_expr_argtype(fcinfo->flinfo, json_arg_num);
3139 284 : JsValue jsv = {0};
3140 284 : HeapTupleHeader rec = NULL;
3141 : Oid tupType;
3142 : int32 tupTypmod;
3143 284 : TupleDesc tupdesc = NULL;
3144 : Datum rettuple;
3145 : JsonbValue jbv;
3146 284 : MemoryContext fnmcxt = fcinfo->flinfo->fn_mcxt;
3147 284 : PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
3148 :
3149 284 : Assert(jtype == JSONOID || jtype == JSONBOID);
3150 :
3151 : /*
3152 : * We arrange to look up the needed I/O info just once per series of
3153 : * calls, assuming the record type doesn't change underneath us.
3154 : */
3155 284 : if (!cache)
3156 216 : fcinfo->flinfo->fn_extra = cache =
3157 : MemoryContextAllocZero(fnmcxt, sizeof(*cache));
3158 :
3159 284 : if (have_record_arg)
3160 : {
3161 262 : Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
3162 :
3163 262 : if (cache->argtype != argtype)
3164 : {
3165 194 : if (!type_is_rowtype(argtype))
3166 0 : ereport(ERROR,
3167 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3168 : errmsg("first argument of %s must be a row type",
3169 : funcname)));
3170 :
3171 194 : cache->argtype = argtype;
3172 : }
3173 :
3174 262 : if (PG_ARGISNULL(0))
3175 : {
3176 250 : if (PG_ARGISNULL(1))
3177 0 : PG_RETURN_NULL();
3178 :
3179 : /*
3180 : * We have no tuple to look at, so the only source of type info is
3181 : * the argtype. The lookup_rowtype_tupdesc call below will error
3182 : * out if we don't have a known composite type oid here.
3183 : */
3184 250 : tupType = argtype;
3185 250 : tupTypmod = -1;
3186 : }
3187 : else
3188 : {
3189 12 : rec = PG_GETARG_HEAPTUPLEHEADER(0);
3190 :
3191 12 : if (PG_ARGISNULL(1))
3192 0 : PG_RETURN_POINTER(rec);
3193 :
3194 : /* Extract type info from the tuple itself */
3195 12 : tupType = HeapTupleHeaderGetTypeId(rec);
3196 12 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
3197 : }
3198 : }
3199 : else
3200 : {
3201 : /* json{b}_to_record case */
3202 22 : if (PG_ARGISNULL(0))
3203 0 : PG_RETURN_NULL();
3204 :
3205 22 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
3206 0 : ereport(ERROR,
3207 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3208 : errmsg("function returning record called in context "
3209 : "that cannot accept type record"),
3210 : errhint("Try calling the function in the FROM clause "
3211 : "using a column definition list.")));
3212 :
3213 22 : Assert(tupdesc);
3214 :
3215 : /*
3216 : * Add tupdesc to the cache and set the appropriate values of
3217 : * tupType/tupTypmod for proper cache usage in populate_composite().
3218 : */
3219 22 : cache->io.tupdesc = tupdesc;
3220 :
3221 22 : tupType = tupdesc->tdtypeid;
3222 22 : tupTypmod = tupdesc->tdtypmod;
3223 : }
3224 :
3225 284 : jsv.is_json = jtype == JSONOID;
3226 :
3227 284 : if (jsv.is_json)
3228 : {
3229 142 : text *json = PG_GETARG_TEXT_PP(json_arg_num);
3230 :
3231 142 : jsv.val.json.str = VARDATA_ANY(json);
3232 142 : jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
3233 142 : jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in
3234 : * populate_composite() */
3235 : }
3236 : else
3237 : {
3238 142 : Jsonb *jb = PG_GETARG_JSONB(json_arg_num);
3239 :
3240 142 : jsv.val.jsonb = &jbv;
3241 :
3242 : /* fill binary jsonb value pointing to jb */
3243 142 : jbv.type = jbvBinary;
3244 142 : jbv.val.binary.data = &jb->root;
3245 142 : jbv.val.binary.len = VARSIZE(jb) - VARHDRSZ;
3246 : }
3247 :
3248 284 : rettuple = populate_composite(&cache->io, tupType, tupTypmod,
3249 : NULL, fnmcxt, rec, &jsv);
3250 :
3251 230 : if (tupdesc)
3252 : {
3253 16 : cache->io.tupdesc = NULL;
3254 16 : ReleaseTupleDesc(tupdesc);
3255 : }
3256 :
3257 230 : PG_RETURN_DATUM(rettuple);
3258 : }
3259 :
3260 : /*
3261 : * get_json_object_as_hash
3262 : *
3263 : * decompose a json object into a hash table.
3264 : */
3265 : static HTAB *
3266 303 : get_json_object_as_hash(char *json, int len, const char *funcname)
3267 : {
3268 : HASHCTL ctl;
3269 : HTAB *tab;
3270 : JHashState *state;
3271 303 : JsonLexContext *lex = makeJsonLexContextCstringLen(json, len, true);
3272 : JsonSemAction *sem;
3273 :
3274 303 : memset(&ctl, 0, sizeof(ctl));
3275 303 : ctl.keysize = NAMEDATALEN;
3276 303 : ctl.entrysize = sizeof(JsonHashEntry);
3277 303 : ctl.hcxt = CurrentMemoryContext;
3278 303 : tab = hash_create("json object hashtable",
3279 : 100,
3280 : &ctl,
3281 : HASH_ELEM | HASH_CONTEXT);
3282 :
3283 303 : state = palloc0(sizeof(JHashState));
3284 303 : sem = palloc0(sizeof(JsonSemAction));
3285 :
3286 303 : state->function_name = funcname;
3287 303 : state->hash = tab;
3288 303 : state->lex = lex;
3289 :
3290 303 : sem->semstate = (void *) state;
3291 303 : sem->array_start = hash_array_start;
3292 303 : sem->scalar = hash_scalar;
3293 303 : sem->object_field_start = hash_object_field_start;
3294 303 : sem->object_field_end = hash_object_field_end;
3295 :
3296 303 : pg_parse_json(lex, sem);
3297 :
3298 300 : return tab;
3299 : }
3300 :
3301 : static void
3302 1008 : hash_object_field_start(void *state, char *fname, bool isnull)
3303 : {
3304 1008 : JHashState *_state = (JHashState *) state;
3305 :
3306 1008 : if (_state->lex->lex_level > 1)
3307 1390 : return;
3308 :
3309 : /* remember token type */
3310 626 : _state->saved_token_type = _state->lex->token_type;
3311 :
3312 1104 : if (_state->lex->token_type == JSON_TOKEN_ARRAY_START ||
3313 478 : _state->lex->token_type == JSON_TOKEN_OBJECT_START)
3314 : {
3315 : /* remember start position of the whole text of the subobject */
3316 205 : _state->save_json_start = _state->lex->token_start;
3317 : }
3318 : else
3319 : {
3320 : /* must be a scalar */
3321 421 : _state->save_json_start = NULL;
3322 : }
3323 : }
3324 :
3325 : static void
3326 1008 : hash_object_field_end(void *state, char *fname, bool isnull)
3327 : {
3328 1008 : JHashState *_state = (JHashState *) state;
3329 : JsonHashEntry *hashentry;
3330 : bool found;
3331 :
3332 : /*
3333 : * Ignore nested fields.
3334 : */
3335 1008 : if (_state->lex->lex_level > 1)
3336 764 : return;
3337 :
3338 : /*
3339 : * Ignore field names >= NAMEDATALEN - they can't match a record field.
3340 : * (Note: without this test, the hash code would truncate the string at
3341 : * NAMEDATALEN-1, and could then match against a similarly-truncated
3342 : * record field name. That would be a reasonable behavior, but this code
3343 : * has previously insisted on exact equality, so we keep this behavior.)
3344 : */
3345 626 : if (strlen(fname) >= NAMEDATALEN)
3346 0 : return;
3347 :
3348 626 : hashentry = hash_search(_state->hash, fname, HASH_ENTER, &found);
3349 :
3350 : /*
3351 : * found being true indicates a duplicate. We don't do anything about
3352 : * that, a later field with the same name overrides the earlier field.
3353 : */
3354 :
3355 626 : hashentry->type = _state->saved_token_type;
3356 626 : Assert(isnull == (hashentry->type == JSON_TOKEN_NULL));
3357 :
3358 626 : if (_state->save_json_start != NULL)
3359 : {
3360 205 : int len = _state->lex->prev_token_terminator - _state->save_json_start;
3361 205 : char *val = palloc((len + 1) * sizeof(char));
3362 :
3363 205 : memcpy(val, _state->save_json_start, len);
3364 205 : val[len] = '\0';
3365 205 : hashentry->val = val;
3366 : }
3367 : else
3368 : {
3369 : /* must have had a scalar instead */
3370 421 : hashentry->val = _state->saved_scalar;
3371 : }
3372 : }
3373 :
3374 : static void
3375 210 : hash_array_start(void *state)
3376 : {
3377 210 : JHashState *_state = (JHashState *) state;
3378 :
3379 210 : if (_state->lex->lex_level == 0)
3380 1 : ereport(ERROR,
3381 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3382 : errmsg("cannot call %s on an array", _state->function_name)));
3383 209 : }
3384 :
3385 : static void
3386 1216 : hash_scalar(void *state, char *token, JsonTokenType tokentype)
3387 : {
3388 1216 : JHashState *_state = (JHashState *) state;
3389 :
3390 1216 : if (_state->lex->lex_level == 0)
3391 2 : ereport(ERROR,
3392 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3393 : errmsg("cannot call %s on a scalar", _state->function_name)));
3394 :
3395 1214 : if (_state->lex->lex_level == 1)
3396 : {
3397 421 : _state->saved_scalar = token;
3398 : /* saved_token_type must already be set in hash_object_field_start() */
3399 421 : Assert(_state->saved_token_type == tokentype);
3400 : }
3401 1214 : }
3402 :
3403 :
3404 : /*
3405 : * SQL function json_populate_recordset
3406 : *
3407 : * set fields in a set of records from the argument json,
3408 : * which must be an array of objects.
3409 : *
3410 : * similar to json_populate_record, but the tuple-building code
3411 : * is pushed down into the semantic action handlers so it's done
3412 : * per object in the array.
3413 : */
3414 : Datum
3415 9 : jsonb_populate_recordset(PG_FUNCTION_ARGS)
3416 : {
3417 9 : return populate_recordset_worker(fcinfo, "jsonb_populate_recordset", true);
3418 : }
3419 :
3420 : Datum
3421 2 : jsonb_to_recordset(PG_FUNCTION_ARGS)
3422 : {
3423 2 : return populate_recordset_worker(fcinfo, "jsonb_to_recordset", false);
3424 : }
3425 :
3426 : Datum
3427 10 : json_populate_recordset(PG_FUNCTION_ARGS)
3428 : {
3429 10 : return populate_recordset_worker(fcinfo, "json_populate_recordset", true);
3430 : }
3431 :
3432 : Datum
3433 3 : json_to_recordset(PG_FUNCTION_ARGS)
3434 : {
3435 3 : return populate_recordset_worker(fcinfo, "json_to_recordset", false);
3436 : }
3437 :
3438 : static void
3439 43 : populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
3440 : {
3441 : HeapTupleData tuple;
3442 43 : HeapTupleHeader tuphead = populate_record(state->ret_tdesc,
3443 : state->my_extra,
3444 : state->rec,
3445 : state->fn_mcxt,
3446 : obj);
3447 :
3448 41 : tuple.t_len = HeapTupleHeaderGetDatumLength(tuphead);
3449 41 : ItemPointerSetInvalid(&(tuple.t_self));
3450 41 : tuple.t_tableOid = InvalidOid;
3451 41 : tuple.t_data = tuphead;
3452 :
3453 41 : tuplestore_puttuple(state->tuple_store, &tuple);
3454 41 : }
3455 :
3456 : /*
3457 : * common worker for json_populate_recordset() and json_to_recordset()
3458 : */
3459 : static Datum
3460 24 : populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
3461 : bool have_record_arg)
3462 : {
3463 24 : int json_arg_num = have_record_arg ? 1 : 0;
3464 24 : Oid jtype = get_fn_expr_argtype(fcinfo->flinfo, json_arg_num);
3465 : ReturnSetInfo *rsi;
3466 : MemoryContext old_cxt;
3467 : HeapTupleHeader rec;
3468 : TupleDesc tupdesc;
3469 : PopulateRecordsetState *state;
3470 :
3471 24 : if (have_record_arg)
3472 : {
3473 19 : Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
3474 :
3475 19 : if (!type_is_rowtype(argtype))
3476 0 : ereport(ERROR,
3477 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3478 : errmsg("first argument of %s must be a row type",
3479 : funcname)));
3480 : }
3481 :
3482 24 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
3483 :
3484 48 : if (!rsi || !IsA(rsi, ReturnSetInfo) ||
3485 48 : (rsi->allowedModes & SFRM_Materialize) == 0 ||
3486 24 : rsi->expectedDesc == NULL)
3487 0 : ereport(ERROR,
3488 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3489 : errmsg("set-valued function called in context that "
3490 : "cannot accept a set")));
3491 :
3492 24 : rsi->returnMode = SFRM_Materialize;
3493 :
3494 : /*
3495 : * get the tupdesc from the result set info - it must be a record type
3496 : * because we already checked that arg1 is a record type, or we're in a
3497 : * to_record function which returns a setof record.
3498 : */
3499 24 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
3500 0 : ereport(ERROR,
3501 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3502 : errmsg("function returning record called in context "
3503 : "that cannot accept type record")));
3504 :
3505 : /* if the json is null send back an empty set */
3506 24 : if (PG_ARGISNULL(json_arg_num))
3507 0 : PG_RETURN_NULL();
3508 :
3509 24 : if (!have_record_arg || PG_ARGISNULL(0))
3510 12 : rec = NULL;
3511 : else
3512 12 : rec = PG_GETARG_HEAPTUPLEHEADER(0);
3513 :
3514 24 : state = palloc0(sizeof(PopulateRecordsetState));
3515 :
3516 : /* make these in a sufficiently long-lived memory context */
3517 24 : old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
3518 24 : state->ret_tdesc = CreateTupleDescCopy(tupdesc);
3519 24 : BlessTupleDesc(state->ret_tdesc);
3520 24 : state->tuple_store = tuplestore_begin_heap(rsi->allowedModes &
3521 : SFRM_Materialize_Random,
3522 : false, work_mem);
3523 24 : MemoryContextSwitchTo(old_cxt);
3524 :
3525 24 : state->function_name = funcname;
3526 24 : state->my_extra = (RecordIOData **) &fcinfo->flinfo->fn_extra;
3527 24 : state->rec = rec;
3528 24 : state->fn_mcxt = fcinfo->flinfo->fn_mcxt;
3529 :
3530 24 : if (jtype == JSONOID)
3531 : {
3532 13 : text *json = PG_GETARG_TEXT_PP(json_arg_num);
3533 : JsonLexContext *lex;
3534 : JsonSemAction *sem;
3535 :
3536 13 : sem = palloc0(sizeof(JsonSemAction));
3537 :
3538 13 : lex = makeJsonLexContext(json, true);
3539 :
3540 13 : sem->semstate = (void *) state;
3541 13 : sem->array_start = populate_recordset_array_start;
3542 13 : sem->array_element_start = populate_recordset_array_element_start;
3543 13 : sem->scalar = populate_recordset_scalar;
3544 13 : sem->object_field_start = populate_recordset_object_field_start;
3545 13 : sem->object_field_end = populate_recordset_object_field_end;
3546 13 : sem->object_start = populate_recordset_object_start;
3547 13 : sem->object_end = populate_recordset_object_end;
3548 :
3549 13 : state->lex = lex;
3550 :
3551 13 : pg_parse_json(lex, sem);
3552 : }
3553 : else
3554 : {
3555 11 : Jsonb *jb = PG_GETARG_JSONB(json_arg_num);
3556 : JsonbIterator *it;
3557 : JsonbValue v;
3558 11 : bool skipNested = false;
3559 : JsonbIteratorToken r;
3560 :
3561 11 : Assert(jtype == JSONBOID);
3562 :
3563 11 : if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
3564 0 : ereport(ERROR,
3565 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3566 : errmsg("cannot call %s on a non-array",
3567 : funcname)));
3568 :
3569 11 : it = JsonbIteratorInit(&jb->root);
3570 :
3571 62 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
3572 : {
3573 41 : skipNested = true;
3574 :
3575 41 : if (r == WJB_ELEM)
3576 : {
3577 : JsObject obj;
3578 :
3579 40 : if (v.type != jbvBinary ||
3580 20 : !JsonContainerIsObject(v.val.binary.data))
3581 0 : ereport(ERROR,
3582 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3583 : errmsg("argument of %s must be an array of objects",
3584 : funcname)));
3585 :
3586 20 : obj.is_json = false;
3587 20 : obj.val.jsonb_cont = v.val.binary.data;
3588 :
3589 20 : populate_recordset_record(state, &obj);
3590 : }
3591 : }
3592 : }
3593 :
3594 22 : rsi->setResult = state->tuple_store;
3595 22 : rsi->setDesc = state->ret_tdesc;
3596 :
3597 22 : PG_RETURN_NULL();
3598 : }
3599 :
3600 : static void
3601 29 : populate_recordset_object_start(void *state)
3602 : {
3603 29 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
3604 29 : int lex_level = _state->lex->lex_level;
3605 : HASHCTL ctl;
3606 :
3607 : /* Reject object at top level: we must have an array at level 0 */
3608 29 : if (lex_level == 0)
3609 0 : ereport(ERROR,
3610 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3611 : errmsg("cannot call %s on an object",
3612 : _state->function_name)));
3613 :
3614 : /* Nested objects require no special processing */
3615 29 : if (lex_level > 1)
3616 35 : return;
3617 :
3618 : /* Object at level 1: set up a new hash table for this object */
3619 23 : memset(&ctl, 0, sizeof(ctl));
3620 23 : ctl.keysize = NAMEDATALEN;
3621 23 : ctl.entrysize = sizeof(JsonHashEntry);
3622 23 : ctl.hcxt = CurrentMemoryContext;
3623 23 : _state->json_hash = hash_create("json object hashtable",
3624 : 100,
3625 : &ctl,
3626 : HASH_ELEM | HASH_CONTEXT);
3627 : }
3628 :
3629 : static void
3630 29 : populate_recordset_object_end(void *state)
3631 : {
3632 29 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
3633 : JsObject obj;
3634 :
3635 : /* Nested objects require no special processing */
3636 29 : if (_state->lex->lex_level > 1)
3637 34 : return;
3638 :
3639 23 : obj.is_json = true;
3640 23 : obj.val.json_hash = _state->json_hash;
3641 :
3642 : /* Otherwise, construct and return a tuple based on this level-1 object */
3643 23 : populate_recordset_record(_state, &obj);
3644 :
3645 : /* Done with hash for this object */
3646 22 : hash_destroy(_state->json_hash);
3647 22 : _state->json_hash = NULL;
3648 : }
3649 :
3650 : static void
3651 32 : populate_recordset_array_element_start(void *state, bool isnull)
3652 : {
3653 32 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
3654 :
3655 55 : if (_state->lex->lex_level == 1 &&
3656 23 : _state->lex->token_type != JSON_TOKEN_OBJECT_START)
3657 0 : ereport(ERROR,
3658 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3659 : errmsg("argument of %s must be an array of objects",
3660 : _state->function_name)));
3661 32 : }
3662 :
3663 : static void
3664 16 : populate_recordset_array_start(void *state)
3665 : {
3666 : /* nothing to do */
3667 16 : }
3668 :
3669 : static void
3670 62 : populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype)
3671 : {
3672 62 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
3673 :
3674 62 : if (_state->lex->lex_level == 0)
3675 0 : ereport(ERROR,
3676 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3677 : errmsg("cannot call %s on a scalar",
3678 : _state->function_name)));
3679 :
3680 62 : if (_state->lex->lex_level == 2)
3681 46 : _state->saved_scalar = token;
3682 62 : }
3683 :
3684 : static void
3685 62 : populate_recordset_object_field_start(void *state, char *fname, bool isnull)
3686 : {
3687 62 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
3688 :
3689 62 : if (_state->lex->lex_level > 2)
3690 69 : return;
3691 :
3692 55 : _state->saved_token_type = _state->lex->token_type;
3693 :
3694 107 : if (_state->lex->token_type == JSON_TOKEN_ARRAY_START ||
3695 52 : _state->lex->token_type == JSON_TOKEN_OBJECT_START)
3696 : {
3697 9 : _state->save_json_start = _state->lex->token_start;
3698 : }
3699 : else
3700 : {
3701 46 : _state->save_json_start = NULL;
3702 : }
3703 : }
3704 :
3705 : static void
3706 62 : populate_recordset_object_field_end(void *state, char *fname, bool isnull)
3707 : {
3708 62 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
3709 : JsonHashEntry *hashentry;
3710 : bool found;
3711 :
3712 : /*
3713 : * Ignore nested fields.
3714 : */
3715 62 : if (_state->lex->lex_level > 2)
3716 14 : return;
3717 :
3718 : /*
3719 : * Ignore field names >= NAMEDATALEN - they can't match a record field.
3720 : * (Note: without this test, the hash code would truncate the string at
3721 : * NAMEDATALEN-1, and could then match against a similarly-truncated
3722 : * record field name. That would be a reasonable behavior, but this code
3723 : * has previously insisted on exact equality, so we keep this behavior.)
3724 : */
3725 55 : if (strlen(fname) >= NAMEDATALEN)
3726 0 : return;
3727 :
3728 55 : hashentry = hash_search(_state->json_hash, fname, HASH_ENTER, &found);
3729 :
3730 : /*
3731 : * found being true indicates a duplicate. We don't do anything about
3732 : * that, a later field with the same name overrides the earlier field.
3733 : */
3734 :
3735 55 : hashentry->type = _state->saved_token_type;
3736 55 : Assert(isnull == (hashentry->type == JSON_TOKEN_NULL));
3737 :
3738 55 : if (_state->save_json_start != NULL)
3739 : {
3740 9 : int len = _state->lex->prev_token_terminator - _state->save_json_start;
3741 9 : char *val = palloc((len + 1) * sizeof(char));
3742 :
3743 9 : memcpy(val, _state->save_json_start, len);
3744 9 : val[len] = '\0';
3745 9 : hashentry->val = val;
3746 : }
3747 : else
3748 : {
3749 : /* must have had a scalar instead */
3750 46 : hashentry->val = _state->saved_scalar;
3751 : }
3752 : }
3753 :
3754 : /*
3755 : * findJsonbValueFromContainer() wrapper that sets up JsonbValue key string.
3756 : */
3757 : static JsonbValue *
3758 6601 : findJsonbValueFromContainerLen(JsonbContainer *container, uint32 flags,
3759 : char *key, uint32 keylen)
3760 : {
3761 : JsonbValue k;
3762 :
3763 6601 : k.type = jbvString;
3764 6601 : k.val.string.val = key;
3765 6601 : k.val.string.len = keylen;
3766 :
3767 6601 : return findJsonbValueFromContainer(container, flags, &k);
3768 : }
3769 :
3770 : /*
3771 : * Semantic actions for json_strip_nulls.
3772 : *
3773 : * Simply repeat the input on the output unless we encounter
3774 : * a null object field. State for this is set when the field
3775 : * is started and reset when the scalar action (which must be next)
3776 : * is called.
3777 : */
3778 :
3779 : static void
3780 6 : sn_object_start(void *state)
3781 : {
3782 6 : StripnullState *_state = (StripnullState *) state;
3783 :
3784 6 : appendStringInfoCharMacro(_state->strval, '{');
3785 6 : }
3786 :
3787 : static void
3788 6 : sn_object_end(void *state)
3789 : {
3790 6 : StripnullState *_state = (StripnullState *) state;
3791 :
3792 6 : appendStringInfoCharMacro(_state->strval, '}');
3793 6 : }
3794 :
3795 : static void
3796 3 : sn_array_start(void *state)
3797 : {
3798 3 : StripnullState *_state = (StripnullState *) state;
3799 :
3800 3 : appendStringInfoCharMacro(_state->strval, '[');
3801 3 : }
3802 :
3803 : static void
3804 3 : sn_array_end(void *state)
3805 : {
3806 3 : StripnullState *_state = (StripnullState *) state;
3807 :
3808 3 : appendStringInfoCharMacro(_state->strval, ']');
3809 3 : }
3810 :
3811 : static void
3812 13 : sn_object_field_start(void *state, char *fname, bool isnull)
3813 : {
3814 13 : StripnullState *_state = (StripnullState *) state;
3815 :
3816 13 : if (isnull)
3817 : {
3818 : /*
3819 : * The next thing must be a scalar or isnull couldn't be true, so
3820 : * there is no danger of this state being carried down into a nested
3821 : * object or array. The flag will be reset in the scalar action.
3822 : */
3823 5 : _state->skip_next_null = true;
3824 18 : return;
3825 : }
3826 :
3827 8 : if (_state->strval->data[_state->strval->len - 1] != '{')
3828 4 : appendStringInfoCharMacro(_state->strval, ',');
3829 :
3830 : /*
3831 : * Unfortunately we don't have the quoted and escaped string any more, so
3832 : * we have to re-escape it.
3833 : */
3834 8 : escape_json(_state->strval, fname);
3835 :
3836 8 : appendStringInfoCharMacro(_state->strval, ':');
3837 : }
3838 :
3839 : static void
3840 11 : sn_array_element_start(void *state, bool isnull)
3841 : {
3842 11 : StripnullState *_state = (StripnullState *) state;
3843 :
3844 11 : if (_state->strval->data[_state->strval->len - 1] != '[')
3845 8 : appendStringInfoCharMacro(_state->strval, ',');
3846 11 : }
3847 :
3848 : static void
3849 22 : sn_scalar(void *state, char *token, JsonTokenType tokentype)
3850 : {
3851 22 : StripnullState *_state = (StripnullState *) state;
3852 :
3853 22 : if (_state->skip_next_null)
3854 : {
3855 5 : Assert(tokentype == JSON_TOKEN_NULL);
3856 5 : _state->skip_next_null = false;
3857 27 : return;
3858 : }
3859 :
3860 17 : if (tokentype == JSON_TOKEN_STRING)
3861 1 : escape_json(_state->strval, token);
3862 : else
3863 16 : appendStringInfoString(_state->strval, token);
3864 : }
3865 :
3866 : /*
3867 : * SQL function json_strip_nulls(json) -> json
3868 : */
3869 : Datum
3870 7 : json_strip_nulls(PG_FUNCTION_ARGS)
3871 : {
3872 7 : text *json = PG_GETARG_TEXT_PP(0);
3873 : StripnullState *state;
3874 : JsonLexContext *lex;
3875 : JsonSemAction *sem;
3876 :
3877 7 : lex = makeJsonLexContext(json, true);
3878 7 : state = palloc0(sizeof(StripnullState));
3879 7 : sem = palloc0(sizeof(JsonSemAction));
3880 :
3881 7 : state->strval = makeStringInfo();
3882 7 : state->skip_next_null = false;
3883 7 : state->lex = lex;
3884 :
3885 7 : sem->semstate = (void *) state;
3886 7 : sem->object_start = sn_object_start;
3887 7 : sem->object_end = sn_object_end;
3888 7 : sem->array_start = sn_array_start;
3889 7 : sem->array_end = sn_array_end;
3890 7 : sem->scalar = sn_scalar;
3891 7 : sem->array_element_start = sn_array_element_start;
3892 7 : sem->object_field_start = sn_object_field_start;
3893 :
3894 7 : pg_parse_json(lex, sem);
3895 :
3896 7 : PG_RETURN_TEXT_P(cstring_to_text_with_len(state->strval->data,
3897 : state->strval->len));
3898 :
3899 : }
3900 :
3901 : /*
3902 : * SQL function jsonb_strip_nulls(jsonb) -> jsonb
3903 : */
3904 : Datum
3905 7 : jsonb_strip_nulls(PG_FUNCTION_ARGS)
3906 : {
3907 7 : Jsonb *jb = PG_GETARG_JSONB(0);
3908 : JsonbIterator *it;
3909 7 : JsonbParseState *parseState = NULL;
3910 7 : JsonbValue *res = NULL;
3911 : JsonbValue v,
3912 : k;
3913 : JsonbIteratorToken type;
3914 7 : bool last_was_key = false;
3915 :
3916 7 : if (JB_ROOT_IS_SCALAR(jb))
3917 3 : PG_RETURN_POINTER(jb);
3918 :
3919 4 : it = JsonbIteratorInit(&jb->root);
3920 :
3921 58 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
3922 : {
3923 50 : Assert(!(type == WJB_KEY && last_was_key));
3924 :
3925 50 : if (type == WJB_KEY)
3926 : {
3927 : /* stash the key until we know if it has a null value */
3928 13 : k = v;
3929 13 : last_was_key = true;
3930 13 : continue;
3931 : }
3932 :
3933 37 : if (last_was_key)
3934 : {
3935 : /* if the last element was a key this one can't be */
3936 13 : last_was_key = false;
3937 :
3938 : /* skip this field if value is null */
3939 13 : if (type == WJB_VALUE && v.type == jbvNull)
3940 5 : continue;
3941 :
3942 : /* otherwise, do a delayed push of the key */
3943 8 : (void) pushJsonbValue(&parseState, WJB_KEY, &k);
3944 : }
3945 :
3946 32 : if (type == WJB_VALUE || type == WJB_ELEM)
3947 14 : res = pushJsonbValue(&parseState, type, &v);
3948 : else
3949 18 : res = pushJsonbValue(&parseState, type, NULL);
3950 : }
3951 :
3952 4 : Assert(res != NULL);
3953 :
3954 4 : PG_RETURN_POINTER(JsonbValueToJsonb(res));
3955 : }
3956 :
3957 : /*
3958 : * Add values from the jsonb to the parse state.
3959 : *
3960 : * If the parse state container is an object, the jsonb is pushed as
3961 : * a value, not a key.
3962 : *
3963 : * This needs to be done using an iterator because pushJsonbValue doesn't
3964 : * like getting jbvBinary values, so we can't just push jb as a whole.
3965 : */
3966 : static void
3967 37 : addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
3968 : {
3969 : JsonbIterator *it;
3970 37 : JsonbValue *o = &(*jbps)->contVal;
3971 : JsonbValue v;
3972 : JsonbIteratorToken type;
3973 :
3974 37 : it = JsonbIteratorInit(&jb->root);
3975 :
3976 37 : Assert(o->type == jbvArray || o->type == jbvObject);
3977 :
3978 37 : if (JB_ROOT_IS_SCALAR(jb))
3979 : {
3980 19 : (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
3981 19 : (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
3982 :
3983 19 : switch (o->type)
3984 : {
3985 : case jbvArray:
3986 17 : (void) pushJsonbValue(jbps, WJB_ELEM, &v);
3987 17 : break;
3988 : case jbvObject:
3989 2 : (void) pushJsonbValue(jbps, WJB_VALUE, &v);
3990 2 : break;
3991 : default:
3992 0 : elog(ERROR, "unexpected parent of nested structure");
3993 : }
3994 : }
3995 : else
3996 : {
3997 112 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
3998 : {
3999 76 : if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
4000 40 : (void) pushJsonbValue(jbps, type, &v);
4001 : else
4002 36 : (void) pushJsonbValue(jbps, type, NULL);
4003 : }
4004 : }
4005 :
4006 37 : }
4007 :
4008 : /*
4009 : * SQL function jsonb_pretty (jsonb)
4010 : *
4011 : * Pretty-printed text for the jsonb
4012 : */
4013 : Datum
4014 3 : jsonb_pretty(PG_FUNCTION_ARGS)
4015 : {
4016 3 : Jsonb *jb = PG_GETARG_JSONB(0);
4017 3 : StringInfo str = makeStringInfo();
4018 :
4019 3 : JsonbToCStringIndent(str, &jb->root, VARSIZE(jb));
4020 :
4021 3 : PG_RETURN_TEXT_P(cstring_to_text_with_len(str->data, str->len));
4022 : }
4023 :
4024 : /*
4025 : * SQL function jsonb_concat (jsonb, jsonb)
4026 : *
4027 : * function for || operator
4028 : */
4029 : Datum
4030 25 : jsonb_concat(PG_FUNCTION_ARGS)
4031 : {
4032 25 : Jsonb *jb1 = PG_GETARG_JSONB(0);
4033 25 : Jsonb *jb2 = PG_GETARG_JSONB(1);
4034 25 : JsonbParseState *state = NULL;
4035 : JsonbValue *res;
4036 : JsonbIterator *it1,
4037 : *it2;
4038 :
4039 : /*
4040 : * If one of the jsonb is empty, just return the other if it's not scalar
4041 : * and both are of the same kind. If it's a scalar or they are of
4042 : * different kinds we need to perform the concatenation even if one is
4043 : * empty.
4044 : */
4045 25 : if (JB_ROOT_IS_OBJECT(jb1) == JB_ROOT_IS_OBJECT(jb2))
4046 : {
4047 19 : if (JB_ROOT_COUNT(jb1) == 0 && !JB_ROOT_IS_SCALAR(jb2))
4048 5 : PG_RETURN_JSONB(jb2);
4049 14 : else if (JB_ROOT_COUNT(jb2) == 0 && !JB_ROOT_IS_SCALAR(jb1))
4050 2 : PG_RETURN_JSONB(jb1);
4051 : }
4052 :
4053 18 : it1 = JsonbIteratorInit(&jb1->root);
4054 18 : it2 = JsonbIteratorInit(&jb2->root);
4055 :
4056 18 : res = IteratorConcat(&it1, &it2, &state);
4057 :
4058 16 : Assert(res != NULL);
4059 :
4060 16 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4061 : }
4062 :
4063 :
4064 : /*
4065 : * SQL function jsonb_delete (jsonb, text)
4066 : *
4067 : * return a copy of the jsonb with the indicated item
4068 : * removed.
4069 : */
4070 : Datum
4071 14 : jsonb_delete(PG_FUNCTION_ARGS)
4072 : {
4073 14 : Jsonb *in = PG_GETARG_JSONB(0);
4074 14 : text *key = PG_GETARG_TEXT_PP(1);
4075 14 : char *keyptr = VARDATA_ANY(key);
4076 14 : int keylen = VARSIZE_ANY_EXHDR(key);
4077 14 : JsonbParseState *state = NULL;
4078 : JsonbIterator *it;
4079 : JsonbValue v,
4080 14 : *res = NULL;
4081 14 : bool skipNested = false;
4082 : JsonbIteratorToken r;
4083 :
4084 14 : if (JB_ROOT_IS_SCALAR(in))
4085 1 : ereport(ERROR,
4086 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4087 : errmsg("cannot delete from scalar")));
4088 :
4089 13 : if (JB_ROOT_COUNT(in) == 0)
4090 2 : PG_RETURN_JSONB(in);
4091 :
4092 11 : it = JsonbIteratorInit(&in->root);
4093 :
4094 101 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != 0)
4095 : {
4096 79 : skipNested = true;
4097 :
4098 112 : if ((r == WJB_ELEM || r == WJB_KEY) &&
4099 99 : (v.type == jbvString && keylen == v.val.string.len &&
4100 33 : memcmp(keyptr, v.val.string.val, keylen) == 0))
4101 : {
4102 : /* skip corresponding value as well */
4103 9 : if (r == WJB_KEY)
4104 9 : JsonbIteratorNext(&it, &v, true);
4105 :
4106 9 : continue;
4107 : }
4108 :
4109 70 : res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4110 : }
4111 :
4112 11 : Assert(res != NULL);
4113 :
4114 11 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4115 : }
4116 :
4117 : /*
4118 : * SQL function jsonb_delete (jsonb, variadic text[])
4119 : *
4120 : * return a copy of the jsonb with the indicated items
4121 : * removed.
4122 : */
4123 : Datum
4124 3 : jsonb_delete_array(PG_FUNCTION_ARGS)
4125 : {
4126 3 : Jsonb *in = PG_GETARG_JSONB(0);
4127 3 : ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
4128 : Datum *keys_elems;
4129 : bool *keys_nulls;
4130 : int keys_len;
4131 3 : JsonbParseState *state = NULL;
4132 : JsonbIterator *it;
4133 : JsonbValue v,
4134 3 : *res = NULL;
4135 3 : bool skipNested = false;
4136 : JsonbIteratorToken r;
4137 :
4138 3 : if (ARR_NDIM(keys) > 1)
4139 0 : ereport(ERROR,
4140 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4141 : errmsg("wrong number of array subscripts")));
4142 :
4143 3 : if (JB_ROOT_IS_SCALAR(in))
4144 0 : ereport(ERROR,
4145 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4146 : errmsg("cannot delete from scalar")));
4147 :
4148 3 : if (JB_ROOT_COUNT(in) == 0)
4149 0 : PG_RETURN_JSONB(in);
4150 :
4151 3 : deconstruct_array(keys, TEXTOID, -1, false, 'i',
4152 : &keys_elems, &keys_nulls, &keys_len);
4153 :
4154 3 : if (keys_len == 0)
4155 1 : PG_RETURN_JSONB(in);
4156 :
4157 2 : it = JsonbIteratorInit(&in->root);
4158 :
4159 17 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != 0)
4160 : {
4161 13 : skipNested = true;
4162 :
4163 13 : if ((r == WJB_ELEM || r == WJB_KEY) && v.type == jbvString)
4164 : {
4165 : int i;
4166 6 : bool found = false;
4167 :
4168 11 : for (i = 0; i < keys_len; i++)
4169 : {
4170 : char *keyptr;
4171 : int keylen;
4172 :
4173 8 : if (keys_nulls[i])
4174 0 : continue;
4175 :
4176 8 : keyptr = VARDATA_ANY(keys_elems[i]);
4177 8 : keylen = VARSIZE_ANY_EXHDR(keys_elems[i]);
4178 16 : if (keylen == v.val.string.len &&
4179 8 : memcmp(keyptr, v.val.string.val, keylen) == 0)
4180 : {
4181 3 : found = true;
4182 3 : break;
4183 : }
4184 : }
4185 6 : if (found)
4186 : {
4187 : /* skip corresponding value as well */
4188 3 : if (r == WJB_KEY)
4189 3 : JsonbIteratorNext(&it, &v, true);
4190 :
4191 3 : continue;
4192 : }
4193 : }
4194 :
4195 10 : res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4196 : }
4197 :
4198 2 : Assert(res != NULL);
4199 :
4200 2 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4201 : }
4202 :
4203 : /*
4204 : * SQL function jsonb_delete (jsonb, int)
4205 : *
4206 : * return a copy of the jsonb with the indicated item
4207 : * removed. Negative int means count back from the
4208 : * end of the items.
4209 : */
4210 : Datum
4211 11 : jsonb_delete_idx(PG_FUNCTION_ARGS)
4212 : {
4213 11 : Jsonb *in = PG_GETARG_JSONB(0);
4214 11 : int idx = PG_GETARG_INT32(1);
4215 11 : JsonbParseState *state = NULL;
4216 : JsonbIterator *it;
4217 11 : uint32 i = 0,
4218 : n;
4219 : JsonbValue v,
4220 11 : *res = NULL;
4221 : JsonbIteratorToken r;
4222 :
4223 11 : if (JB_ROOT_IS_SCALAR(in))
4224 1 : ereport(ERROR,
4225 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4226 : errmsg("cannot delete from scalar")));
4227 :
4228 10 : if (JB_ROOT_IS_OBJECT(in))
4229 1 : ereport(ERROR,
4230 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4231 : errmsg("cannot delete from object using integer index")));
4232 :
4233 9 : if (JB_ROOT_COUNT(in) == 0)
4234 1 : PG_RETURN_JSONB(in);
4235 :
4236 8 : it = JsonbIteratorInit(&in->root);
4237 :
4238 8 : r = JsonbIteratorNext(&it, &v, false);
4239 8 : Assert(r == WJB_BEGIN_ARRAY);
4240 8 : n = v.val.array.nElems;
4241 :
4242 8 : if (idx < 0)
4243 : {
4244 4 : if (-idx > n)
4245 1 : idx = n;
4246 : else
4247 3 : idx = n + idx;
4248 : }
4249 :
4250 8 : if (idx >= n)
4251 2 : PG_RETURN_JSONB(in);
4252 :
4253 6 : pushJsonbValue(&state, r, NULL);
4254 :
4255 36 : while ((r = JsonbIteratorNext(&it, &v, true)) != 0)
4256 : {
4257 24 : if (r == WJB_ELEM)
4258 : {
4259 18 : if (i++ == idx)
4260 6 : continue;
4261 : }
4262 :
4263 18 : res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4264 : }
4265 :
4266 6 : Assert(res != NULL);
4267 :
4268 6 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4269 : }
4270 :
4271 : /*
4272 : * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
4273 : *
4274 : */
4275 : Datum
4276 27 : jsonb_set(PG_FUNCTION_ARGS)
4277 : {
4278 27 : Jsonb *in = PG_GETARG_JSONB(0);
4279 27 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
4280 27 : Jsonb *newval = PG_GETARG_JSONB(2);
4281 27 : bool create = PG_GETARG_BOOL(3);
4282 27 : JsonbValue *res = NULL;
4283 : Datum *path_elems;
4284 : bool *path_nulls;
4285 : int path_len;
4286 : JsonbIterator *it;
4287 27 : JsonbParseState *st = NULL;
4288 :
4289 27 : if (ARR_NDIM(path) > 1)
4290 0 : ereport(ERROR,
4291 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4292 : errmsg("wrong number of array subscripts")));
4293 :
4294 27 : if (JB_ROOT_IS_SCALAR(in))
4295 1 : ereport(ERROR,
4296 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4297 : errmsg("cannot set path in scalar")));
4298 :
4299 26 : if (JB_ROOT_COUNT(in) == 0 && !create)
4300 2 : PG_RETURN_JSONB(in);
4301 :
4302 24 : deconstruct_array(path, TEXTOID, -1, false, 'i',
4303 : &path_elems, &path_nulls, &path_len);
4304 :
4305 24 : if (path_len == 0)
4306 0 : PG_RETURN_JSONB(in);
4307 :
4308 24 : it = JsonbIteratorInit(&in->root);
4309 :
4310 24 : res = setPath(&it, path_elems, path_nulls, path_len, &st,
4311 : 0, newval, create ? JB_PATH_CREATE : JB_PATH_REPLACE);
4312 :
4313 19 : Assert(res != NULL);
4314 :
4315 19 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4316 : }
4317 :
4318 :
4319 : /*
4320 : * SQL function jsonb_delete_path(jsonb, text[])
4321 : */
4322 : Datum
4323 10 : jsonb_delete_path(PG_FUNCTION_ARGS)
4324 : {
4325 10 : Jsonb *in = PG_GETARG_JSONB(0);
4326 10 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
4327 10 : JsonbValue *res = NULL;
4328 : Datum *path_elems;
4329 : bool *path_nulls;
4330 : int path_len;
4331 : JsonbIterator *it;
4332 10 : JsonbParseState *st = NULL;
4333 :
4334 10 : if (ARR_NDIM(path) > 1)
4335 0 : ereport(ERROR,
4336 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4337 : errmsg("wrong number of array subscripts")));
4338 :
4339 10 : if (JB_ROOT_IS_SCALAR(in))
4340 1 : ereport(ERROR,
4341 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4342 : errmsg("cannot delete path in scalar")));
4343 :
4344 9 : if (JB_ROOT_COUNT(in) == 0)
4345 2 : PG_RETURN_JSONB(in);
4346 :
4347 7 : deconstruct_array(path, TEXTOID, -1, false, 'i',
4348 : &path_elems, &path_nulls, &path_len);
4349 :
4350 7 : if (path_len == 0)
4351 0 : PG_RETURN_JSONB(in);
4352 :
4353 7 : it = JsonbIteratorInit(&in->root);
4354 :
4355 7 : res = setPath(&it, path_elems, path_nulls, path_len, &st,
4356 : 0, NULL, JB_PATH_DELETE);
4357 :
4358 6 : Assert(res != NULL);
4359 :
4360 6 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4361 : }
4362 :
4363 : /*
4364 : * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
4365 : *
4366 : */
4367 : Datum
4368 22 : jsonb_insert(PG_FUNCTION_ARGS)
4369 : {
4370 22 : Jsonb *in = PG_GETARG_JSONB(0);
4371 22 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
4372 22 : Jsonb *newval = PG_GETARG_JSONB(2);
4373 22 : bool after = PG_GETARG_BOOL(3);
4374 22 : JsonbValue *res = NULL;
4375 : Datum *path_elems;
4376 : bool *path_nulls;
4377 : int path_len;
4378 : JsonbIterator *it;
4379 22 : JsonbParseState *st = NULL;
4380 :
4381 22 : if (ARR_NDIM(path) > 1)
4382 0 : ereport(ERROR,
4383 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4384 : errmsg("wrong number of array subscripts")));
4385 :
4386 22 : if (JB_ROOT_IS_SCALAR(in))
4387 0 : ereport(ERROR,
4388 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4389 : errmsg("cannot set path in scalar")));
4390 :
4391 22 : deconstruct_array(path, TEXTOID, -1, false, 'i',
4392 : &path_elems, &path_nulls, &path_len);
4393 :
4394 22 : if (path_len == 0)
4395 0 : PG_RETURN_JSONB(in);
4396 :
4397 22 : it = JsonbIteratorInit(&in->root);
4398 :
4399 22 : res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newval,
4400 : after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
4401 :
4402 20 : Assert(res != NULL);
4403 :
4404 20 : PG_RETURN_JSONB(JsonbValueToJsonb(res));
4405 : }
4406 :
4407 : /*
4408 : * Iterate over all jsonb objects and merge them into one.
4409 : * The logic of this function copied from the same hstore function,
4410 : * except the case, when it1 & it2 represents jbvObject.
4411 : * In that case we just append the content of it2 to it1 without any
4412 : * verifications.
4413 : */
4414 : static JsonbValue *
4415 18 : IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
4416 : JsonbParseState **state)
4417 : {
4418 : JsonbValue v1,
4419 : v2,
4420 18 : *res = NULL;
4421 : JsonbIteratorToken r1,
4422 : r2,
4423 : rk1,
4424 : rk2;
4425 :
4426 18 : r1 = rk1 = JsonbIteratorNext(it1, &v1, false);
4427 18 : r2 = rk2 = JsonbIteratorNext(it2, &v2, false);
4428 :
4429 : /*
4430 : * Both elements are objects.
4431 : */
4432 23 : if (rk1 == WJB_BEGIN_OBJECT && rk2 == WJB_BEGIN_OBJECT)
4433 : {
4434 : /*
4435 : * Append the all tokens from v1 to res, except last WJB_END_OBJECT
4436 : * (because res will not be finished yet).
4437 : */
4438 5 : pushJsonbValue(state, r1, NULL);
4439 34 : while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_OBJECT)
4440 24 : pushJsonbValue(state, r1, &v1);
4441 :
4442 : /*
4443 : * Append the all tokens from v2 to res, include last WJB_END_OBJECT
4444 : * (the concatenation will be completed).
4445 : */
4446 31 : while ((r2 = JsonbIteratorNext(it2, &v2, true)) != 0)
4447 21 : res = pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
4448 : }
4449 :
4450 : /*
4451 : * Both elements are arrays (either can be scalar).
4452 : */
4453 13 : else if (rk1 == WJB_BEGIN_ARRAY && rk2 == WJB_BEGIN_ARRAY)
4454 : {
4455 7 : pushJsonbValue(state, r1, NULL);
4456 :
4457 23 : while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
4458 : {
4459 9 : Assert(r1 == WJB_ELEM);
4460 9 : pushJsonbValue(state, r1, &v1);
4461 : }
4462 :
4463 24 : while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_END_ARRAY)
4464 : {
4465 10 : Assert(r2 == WJB_ELEM);
4466 10 : pushJsonbValue(state, WJB_ELEM, &v2);
4467 : }
4468 :
4469 7 : res = pushJsonbValue(state, WJB_END_ARRAY, NULL /* signal to sort */ );
4470 : }
4471 : /* have we got array || object or object || array? */
4472 6 : else if (((rk1 == WJB_BEGIN_ARRAY && !(*it1)->isScalar) && rk2 == WJB_BEGIN_OBJECT) ||
4473 3 : (rk1 == WJB_BEGIN_OBJECT && (rk2 == WJB_BEGIN_ARRAY && !(*it2)->isScalar)))
4474 4 : {
4475 :
4476 4 : JsonbIterator **it_array = rk1 == WJB_BEGIN_ARRAY ? it1 : it2;
4477 4 : JsonbIterator **it_object = rk1 == WJB_BEGIN_OBJECT ? it1 : it2;
4478 :
4479 4 : bool prepend = (rk1 == WJB_BEGIN_OBJECT);
4480 :
4481 4 : pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
4482 :
4483 4 : if (prepend)
4484 : {
4485 2 : pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
4486 10 : while ((r1 = JsonbIteratorNext(it_object, &v1, true)) != 0)
4487 6 : pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
4488 :
4489 8 : while ((r2 = JsonbIteratorNext(it_array, &v2, true)) != 0)
4490 4 : res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
4491 : }
4492 : else
4493 : {
4494 6 : while ((r1 = JsonbIteratorNext(it_array, &v1, true)) != WJB_END_ARRAY)
4495 2 : pushJsonbValue(state, r1, &v1);
4496 :
4497 2 : pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
4498 10 : while ((r2 = JsonbIteratorNext(it_object, &v2, true)) != 0)
4499 6 : pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
4500 :
4501 2 : res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
4502 : }
4503 : }
4504 : else
4505 : {
4506 : /*
4507 : * This must be scalar || object or object || scalar, as that's all
4508 : * that's left. Both of these make no sense, so error out.
4509 : */
4510 2 : ereport(ERROR,
4511 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4512 : errmsg("invalid concatenation of jsonb objects")));
4513 : }
4514 :
4515 16 : return res;
4516 : }
4517 :
4518 : /*
4519 : * Do most of the heavy work for jsonb_set/jsonb_insert
4520 : *
4521 : * If JB_PATH_DELETE bit is set in op_type, the element is to be removed.
4522 : *
4523 : * If any bit mentioned in JB_PATH_CREATE_OR_INSERT is set in op_type,
4524 : * we create the new value if the key or array index does not exist.
4525 : *
4526 : * Bits JB_PATH_INSERT_BEFORE and JB_PATH_INSERT_AFTER in op_type
4527 : * behave as JB_PATH_CREATE if new value is inserted in JsonbObject.
4528 : *
4529 : * All path elements before the last must already exist
4530 : * whatever bits in op_type are set, or nothing is done.
4531 : */
4532 : static JsonbValue *
4533 104 : setPath(JsonbIterator **it, Datum *path_elems,
4534 : bool *path_nulls, int path_len,
4535 : JsonbParseState **st, int level, Jsonb *newval, int op_type)
4536 : {
4537 : JsonbValue v;
4538 : JsonbIteratorToken r;
4539 : JsonbValue *res;
4540 :
4541 104 : check_stack_depth();
4542 :
4543 104 : if (path_nulls[level])
4544 3 : ereport(ERROR,
4545 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4546 : errmsg("path element at position %d is null",
4547 : level + 1)));
4548 :
4549 101 : r = JsonbIteratorNext(it, &v, false);
4550 :
4551 101 : switch (r)
4552 : {
4553 : case WJB_BEGIN_ARRAY:
4554 39 : (void) pushJsonbValue(st, r, NULL);
4555 39 : setPathArray(it, path_elems, path_nulls, path_len, st, level,
4556 39 : newval, v.val.array.nElems, op_type);
4557 36 : r = JsonbIteratorNext(it, &v, false);
4558 36 : Assert(r == WJB_END_ARRAY);
4559 36 : res = pushJsonbValue(st, r, NULL);
4560 36 : break;
4561 : case WJB_BEGIN_OBJECT:
4562 62 : (void) pushJsonbValue(st, r, NULL);
4563 62 : setPathObject(it, path_elems, path_nulls, path_len, st, level,
4564 62 : newval, v.val.object.nPairs, op_type);
4565 50 : r = JsonbIteratorNext(it, &v, true);
4566 50 : Assert(r == WJB_END_OBJECT);
4567 50 : res = pushJsonbValue(st, r, NULL);
4568 50 : break;
4569 : case WJB_ELEM:
4570 : case WJB_VALUE:
4571 0 : res = pushJsonbValue(st, r, &v);
4572 0 : break;
4573 : default:
4574 0 : elog(ERROR, "unrecognized iterator result: %d", (int) r);
4575 : res = NULL; /* keep compiler quiet */
4576 : break;
4577 : }
4578 :
4579 86 : return res;
4580 : }
4581 :
4582 : /*
4583 : * Object walker for setPath
4584 : */
4585 : static void
4586 62 : setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
4587 : int path_len, JsonbParseState **st, int level,
4588 : Jsonb *newval, uint32 npairs, int op_type)
4589 : {
4590 : JsonbValue v;
4591 : int i;
4592 : JsonbValue k;
4593 62 : bool done = false;
4594 :
4595 62 : if (level >= path_len || path_nulls[level])
4596 0 : done = true;
4597 :
4598 : /* empty object is a special case for create */
4599 63 : if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) &&
4600 1 : (level == path_len - 1))
4601 : {
4602 : JsonbValue newkey;
4603 :
4604 1 : newkey.type = jbvString;
4605 1 : newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
4606 1 : newkey.val.string.val = VARDATA_ANY(path_elems[level]);
4607 :
4608 1 : (void) pushJsonbValue(st, WJB_KEY, &newkey);
4609 1 : addJsonbToParseState(st, newval);
4610 : }
4611 :
4612 186 : for (i = 0; i < npairs; i++)
4613 : {
4614 136 : JsonbIteratorToken r = JsonbIteratorNext(it, &k, true);
4615 :
4616 136 : Assert(r == WJB_KEY);
4617 :
4618 272 : if (!done &&
4619 272 : k.val.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
4620 136 : memcmp(k.val.string.val, VARDATA_ANY(path_elems[level]),
4621 136 : k.val.string.len) == 0)
4622 : {
4623 100 : if (level == path_len - 1)
4624 : {
4625 : /*
4626 : * called from jsonb_insert(), it forbids redefining an
4627 : * existing value
4628 : */
4629 6 : if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER))
4630 2 : ereport(ERROR,
4631 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4632 : errmsg("cannot replace existing key"),
4633 : errhint("Try using the function jsonb_set "
4634 : "to replace key value.")));
4635 :
4636 4 : r = JsonbIteratorNext(it, &v, true); /* skip value */
4637 4 : if (!(op_type & JB_PATH_DELETE))
4638 : {
4639 2 : (void) pushJsonbValue(st, WJB_KEY, &k);
4640 2 : addJsonbToParseState(st, newval);
4641 : }
4642 4 : done = true;
4643 : }
4644 : else
4645 : {
4646 50 : (void) pushJsonbValue(st, r, &k);
4647 50 : setPath(it, path_elems, path_nulls, path_len,
4648 : st, level + 1, newval, op_type);
4649 : }
4650 : }
4651 : else
4652 : {
4653 135 : if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
4654 66 : level == path_len - 1 && i == npairs - 1)
4655 : {
4656 : JsonbValue newkey;
4657 :
4658 3 : newkey.type = jbvString;
4659 3 : newkey.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
4660 3 : newkey.val.string.val = VARDATA_ANY(path_elems[level]);
4661 :
4662 3 : (void) pushJsonbValue(st, WJB_KEY, &newkey);
4663 3 : addJsonbToParseState(st, newval);
4664 : }
4665 :
4666 80 : (void) pushJsonbValue(st, r, &k);
4667 80 : r = JsonbIteratorNext(it, &v, false);
4668 80 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4669 80 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
4670 : {
4671 44 : int walking_level = 1;
4672 :
4673 253 : while (walking_level != 0)
4674 : {
4675 165 : r = JsonbIteratorNext(it, &v, false);
4676 :
4677 165 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
4678 10 : ++walking_level;
4679 165 : if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
4680 54 : --walking_level;
4681 :
4682 165 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4683 : }
4684 : }
4685 : }
4686 : }
4687 50 : }
4688 :
4689 : /*
4690 : * Array walker for setPath
4691 : */
4692 : static void
4693 39 : setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
4694 : int path_len, JsonbParseState **st, int level,
4695 : Jsonb *newval, uint32 nelems, int op_type)
4696 : {
4697 : JsonbValue v;
4698 : int idx,
4699 : i;
4700 39 : bool done = false;
4701 :
4702 : /* pick correct index */
4703 39 : if (level < path_len && !path_nulls[level])
4704 36 : {
4705 39 : char *c = TextDatumGetCString(path_elems[level]);
4706 : long lindex;
4707 : char *badp;
4708 :
4709 39 : errno = 0;
4710 39 : lindex = strtol(c, &badp, 10);
4711 39 : if (errno != 0 || badp == c || *badp != '\0' || lindex > INT_MAX ||
4712 : lindex < INT_MIN)
4713 3 : ereport(ERROR,
4714 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
4715 : errmsg("path element at position %d is not an integer: \"%s\"",
4716 : level + 1, c)));
4717 36 : idx = lindex;
4718 : }
4719 : else
4720 0 : idx = nelems;
4721 :
4722 36 : if (idx < 0)
4723 : {
4724 11 : if (-idx > nelems)
4725 3 : idx = INT_MIN;
4726 : else
4727 8 : idx = nelems + idx;
4728 : }
4729 :
4730 36 : if (idx > 0 && idx > nelems)
4731 8 : idx = nelems;
4732 :
4733 : /*
4734 : * if we're creating, and idx == INT_MIN, we prepend the new value to the
4735 : * array also if the array is empty - in which case we don't really care
4736 : * what the idx value is
4737 : */
4738 :
4739 45 : if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) &&
4740 9 : (op_type & JB_PATH_CREATE_OR_INSERT))
4741 : {
4742 9 : Assert(newval != NULL);
4743 9 : addJsonbToParseState(st, newval);
4744 9 : done = true;
4745 : }
4746 :
4747 : /* iterate over the array elements */
4748 118 : for (i = 0; i < nelems; i++)
4749 : {
4750 : JsonbIteratorToken r;
4751 :
4752 82 : if (i == idx && level < path_len)
4753 : {
4754 48 : if (level == path_len - 1)
4755 : {
4756 23 : r = JsonbIteratorNext(it, &v, true); /* skip */
4757 :
4758 23 : if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
4759 13 : addJsonbToParseState(st, newval);
4760 :
4761 : /*
4762 : * We should keep current value only in case of
4763 : * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER because
4764 : * otherwise it should be deleted or replaced
4765 : */
4766 23 : if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE))
4767 12 : (void) pushJsonbValue(st, r, &v);
4768 :
4769 23 : if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
4770 6 : addJsonbToParseState(st, newval);
4771 :
4772 23 : done = true;
4773 : }
4774 : else
4775 1 : (void) setPath(it, path_elems, path_nulls, path_len,
4776 : st, level + 1, newval, op_type);
4777 : }
4778 : else
4779 : {
4780 58 : r = JsonbIteratorNext(it, &v, false);
4781 :
4782 58 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4783 :
4784 58 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
4785 : {
4786 0 : int walking_level = 1;
4787 :
4788 0 : while (walking_level != 0)
4789 : {
4790 0 : r = JsonbIteratorNext(it, &v, false);
4791 :
4792 0 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
4793 0 : ++walking_level;
4794 0 : if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
4795 0 : --walking_level;
4796 :
4797 0 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4798 : }
4799 : }
4800 :
4801 91 : if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
4802 62 : level == path_len - 1 && i == nelems - 1)
4803 : {
4804 3 : addJsonbToParseState(st, newval);
4805 : }
4806 : }
4807 : }
4808 36 : }
4809 :
4810 : /*
4811 : * Iterate over jsonb string values or elements, and pass them together with an
4812 : * iteration state to a specified JsonIterateStringValuesAction.
4813 : */
4814 : void
4815 7 : iterate_jsonb_string_values(Jsonb *jb, void *state, JsonIterateStringValuesAction action)
4816 : {
4817 : JsonbIterator *it;
4818 : JsonbValue v;
4819 : JsonbIteratorToken type;
4820 :
4821 7 : it = JsonbIteratorInit(&jb->root);
4822 :
4823 63 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
4824 : {
4825 49 : if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
4826 : {
4827 10 : action(state, v.val.string.val, v.val.string.len);
4828 : }
4829 : }
4830 7 : }
4831 :
4832 : /*
4833 : * Iterate over json string values or elements, and pass them together with an
4834 : * iteration state to a specified JsonIterateStringValuesAction.
4835 : */
4836 : void
4837 7 : iterate_json_string_values(text *json, void *action_state, JsonIterateStringValuesAction action)
4838 : {
4839 7 : JsonLexContext *lex = makeJsonLexContext(json, true);
4840 7 : JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
4841 7 : IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
4842 :
4843 7 : state->lex = lex;
4844 7 : state->action = action;
4845 7 : state->action_state = action_state;
4846 :
4847 7 : sem->semstate = (void *) state;
4848 7 : sem->scalar = iterate_string_values_scalar;
4849 :
4850 7 : pg_parse_json(lex, sem);
4851 7 : }
4852 :
4853 : /*
4854 : * An auxiliary function for iterate_json_string_values to invoke a specified
4855 : * JsonIterateStringValuesAction.
4856 : */
4857 : static void
4858 11 : iterate_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
4859 : {
4860 11 : IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
4861 :
4862 11 : if (tokentype == JSON_TOKEN_STRING)
4863 10 : (*_state->action) (_state->action_state, token, strlen(token));
4864 11 : }
4865 :
4866 : /*
4867 : * Iterate over a jsonb, and apply a specified JsonTransformStringValuesAction
4868 : * to every string value or element. Any necessary context for a
4869 : * JsonTransformStringValuesAction can be passed in the action_state variable.
4870 : * Function returns a copy of an original jsonb object with transformed values.
4871 : */
4872 : Jsonb *
4873 7 : transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
4874 : JsonTransformStringValuesAction transform_action)
4875 : {
4876 : JsonbIterator *it;
4877 : JsonbValue v,
4878 7 : *res = NULL;
4879 : JsonbIteratorToken type;
4880 7 : JsonbParseState *st = NULL;
4881 : text *out;
4882 7 : bool is_scalar = false;
4883 :
4884 7 : it = JsonbIteratorInit(&jsonb->root);
4885 7 : is_scalar = it->isScalar;
4886 :
4887 83 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
4888 : {
4889 69 : if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
4890 : {
4891 19 : out = transform_action(action_state, v.val.string.val, v.val.string.len);
4892 19 : v.val.string.val = VARDATA_ANY(out);
4893 19 : v.val.string.len = VARSIZE_ANY_EXHDR(out);
4894 19 : res = pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);
4895 : }
4896 : else
4897 : {
4898 70 : res = pushJsonbValue(&st, type, (type == WJB_KEY ||
4899 31 : type == WJB_VALUE ||
4900 : type == WJB_ELEM) ? &v : NULL);
4901 : }
4902 : }
4903 :
4904 7 : if (res->type == jbvArray)
4905 2 : res->val.array.rawScalar = is_scalar;
4906 :
4907 7 : return JsonbValueToJsonb(res);
4908 : }
4909 :
4910 : /*
4911 : * Iterate over a json, and apply a specified JsonTransformStringValuesAction
4912 : * to every string value or element. Any necessary context for a
4913 : * JsonTransformStringValuesAction can be passed in the action_state variable.
4914 : * Function returns a StringInfo, which is a copy of an original json with
4915 : * transformed values.
4916 : */
4917 : text *
4918 7 : transform_json_string_values(text *json, void *action_state,
4919 : JsonTransformStringValuesAction transform_action)
4920 : {
4921 7 : JsonLexContext *lex = makeJsonLexContext(json, true);
4922 7 : JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
4923 7 : TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
4924 :
4925 7 : state->lex = lex;
4926 7 : state->strval = makeStringInfo();
4927 7 : state->action = transform_action;
4928 7 : state->action_state = action_state;
4929 :
4930 7 : sem->semstate = (void *) state;
4931 7 : sem->scalar = transform_string_values_scalar;
4932 7 : sem->object_start = transform_string_values_object_start;
4933 7 : sem->object_end = transform_string_values_object_end;
4934 7 : sem->array_start = transform_string_values_array_start;
4935 7 : sem->array_end = transform_string_values_array_end;
4936 7 : sem->scalar = transform_string_values_scalar;
4937 7 : sem->array_element_start = transform_string_values_array_element_start;
4938 7 : sem->object_field_start = transform_string_values_object_field_start;
4939 :
4940 7 : pg_parse_json(lex, sem);
4941 :
4942 7 : return cstring_to_text_with_len(state->strval->data, state->strval->len);
4943 : }
4944 :
4945 : /*
4946 : * Set of auxiliary functions for transform_json_string_values to invoke a
4947 : * specified JsonTransformStringValuesAction for all values and left everything
4948 : * else untouched.
4949 : */
4950 : static void
4951 9 : transform_string_values_object_start(void *state)
4952 : {
4953 9 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4954 :
4955 9 : appendStringInfoCharMacro(_state->strval, '{');
4956 9 : }
4957 :
4958 : static void
4959 9 : transform_string_values_object_end(void *state)
4960 : {
4961 9 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4962 :
4963 9 : appendStringInfoCharMacro(_state->strval, '}');
4964 9 : }
4965 :
4966 : static void
4967 5 : transform_string_values_array_start(void *state)
4968 : {
4969 5 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4970 :
4971 5 : appendStringInfoCharMacro(_state->strval, '[');
4972 5 : }
4973 :
4974 : static void
4975 5 : transform_string_values_array_end(void *state)
4976 : {
4977 5 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4978 :
4979 5 : appendStringInfoCharMacro(_state->strval, ']');
4980 5 : }
4981 :
4982 : static void
4983 19 : transform_string_values_object_field_start(void *state, char *fname, bool isnull)
4984 : {
4985 19 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
4986 :
4987 19 : if (_state->strval->data[_state->strval->len - 1] != '{')
4988 11 : appendStringInfoCharMacro(_state->strval, ',');
4989 :
4990 : /*
4991 : * Unfortunately we don't have the quoted and escaped string any more, so
4992 : * we have to re-escape it.
4993 : */
4994 19 : escape_json(_state->strval, fname);
4995 19 : appendStringInfoCharMacro(_state->strval, ':');
4996 19 : }
4997 :
4998 : static void
4999 8 : transform_string_values_array_element_start(void *state, bool isnull)
5000 : {
5001 8 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5002 :
5003 8 : if (_state->strval->data[_state->strval->len - 1] != '[')
5004 4 : appendStringInfoCharMacro(_state->strval, ',');
5005 8 : }
5006 :
5007 : static void
5008 20 : transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
5009 : {
5010 20 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5011 :
5012 20 : if (tokentype == JSON_TOKEN_STRING)
5013 : {
5014 19 : text *out = (*_state->action) (_state->action_state, token, strlen(token));
5015 :
5016 19 : escape_json(_state->strval, text_to_cstring(out));
5017 : }
5018 : else
5019 1 : appendStringInfoString(_state->strval, token);
5020 20 : }
|