Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * jsonfuncs.c
4 : * Functions to process JSON data types.
5 : *
6 : * Portions Copyright (c) 1996-2024, 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 "common/jsonapi.h"
22 : #include "common/string.h"
23 : #include "fmgr.h"
24 : #include "funcapi.h"
25 : #include "lib/stringinfo.h"
26 : #include "mb/pg_wchar.h"
27 : #include "miscadmin.h"
28 : #include "nodes/miscnodes.h"
29 : #include "parser/parse_coerce.h"
30 : #include "utils/array.h"
31 : #include "utils/builtins.h"
32 : #include "utils/fmgroids.h"
33 : #include "utils/hsearch.h"
34 : #include "utils/json.h"
35 : #include "utils/jsonb.h"
36 : #include "utils/jsonfuncs.h"
37 : #include "utils/lsyscache.h"
38 : #include "utils/memutils.h"
39 : #include "utils/syscache.h"
40 : #include "utils/typcache.h"
41 :
42 : /* Operations available for setPath */
43 : #define JB_PATH_CREATE 0x0001
44 : #define JB_PATH_DELETE 0x0002
45 : #define JB_PATH_REPLACE 0x0004
46 : #define JB_PATH_INSERT_BEFORE 0x0008
47 : #define JB_PATH_INSERT_AFTER 0x0010
48 : #define JB_PATH_CREATE_OR_INSERT \
49 : (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE)
50 : #define JB_PATH_FILL_GAPS 0x0020
51 : #define JB_PATH_CONSISTENT_POSITION 0x0040
52 :
53 : /* state for json_object_keys */
54 : typedef struct OkeysState
55 : {
56 : JsonLexContext *lex;
57 : char **result;
58 : int result_size;
59 : int result_count;
60 : int sent_count;
61 : } OkeysState;
62 :
63 : /* state for iterate_json_values function */
64 : typedef struct IterateJsonStringValuesState
65 : {
66 : JsonLexContext *lex;
67 : JsonIterateStringValuesAction action; /* an action that will be applied
68 : * to each json value */
69 : void *action_state; /* any necessary context for iteration */
70 : uint32 flags; /* what kind of elements from a json we want
71 : * to iterate */
72 : } IterateJsonStringValuesState;
73 :
74 : /* state for transform_json_string_values function */
75 : typedef struct TransformJsonStringValuesState
76 : {
77 : JsonLexContext *lex;
78 : StringInfo strval; /* resulting json */
79 : JsonTransformStringValuesAction action; /* an action that will be applied
80 : * to each json value */
81 : void *action_state; /* any necessary context for transformation */
82 : } TransformJsonStringValuesState;
83 :
84 : /* state for json_get* functions */
85 : typedef struct GetState
86 : {
87 : JsonLexContext *lex;
88 : text *tresult;
89 : char *result_start;
90 : bool normalize_results;
91 : bool next_scalar;
92 : int npath; /* length of each path-related array */
93 : char **path_names; /* field name(s) being sought */
94 : int *path_indexes; /* array index(es) being sought */
95 : bool *pathok; /* is path matched to current depth? */
96 : int *array_cur_index; /* current element index at each path
97 : * level */
98 : } GetState;
99 :
100 : /* state for json_array_length */
101 : typedef struct AlenState
102 : {
103 : JsonLexContext *lex;
104 : int count;
105 : } AlenState;
106 :
107 : /* state for json_each */
108 : typedef struct EachState
109 : {
110 : JsonLexContext *lex;
111 : Tuplestorestate *tuple_store;
112 : TupleDesc ret_tdesc;
113 : MemoryContext tmp_cxt;
114 : char *result_start;
115 : bool normalize_results;
116 : bool next_scalar;
117 : char *normalized_scalar;
118 : } EachState;
119 :
120 : /* state for json_array_elements */
121 : typedef struct ElementsState
122 : {
123 : JsonLexContext *lex;
124 : const char *function_name;
125 : Tuplestorestate *tuple_store;
126 : TupleDesc ret_tdesc;
127 : MemoryContext tmp_cxt;
128 : char *result_start;
129 : bool normalize_results;
130 : bool next_scalar;
131 : char *normalized_scalar;
132 : } ElementsState;
133 :
134 : /* state for get_json_object_as_hash */
135 : typedef struct JHashState
136 : {
137 : JsonLexContext *lex;
138 : const char *function_name;
139 : HTAB *hash;
140 : char *saved_scalar;
141 : char *save_json_start;
142 : JsonTokenType saved_token_type;
143 : } JHashState;
144 :
145 : /* hashtable element */
146 : typedef struct JsonHashEntry
147 : {
148 : char fname[NAMEDATALEN]; /* hash key (MUST BE FIRST) */
149 : char *val;
150 : JsonTokenType type;
151 : } JsonHashEntry;
152 :
153 : /* structure to cache type I/O metadata needed for populate_scalar() */
154 : typedef struct ScalarIOData
155 : {
156 : Oid typioparam;
157 : FmgrInfo typiofunc;
158 : } ScalarIOData;
159 :
160 : /* these two structures are used recursively */
161 : typedef struct ColumnIOData ColumnIOData;
162 : typedef struct RecordIOData RecordIOData;
163 :
164 : /* structure to cache metadata needed for populate_array() */
165 : typedef struct ArrayIOData
166 : {
167 : ColumnIOData *element_info; /* metadata cache */
168 : Oid element_type; /* array element type id */
169 : int32 element_typmod; /* array element type modifier */
170 : } ArrayIOData;
171 :
172 : /* structure to cache metadata needed for populate_composite() */
173 : typedef struct CompositeIOData
174 : {
175 : /*
176 : * We use pointer to a RecordIOData here because variable-length struct
177 : * RecordIOData can't be used directly in ColumnIOData.io union
178 : */
179 : RecordIOData *record_io; /* metadata cache for populate_record() */
180 : TupleDesc tupdesc; /* cached tuple descriptor */
181 : /* these fields differ from target type only if domain over composite: */
182 : Oid base_typid; /* base type id */
183 : int32 base_typmod; /* base type modifier */
184 : /* this field is used only if target type is domain over composite: */
185 : void *domain_info; /* opaque cache for domain checks */
186 : } CompositeIOData;
187 :
188 : /* structure to cache metadata needed for populate_domain() */
189 : typedef struct DomainIOData
190 : {
191 : ColumnIOData *base_io; /* metadata cache */
192 : Oid base_typid; /* base type id */
193 : int32 base_typmod; /* base type modifier */
194 : void *domain_info; /* opaque cache for domain checks */
195 : } DomainIOData;
196 :
197 : /* enumeration type categories */
198 : typedef enum TypeCat
199 : {
200 : TYPECAT_SCALAR = 's',
201 : TYPECAT_ARRAY = 'a',
202 : TYPECAT_COMPOSITE = 'c',
203 : TYPECAT_COMPOSITE_DOMAIN = 'C',
204 : TYPECAT_DOMAIN = 'd',
205 : } TypeCat;
206 :
207 : /* these two are stolen from hstore / record_out, used in populate_record* */
208 :
209 : /* structure to cache record metadata needed for populate_record_field() */
210 : struct ColumnIOData
211 : {
212 : Oid typid; /* column type id */
213 : int32 typmod; /* column type modifier */
214 : TypeCat typcat; /* column type category */
215 : ScalarIOData scalar_io; /* metadata cache for direct conversion
216 : * through input function */
217 : union
218 : {
219 : ArrayIOData array;
220 : CompositeIOData composite;
221 : DomainIOData domain;
222 : } io; /* metadata cache for various column type
223 : * categories */
224 : };
225 :
226 : /* structure to cache record metadata needed for populate_record() */
227 : struct RecordIOData
228 : {
229 : Oid record_type;
230 : int32 record_typmod;
231 : int ncolumns;
232 : ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
233 : };
234 :
235 : /* per-query cache for populate_record_worker and populate_recordset_worker */
236 : typedef struct PopulateRecordCache
237 : {
238 : Oid argtype; /* declared type of the record argument */
239 : ColumnIOData c; /* metadata cache for populate_composite() */
240 : MemoryContext fn_mcxt; /* where this is stored */
241 : } PopulateRecordCache;
242 :
243 : /* per-call state for populate_recordset */
244 : typedef struct PopulateRecordsetState
245 : {
246 : JsonLexContext *lex;
247 : const char *function_name;
248 : HTAB *json_hash;
249 : char *saved_scalar;
250 : char *save_json_start;
251 : JsonTokenType saved_token_type;
252 : Tuplestorestate *tuple_store;
253 : HeapTupleHeader rec;
254 : PopulateRecordCache *cache;
255 : } PopulateRecordsetState;
256 :
257 : /* common data for populate_array_json() and populate_array_dim_jsonb() */
258 : typedef struct PopulateArrayContext
259 : {
260 : ArrayBuildState *astate; /* array build state */
261 : ArrayIOData *aio; /* metadata cache */
262 : MemoryContext acxt; /* array build memory context */
263 : MemoryContext mcxt; /* cache memory context */
264 : const char *colname; /* for diagnostics only */
265 : int *dims; /* dimensions */
266 : int *sizes; /* current dimension counters */
267 : int ndims; /* number of dimensions */
268 : Node *escontext; /* For soft-error handling */
269 : } PopulateArrayContext;
270 :
271 : /* state for populate_array_json() */
272 : typedef struct PopulateArrayState
273 : {
274 : JsonLexContext *lex; /* json lexer */
275 : PopulateArrayContext *ctx; /* context */
276 : char *element_start; /* start of the current array element */
277 : char *element_scalar; /* current array element token if it is a
278 : * scalar */
279 : JsonTokenType element_type; /* current array element type */
280 : } PopulateArrayState;
281 :
282 : /* state for json_strip_nulls */
283 : typedef struct StripnullState
284 : {
285 : JsonLexContext *lex;
286 : StringInfo strval;
287 : bool skip_next_null;
288 : } StripnullState;
289 :
290 : /* structure for generalized json/jsonb value passing */
291 : typedef struct JsValue
292 : {
293 : bool is_json; /* json/jsonb */
294 : union
295 : {
296 : struct
297 : {
298 : char *str; /* json string */
299 : int len; /* json string length or -1 if null-terminated */
300 : JsonTokenType type; /* json type */
301 : } json; /* json value */
302 :
303 : JsonbValue *jsonb; /* jsonb value */
304 : } val;
305 : } JsValue;
306 :
307 : typedef struct JsObject
308 : {
309 : bool is_json; /* json/jsonb */
310 : union
311 : {
312 : HTAB *json_hash;
313 : JsonbContainer *jsonb_cont;
314 : } val;
315 : } JsObject;
316 :
317 : /* useful macros for testing JsValue properties */
318 : #define JsValueIsNull(jsv) \
319 : ((jsv)->is_json ? \
320 : (!(jsv)->val.json.str || (jsv)->val.json.type == JSON_TOKEN_NULL) : \
321 : (!(jsv)->val.jsonb || (jsv)->val.jsonb->type == jbvNull))
322 :
323 : #define JsValueIsString(jsv) \
324 : ((jsv)->is_json ? (jsv)->val.json.type == JSON_TOKEN_STRING \
325 : : ((jsv)->val.jsonb && (jsv)->val.jsonb->type == jbvString))
326 :
327 : #define JsObjectIsEmpty(jso) \
328 : ((jso)->is_json \
329 : ? hash_get_num_entries((jso)->val.json_hash) == 0 \
330 : : ((jso)->val.jsonb_cont == NULL || \
331 : JsonContainerSize((jso)->val.jsonb_cont) == 0))
332 :
333 : #define JsObjectFree(jso) \
334 : do { \
335 : if ((jso)->is_json) \
336 : hash_destroy((jso)->val.json_hash); \
337 : } while (0)
338 :
339 : static int report_json_context(JsonLexContext *lex);
340 :
341 : /* semantic action functions for json_object_keys */
342 : static JsonParseErrorType okeys_object_field_start(void *state, char *fname, bool isnull);
343 : static JsonParseErrorType okeys_array_start(void *state);
344 : static JsonParseErrorType okeys_scalar(void *state, char *token, JsonTokenType tokentype);
345 :
346 : /* semantic action functions for json_get* functions */
347 : static JsonParseErrorType get_object_start(void *state);
348 : static JsonParseErrorType get_object_end(void *state);
349 : static JsonParseErrorType get_object_field_start(void *state, char *fname, bool isnull);
350 : static JsonParseErrorType get_object_field_end(void *state, char *fname, bool isnull);
351 : static JsonParseErrorType get_array_start(void *state);
352 : static JsonParseErrorType get_array_end(void *state);
353 : static JsonParseErrorType get_array_element_start(void *state, bool isnull);
354 : static JsonParseErrorType get_array_element_end(void *state, bool isnull);
355 : static JsonParseErrorType get_scalar(void *state, char *token, JsonTokenType tokentype);
356 :
357 : /* common worker function for json getter functions */
358 : static Datum get_path_all(FunctionCallInfo fcinfo, bool as_text);
359 : static text *get_worker(text *json, char **tpath, int *ipath, int npath,
360 : bool normalize_results);
361 : static Datum get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text);
362 : static text *JsonbValueAsText(JsonbValue *v);
363 :
364 : /* semantic action functions for json_array_length */
365 : static JsonParseErrorType alen_object_start(void *state);
366 : static JsonParseErrorType alen_scalar(void *state, char *token, JsonTokenType tokentype);
367 : static JsonParseErrorType alen_array_element_start(void *state, bool isnull);
368 :
369 : /* common workers for json{b}_each* functions */
370 : static Datum each_worker(FunctionCallInfo fcinfo, bool as_text);
371 : static Datum each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
372 : bool as_text);
373 :
374 : /* semantic action functions for json_each */
375 : static JsonParseErrorType each_object_field_start(void *state, char *fname, bool isnull);
376 : static JsonParseErrorType each_object_field_end(void *state, char *fname, bool isnull);
377 : static JsonParseErrorType each_array_start(void *state);
378 : static JsonParseErrorType each_scalar(void *state, char *token, JsonTokenType tokentype);
379 :
380 : /* common workers for json{b}_array_elements_* functions */
381 : static Datum elements_worker(FunctionCallInfo fcinfo, const char *funcname,
382 : bool as_text);
383 : static Datum elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
384 : bool as_text);
385 :
386 : /* semantic action functions for json_array_elements */
387 : static JsonParseErrorType elements_object_start(void *state);
388 : static JsonParseErrorType elements_array_element_start(void *state, bool isnull);
389 : static JsonParseErrorType elements_array_element_end(void *state, bool isnull);
390 : static JsonParseErrorType elements_scalar(void *state, char *token, JsonTokenType tokentype);
391 :
392 : /* turn a json object into a hash table */
393 : static HTAB *get_json_object_as_hash(char *json, int len, const char *funcname,
394 : Node *escontext);
395 :
396 : /* semantic actions for populate_array_json */
397 : static JsonParseErrorType populate_array_object_start(void *_state);
398 : static JsonParseErrorType populate_array_array_end(void *_state);
399 : static JsonParseErrorType populate_array_element_start(void *_state, bool isnull);
400 : static JsonParseErrorType populate_array_element_end(void *_state, bool isnull);
401 : static JsonParseErrorType populate_array_scalar(void *_state, char *token, JsonTokenType tokentype);
402 :
403 : /* semantic action functions for get_json_object_as_hash */
404 : static JsonParseErrorType hash_object_field_start(void *state, char *fname, bool isnull);
405 : static JsonParseErrorType hash_object_field_end(void *state, char *fname, bool isnull);
406 : static JsonParseErrorType hash_array_start(void *state);
407 : static JsonParseErrorType hash_scalar(void *state, char *token, JsonTokenType tokentype);
408 :
409 : /* semantic action functions for populate_recordset */
410 : static JsonParseErrorType populate_recordset_object_field_start(void *state, char *fname, bool isnull);
411 : static JsonParseErrorType populate_recordset_object_field_end(void *state, char *fname, bool isnull);
412 : static JsonParseErrorType populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype);
413 : static JsonParseErrorType populate_recordset_object_start(void *state);
414 : static JsonParseErrorType populate_recordset_object_end(void *state);
415 : static JsonParseErrorType populate_recordset_array_start(void *state);
416 : static JsonParseErrorType populate_recordset_array_element_start(void *state, bool isnull);
417 :
418 : /* semantic action functions for json_strip_nulls */
419 : static JsonParseErrorType sn_object_start(void *state);
420 : static JsonParseErrorType sn_object_end(void *state);
421 : static JsonParseErrorType sn_array_start(void *state);
422 : static JsonParseErrorType sn_array_end(void *state);
423 : static JsonParseErrorType sn_object_field_start(void *state, char *fname, bool isnull);
424 : static JsonParseErrorType sn_array_element_start(void *state, bool isnull);
425 : static JsonParseErrorType sn_scalar(void *state, char *token, JsonTokenType tokentype);
426 :
427 : /* worker functions for populate_record, to_record, populate_recordset and to_recordset */
428 : static Datum populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
429 : bool is_json, bool have_record_arg);
430 : static Datum populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
431 : bool is_json, bool have_record_arg,
432 : Node *escontext);
433 :
434 : /* helper functions for populate_record[set] */
435 : static HeapTupleHeader populate_record(TupleDesc tupdesc, RecordIOData **record_p,
436 : HeapTupleHeader defaultval, MemoryContext mcxt,
437 : JsObject *obj, Node *escontext);
438 : static void get_record_type_from_argument(FunctionCallInfo fcinfo,
439 : const char *funcname,
440 : PopulateRecordCache *cache);
441 : static void get_record_type_from_query(FunctionCallInfo fcinfo,
442 : const char *funcname,
443 : PopulateRecordCache *cache);
444 : static bool JsValueToJsObject(JsValue *jsv, JsObject *jso, Node *escontext);
445 : static Datum populate_composite(CompositeIOData *io, Oid typid,
446 : const char *colname, MemoryContext mcxt,
447 : HeapTupleHeader defaultval, JsValue *jsv, bool *isnull,
448 : Node *escontext);
449 : static Datum populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
450 : bool *isnull, Node *escontext);
451 : static void prepare_column_cache(ColumnIOData *column, Oid typid, int32 typmod,
452 : MemoryContext mcxt, bool need_scalar);
453 : static Datum populate_record_field(ColumnIOData *col, Oid typid, int32 typmod,
454 : const char *colname, MemoryContext mcxt, Datum defaultval,
455 : JsValue *jsv, bool *isnull, Node *escontext);
456 : static RecordIOData *allocate_record_info(MemoryContext mcxt, int ncolumns);
457 : static bool JsObjectGetField(JsObject *obj, char *field, JsValue *jsv);
458 : static void populate_recordset_record(PopulateRecordsetState *state, JsObject *obj);
459 : static bool populate_array_json(PopulateArrayContext *ctx, char *json, int len);
460 : static bool populate_array_dim_jsonb(PopulateArrayContext *ctx, JsonbValue *jbv,
461 : int ndim);
462 : static void populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim);
463 : static bool populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims);
464 : static bool populate_array_check_dimension(PopulateArrayContext *ctx, int ndim);
465 : static bool populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv);
466 : static Datum populate_array(ArrayIOData *aio, const char *colname,
467 : MemoryContext mcxt, JsValue *jsv,
468 : bool *isnull,
469 : Node *escontext);
470 : static Datum populate_domain(DomainIOData *io, Oid typid, const char *colname,
471 : MemoryContext mcxt, JsValue *jsv, bool *isnull,
472 : Node *escontext);
473 :
474 : /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
475 : static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
476 : JsonbParseState **state);
477 : static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
478 : bool *path_nulls, int path_len,
479 : JsonbParseState **st, int level, JsonbValue *newval,
480 : int op_type);
481 : static void setPathObject(JsonbIterator **it, Datum *path_elems,
482 : bool *path_nulls, int path_len, JsonbParseState **st,
483 : int level,
484 : JsonbValue *newval, uint32 npairs, int op_type);
485 : static void setPathArray(JsonbIterator **it, Datum *path_elems,
486 : bool *path_nulls, int path_len, JsonbParseState **st,
487 : int level,
488 : JsonbValue *newval, uint32 nelems, int op_type);
489 :
490 : /* function supporting iterate_json_values */
491 : static JsonParseErrorType iterate_values_scalar(void *state, char *token, JsonTokenType tokentype);
492 : static JsonParseErrorType iterate_values_object_field_start(void *state, char *fname, bool isnull);
493 :
494 : /* functions supporting transform_json_string_values */
495 : static JsonParseErrorType transform_string_values_object_start(void *state);
496 : static JsonParseErrorType transform_string_values_object_end(void *state);
497 : static JsonParseErrorType transform_string_values_array_start(void *state);
498 : static JsonParseErrorType transform_string_values_array_end(void *state);
499 : static JsonParseErrorType transform_string_values_object_field_start(void *state, char *fname, bool isnull);
500 : static JsonParseErrorType transform_string_values_array_element_start(void *state, bool isnull);
501 : static JsonParseErrorType transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
502 :
503 :
504 : /*
505 : * pg_parse_json_or_errsave
506 : *
507 : * This function is like pg_parse_json, except that it does not return a
508 : * JsonParseErrorType. Instead, in case of any failure, this function will
509 : * save error data into *escontext if that's an ErrorSaveContext, otherwise
510 : * ereport(ERROR).
511 : *
512 : * Returns a boolean indicating success or failure (failure will only be
513 : * returned when escontext is an ErrorSaveContext).
514 : */
515 : bool
516 34010 : pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
517 : Node *escontext)
518 : {
519 : JsonParseErrorType result;
520 :
521 34010 : result = pg_parse_json(lex, sem);
522 33818 : if (result != JSON_SUCCESS)
523 : {
524 492 : json_errsave_error(result, lex, escontext);
525 54 : return false;
526 : }
527 33326 : return true;
528 : }
529 :
530 : /*
531 : * makeJsonLexContext
532 : *
533 : * This is like makeJsonLexContextCstringLen, but it accepts a text value
534 : * directly.
535 : */
536 : JsonLexContext *
537 11310 : makeJsonLexContext(JsonLexContext *lex, text *json, bool need_escapes)
538 : {
539 : /*
540 : * Most callers pass a detoasted datum, but it's not clear that they all
541 : * do. pg_detoast_datum_packed() is cheap insurance.
542 : */
543 11310 : json = pg_detoast_datum_packed(json);
544 :
545 33930 : return makeJsonLexContextCstringLen(lex,
546 11310 : VARDATA_ANY(json),
547 11310 : VARSIZE_ANY_EXHDR(json),
548 : GetDatabaseEncoding(),
549 : need_escapes);
550 : }
551 :
552 : /*
553 : * SQL function json_object_keys
554 : *
555 : * Returns the set of keys for the object argument.
556 : *
557 : * This SRF operates in value-per-call mode. It processes the
558 : * object during the first call, and the keys are simply stashed
559 : * in an array, whose size is expanded as necessary. This is probably
560 : * safe enough for a list of keys of a single object, since they are
561 : * limited in size to NAMEDATALEN and the number of keys is unlikely to
562 : * be so huge that it has major memory implications.
563 : */
564 : Datum
565 54 : jsonb_object_keys(PG_FUNCTION_ARGS)
566 : {
567 : FuncCallContext *funcctx;
568 : OkeysState *state;
569 :
570 54 : if (SRF_IS_FIRSTCALL())
571 : {
572 : MemoryContext oldcontext;
573 18 : Jsonb *jb = PG_GETARG_JSONB_P(0);
574 18 : bool skipNested = false;
575 : JsonbIterator *it;
576 : JsonbValue v;
577 : JsonbIteratorToken r;
578 :
579 18 : if (JB_ROOT_IS_SCALAR(jb))
580 6 : ereport(ERROR,
581 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
582 : errmsg("cannot call %s on a scalar",
583 : "jsonb_object_keys")));
584 12 : else if (JB_ROOT_IS_ARRAY(jb))
585 6 : ereport(ERROR,
586 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
587 : errmsg("cannot call %s on an array",
588 : "jsonb_object_keys")));
589 :
590 6 : funcctx = SRF_FIRSTCALL_INIT();
591 6 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
592 :
593 6 : state = palloc(sizeof(OkeysState));
594 :
595 6 : state->result_size = JB_ROOT_COUNT(jb);
596 6 : state->result_count = 0;
597 6 : state->sent_count = 0;
598 6 : state->result = palloc(state->result_size * sizeof(char *));
599 :
600 6 : it = JsonbIteratorInit(&jb->root);
601 :
602 90 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
603 : {
604 84 : skipNested = true;
605 :
606 84 : if (r == WJB_KEY)
607 : {
608 : char *cstr;
609 :
610 36 : cstr = palloc(v.val.string.len + 1 * sizeof(char));
611 36 : memcpy(cstr, v.val.string.val, v.val.string.len);
612 36 : cstr[v.val.string.len] = '\0';
613 36 : state->result[state->result_count++] = cstr;
614 : }
615 : }
616 :
617 6 : MemoryContextSwitchTo(oldcontext);
618 6 : funcctx->user_fctx = (void *) state;
619 : }
620 :
621 42 : funcctx = SRF_PERCALL_SETUP();
622 42 : state = (OkeysState *) funcctx->user_fctx;
623 :
624 42 : if (state->sent_count < state->result_count)
625 : {
626 36 : char *nxt = state->result[state->sent_count++];
627 :
628 36 : SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
629 : }
630 :
631 6 : SRF_RETURN_DONE(funcctx);
632 : }
633 :
634 : /*
635 : * Report a JSON error.
636 : */
637 : void
638 492 : json_errsave_error(JsonParseErrorType error, JsonLexContext *lex,
639 : Node *escontext)
640 : {
641 492 : if (error == JSON_UNICODE_HIGH_ESCAPE ||
642 492 : error == JSON_UNICODE_UNTRANSLATABLE ||
643 : error == JSON_UNICODE_CODE_POINT_ZERO)
644 24 : errsave(escontext,
645 : (errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
646 : errmsg("unsupported Unicode escape sequence"),
647 : errdetail_internal("%s", json_errdetail(error, lex)),
648 : report_json_context(lex)));
649 468 : else if (error == JSON_SEM_ACTION_FAILED)
650 : {
651 : /* semantic action function had better have reported something */
652 6 : if (!SOFT_ERROR_OCCURRED(escontext))
653 0 : elog(ERROR, "JSON semantic action function did not provide error information");
654 : }
655 : else
656 462 : errsave(escontext,
657 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
658 : errmsg("invalid input syntax for type %s", "json"),
659 : errdetail_internal("%s", json_errdetail(error, lex)),
660 : report_json_context(lex)));
661 54 : }
662 :
663 : /*
664 : * Report a CONTEXT line for bogus JSON input.
665 : *
666 : * lex->token_terminator must be set to identify the spot where we detected
667 : * the error. Note that lex->token_start might be NULL, in case we recognized
668 : * error at EOF.
669 : *
670 : * The return value isn't meaningful, but we make it non-void so that this
671 : * can be invoked inside ereport().
672 : */
673 : static int
674 450 : report_json_context(JsonLexContext *lex)
675 : {
676 : const char *context_start;
677 : const char *context_end;
678 : const char *line_start;
679 : char *ctxt;
680 : int ctxtlen;
681 : const char *prefix;
682 : const char *suffix;
683 :
684 : /* Choose boundaries for the part of the input we will display */
685 450 : line_start = lex->line_start;
686 450 : context_start = line_start;
687 450 : context_end = lex->token_terminator;
688 : Assert(context_end >= context_start);
689 :
690 : /* Advance until we are close enough to context_end */
691 582 : while (context_end - context_start >= 50)
692 : {
693 : /* Advance to next multibyte character */
694 132 : if (IS_HIGHBIT_SET(*context_start))
695 0 : context_start += pg_mblen(context_start);
696 : else
697 132 : context_start++;
698 : }
699 :
700 : /*
701 : * We add "..." to indicate that the excerpt doesn't start at the
702 : * beginning of the line ... but if we're within 3 characters of the
703 : * beginning of the line, we might as well just show the whole line.
704 : */
705 450 : if (context_start - line_start <= 3)
706 438 : context_start = line_start;
707 :
708 : /* Get a null-terminated copy of the data to present */
709 450 : ctxtlen = context_end - context_start;
710 450 : ctxt = palloc(ctxtlen + 1);
711 450 : memcpy(ctxt, context_start, ctxtlen);
712 450 : ctxt[ctxtlen] = '\0';
713 :
714 : /*
715 : * Show the context, prefixing "..." if not starting at start of line, and
716 : * suffixing "..." if not ending at end of line.
717 : */
718 450 : prefix = (context_start > line_start) ? "..." : "";
719 1290 : suffix = (lex->token_type != JSON_TOKEN_END &&
720 390 : context_end - lex->input < lex->input_length &&
721 840 : *context_end != '\n' && *context_end != '\r') ? "..." : "";
722 :
723 450 : return errcontext("JSON data, line %d: %s%s%s",
724 : lex->line_number, prefix, ctxt, suffix);
725 : }
726 :
727 :
728 : Datum
729 1860 : json_object_keys(PG_FUNCTION_ARGS)
730 : {
731 : FuncCallContext *funcctx;
732 : OkeysState *state;
733 :
734 1860 : if (SRF_IS_FIRSTCALL())
735 : {
736 24 : text *json = PG_GETARG_TEXT_PP(0);
737 : JsonLexContext lex;
738 : JsonSemAction *sem;
739 : MemoryContext oldcontext;
740 :
741 24 : funcctx = SRF_FIRSTCALL_INIT();
742 24 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
743 :
744 24 : state = palloc(sizeof(OkeysState));
745 24 : sem = palloc0(sizeof(JsonSemAction));
746 :
747 24 : state->lex = makeJsonLexContext(&lex, json, true);
748 24 : state->result_size = 256;
749 24 : state->result_count = 0;
750 24 : state->sent_count = 0;
751 24 : state->result = palloc(256 * sizeof(char *));
752 :
753 24 : sem->semstate = (void *) state;
754 24 : sem->array_start = okeys_array_start;
755 24 : sem->scalar = okeys_scalar;
756 24 : sem->object_field_start = okeys_object_field_start;
757 : /* remainder are all NULL, courtesy of palloc0 above */
758 :
759 24 : pg_parse_json_or_ereport(&lex, sem);
760 : /* keys are now in state->result */
761 :
762 12 : freeJsonLexContext(&lex);
763 12 : pfree(sem);
764 :
765 12 : MemoryContextSwitchTo(oldcontext);
766 12 : funcctx->user_fctx = (void *) state;
767 : }
768 :
769 1848 : funcctx = SRF_PERCALL_SETUP();
770 1848 : state = (OkeysState *) funcctx->user_fctx;
771 :
772 1848 : if (state->sent_count < state->result_count)
773 : {
774 1836 : char *nxt = state->result[state->sent_count++];
775 :
776 1836 : SRF_RETURN_NEXT(funcctx, CStringGetTextDatum(nxt));
777 : }
778 :
779 12 : SRF_RETURN_DONE(funcctx);
780 : }
781 :
782 : static JsonParseErrorType
783 1842 : okeys_object_field_start(void *state, char *fname, bool isnull)
784 : {
785 1842 : OkeysState *_state = (OkeysState *) state;
786 :
787 : /* only collecting keys for the top level object */
788 1842 : if (_state->lex->lex_level != 1)
789 6 : return JSON_SUCCESS;
790 :
791 : /* enlarge result array if necessary */
792 1836 : if (_state->result_count >= _state->result_size)
793 : {
794 6 : _state->result_size *= 2;
795 6 : _state->result = (char **)
796 6 : repalloc(_state->result, sizeof(char *) * _state->result_size);
797 : }
798 :
799 : /* save a copy of the field name */
800 1836 : _state->result[_state->result_count++] = pstrdup(fname);
801 :
802 1836 : return JSON_SUCCESS;
803 : }
804 :
805 : static JsonParseErrorType
806 12 : okeys_array_start(void *state)
807 : {
808 12 : OkeysState *_state = (OkeysState *) state;
809 :
810 : /* top level must be a json object */
811 12 : if (_state->lex->lex_level == 0)
812 6 : ereport(ERROR,
813 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
814 : errmsg("cannot call %s on an array",
815 : "json_object_keys")));
816 :
817 6 : return JSON_SUCCESS;
818 : }
819 :
820 : static JsonParseErrorType
821 1854 : okeys_scalar(void *state, char *token, JsonTokenType tokentype)
822 : {
823 1854 : OkeysState *_state = (OkeysState *) state;
824 :
825 : /* top level must be a json object */
826 1854 : if (_state->lex->lex_level == 0)
827 6 : ereport(ERROR,
828 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
829 : errmsg("cannot call %s on a scalar",
830 : "json_object_keys")));
831 :
832 1848 : return JSON_SUCCESS;
833 : }
834 :
835 : /*
836 : * json and jsonb getter functions
837 : * these implement the -> ->> #> and #>> operators
838 : * and the json{b?}_extract_path*(json, text, ...) functions
839 : */
840 :
841 :
842 : Datum
843 980 : json_object_field(PG_FUNCTION_ARGS)
844 : {
845 980 : text *json = PG_GETARG_TEXT_PP(0);
846 980 : text *fname = PG_GETARG_TEXT_PP(1);
847 980 : char *fnamestr = text_to_cstring(fname);
848 : text *result;
849 :
850 980 : result = get_worker(json, &fnamestr, NULL, 1, false);
851 :
852 956 : if (result != NULL)
853 782 : PG_RETURN_TEXT_P(result);
854 : else
855 174 : PG_RETURN_NULL();
856 : }
857 :
858 : Datum
859 24690 : jsonb_object_field(PG_FUNCTION_ARGS)
860 : {
861 24690 : Jsonb *jb = PG_GETARG_JSONB_P(0);
862 24690 : text *key = PG_GETARG_TEXT_PP(1);
863 : JsonbValue *v;
864 : JsonbValue vbuf;
865 :
866 24690 : if (!JB_ROOT_IS_OBJECT(jb))
867 24 : PG_RETURN_NULL();
868 :
869 49332 : v = getKeyJsonValueFromContainer(&jb->root,
870 24666 : VARDATA_ANY(key),
871 24666 : VARSIZE_ANY_EXHDR(key),
872 : &vbuf);
873 :
874 24666 : if (v != NULL)
875 432 : PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
876 :
877 24234 : PG_RETURN_NULL();
878 : }
879 :
880 : Datum
881 924 : json_object_field_text(PG_FUNCTION_ARGS)
882 : {
883 924 : text *json = PG_GETARG_TEXT_PP(0);
884 924 : text *fname = PG_GETARG_TEXT_PP(1);
885 924 : char *fnamestr = text_to_cstring(fname);
886 : text *result;
887 :
888 924 : result = get_worker(json, &fnamestr, NULL, 1, true);
889 :
890 918 : if (result != NULL)
891 882 : PG_RETURN_TEXT_P(result);
892 : else
893 36 : PG_RETURN_NULL();
894 : }
895 :
896 : Datum
897 198 : jsonb_object_field_text(PG_FUNCTION_ARGS)
898 : {
899 198 : Jsonb *jb = PG_GETARG_JSONB_P(0);
900 198 : text *key = PG_GETARG_TEXT_PP(1);
901 : JsonbValue *v;
902 : JsonbValue vbuf;
903 :
904 198 : if (!JB_ROOT_IS_OBJECT(jb))
905 24 : PG_RETURN_NULL();
906 :
907 348 : v = getKeyJsonValueFromContainer(&jb->root,
908 174 : VARDATA_ANY(key),
909 174 : VARSIZE_ANY_EXHDR(key),
910 : &vbuf);
911 :
912 174 : if (v != NULL && v->type != jbvNull)
913 144 : PG_RETURN_TEXT_P(JsonbValueAsText(v));
914 :
915 30 : PG_RETURN_NULL();
916 : }
917 :
918 : Datum
919 280 : json_array_element(PG_FUNCTION_ARGS)
920 : {
921 280 : text *json = PG_GETARG_TEXT_PP(0);
922 280 : int element = PG_GETARG_INT32(1);
923 : text *result;
924 :
925 280 : result = get_worker(json, NULL, &element, 1, false);
926 :
927 280 : if (result != NULL)
928 244 : PG_RETURN_TEXT_P(result);
929 : else
930 36 : PG_RETURN_NULL();
931 : }
932 :
933 : Datum
934 318 : jsonb_array_element(PG_FUNCTION_ARGS)
935 : {
936 318 : Jsonb *jb = PG_GETARG_JSONB_P(0);
937 318 : int element = PG_GETARG_INT32(1);
938 : JsonbValue *v;
939 :
940 318 : if (!JB_ROOT_IS_ARRAY(jb))
941 18 : PG_RETURN_NULL();
942 :
943 : /* Handle negative subscript */
944 300 : if (element < 0)
945 : {
946 18 : uint32 nelements = JB_ROOT_COUNT(jb);
947 :
948 18 : if (-element > nelements)
949 6 : PG_RETURN_NULL();
950 : else
951 12 : element += nelements;
952 : }
953 :
954 294 : v = getIthJsonbValueFromContainer(&jb->root, element);
955 294 : if (v != NULL)
956 264 : PG_RETURN_JSONB_P(JsonbValueToJsonb(v));
957 :
958 30 : PG_RETURN_NULL();
959 : }
960 :
961 : Datum
962 48 : json_array_element_text(PG_FUNCTION_ARGS)
963 : {
964 48 : text *json = PG_GETARG_TEXT_PP(0);
965 48 : int element = PG_GETARG_INT32(1);
966 : text *result;
967 :
968 48 : result = get_worker(json, NULL, &element, 1, true);
969 :
970 48 : if (result != NULL)
971 24 : PG_RETURN_TEXT_P(result);
972 : else
973 24 : PG_RETURN_NULL();
974 : }
975 :
976 : Datum
977 60 : jsonb_array_element_text(PG_FUNCTION_ARGS)
978 : {
979 60 : Jsonb *jb = PG_GETARG_JSONB_P(0);
980 60 : int element = PG_GETARG_INT32(1);
981 : JsonbValue *v;
982 :
983 60 : if (!JB_ROOT_IS_ARRAY(jb))
984 12 : PG_RETURN_NULL();
985 :
986 : /* Handle negative subscript */
987 48 : if (element < 0)
988 : {
989 0 : uint32 nelements = JB_ROOT_COUNT(jb);
990 :
991 0 : if (-element > nelements)
992 0 : PG_RETURN_NULL();
993 : else
994 0 : element += nelements;
995 : }
996 :
997 48 : v = getIthJsonbValueFromContainer(&jb->root, element);
998 :
999 48 : if (v != NULL && v->type != jbvNull)
1000 24 : PG_RETURN_TEXT_P(JsonbValueAsText(v));
1001 :
1002 24 : PG_RETURN_NULL();
1003 : }
1004 :
1005 : Datum
1006 288 : json_extract_path(PG_FUNCTION_ARGS)
1007 : {
1008 288 : return get_path_all(fcinfo, false);
1009 : }
1010 :
1011 : Datum
1012 180 : json_extract_path_text(PG_FUNCTION_ARGS)
1013 : {
1014 180 : return get_path_all(fcinfo, true);
1015 : }
1016 :
1017 : /*
1018 : * common routine for extract_path functions
1019 : */
1020 : static Datum
1021 468 : get_path_all(FunctionCallInfo fcinfo, bool as_text)
1022 : {
1023 468 : text *json = PG_GETARG_TEXT_PP(0);
1024 468 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
1025 : text *result;
1026 : Datum *pathtext;
1027 : bool *pathnulls;
1028 : int npath;
1029 : char **tpath;
1030 : int *ipath;
1031 : int i;
1032 :
1033 : /*
1034 : * If the array contains any null elements, return NULL, on the grounds
1035 : * that you'd have gotten NULL if any RHS value were NULL in a nested
1036 : * series of applications of the -> operator. (Note: because we also
1037 : * return NULL for error cases such as no-such-field, this is true
1038 : * regardless of the contents of the rest of the array.)
1039 : */
1040 468 : if (array_contains_nulls(path))
1041 12 : PG_RETURN_NULL();
1042 :
1043 456 : deconstruct_array_builtin(path, TEXTOID, &pathtext, &pathnulls, &npath);
1044 :
1045 456 : tpath = palloc(npath * sizeof(char *));
1046 456 : ipath = palloc(npath * sizeof(int));
1047 :
1048 1248 : for (i = 0; i < npath; i++)
1049 : {
1050 : Assert(!pathnulls[i]);
1051 792 : tpath[i] = TextDatumGetCString(pathtext[i]);
1052 :
1053 : /*
1054 : * we have no idea at this stage what structure the document is so
1055 : * just convert anything in the path that we can to an integer and set
1056 : * all the other integers to INT_MIN which will never match.
1057 : */
1058 792 : if (*tpath[i] != '\0')
1059 : {
1060 : int ind;
1061 : char *endptr;
1062 :
1063 780 : errno = 0;
1064 780 : ind = strtoint(tpath[i], &endptr, 10);
1065 780 : if (endptr == tpath[i] || *endptr != '\0' || errno != 0)
1066 564 : ipath[i] = INT_MIN;
1067 : else
1068 216 : ipath[i] = ind;
1069 : }
1070 : else
1071 12 : ipath[i] = INT_MIN;
1072 : }
1073 :
1074 456 : result = get_worker(json, tpath, ipath, npath, as_text);
1075 :
1076 456 : if (result != NULL)
1077 336 : PG_RETURN_TEXT_P(result);
1078 : else
1079 120 : PG_RETURN_NULL();
1080 : }
1081 :
1082 : /*
1083 : * get_worker
1084 : *
1085 : * common worker for all the json getter functions
1086 : *
1087 : * json: JSON object (in text form)
1088 : * tpath[]: field name(s) to extract
1089 : * ipath[]: array index(es) (zero-based) to extract, accepts negatives
1090 : * npath: length of tpath[] and/or ipath[]
1091 : * normalize_results: true to de-escape string and null scalars
1092 : *
1093 : * tpath can be NULL, or any one tpath[] entry can be NULL, if an object
1094 : * field is not to be matched at that nesting level. Similarly, ipath can
1095 : * be NULL, or any one ipath[] entry can be INT_MIN if an array element is
1096 : * not to be matched at that nesting level (a json datum should never be
1097 : * large enough to have -INT_MIN elements due to MaxAllocSize restriction).
1098 : */
1099 : static text *
1100 2688 : get_worker(text *json,
1101 : char **tpath,
1102 : int *ipath,
1103 : int npath,
1104 : bool normalize_results)
1105 : {
1106 2688 : JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
1107 2688 : GetState *state = palloc0(sizeof(GetState));
1108 :
1109 : Assert(npath >= 0);
1110 :
1111 2688 : state->lex = makeJsonLexContext(NULL, json, true);
1112 :
1113 : /* is it "_as_text" variant? */
1114 2688 : state->normalize_results = normalize_results;
1115 2688 : state->npath = npath;
1116 2688 : state->path_names = tpath;
1117 2688 : state->path_indexes = ipath;
1118 2688 : state->pathok = palloc0(sizeof(bool) * npath);
1119 2688 : state->array_cur_index = palloc(sizeof(int) * npath);
1120 :
1121 2688 : if (npath > 0)
1122 2628 : state->pathok[0] = true;
1123 :
1124 2688 : sem->semstate = (void *) state;
1125 :
1126 : /*
1127 : * Not all variants need all the semantic routines. Only set the ones that
1128 : * are actually needed for maximum efficiency.
1129 : */
1130 2688 : sem->scalar = get_scalar;
1131 2688 : if (npath == 0)
1132 : {
1133 60 : sem->object_start = get_object_start;
1134 60 : sem->object_end = get_object_end;
1135 60 : sem->array_start = get_array_start;
1136 60 : sem->array_end = get_array_end;
1137 : }
1138 2688 : if (tpath != NULL)
1139 : {
1140 2360 : sem->object_field_start = get_object_field_start;
1141 2360 : sem->object_field_end = get_object_field_end;
1142 : }
1143 2688 : if (ipath != NULL)
1144 : {
1145 784 : sem->array_start = get_array_start;
1146 784 : sem->array_element_start = get_array_element_start;
1147 784 : sem->array_element_end = get_array_element_end;
1148 : }
1149 :
1150 2688 : pg_parse_json_or_ereport(state->lex, sem);
1151 2658 : freeJsonLexContext(state->lex);
1152 :
1153 2658 : return state->tresult;
1154 : }
1155 :
1156 : static JsonParseErrorType
1157 36 : get_object_start(void *state)
1158 : {
1159 36 : GetState *_state = (GetState *) state;
1160 36 : int lex_level = _state->lex->lex_level;
1161 :
1162 36 : if (lex_level == 0 && _state->npath == 0)
1163 : {
1164 : /*
1165 : * Special case: we should match the entire object. We only need this
1166 : * at outermost level because at nested levels the match will have
1167 : * been started by the outer field or array element callback.
1168 : */
1169 12 : _state->result_start = _state->lex->token_start;
1170 : }
1171 :
1172 36 : return JSON_SUCCESS;
1173 : }
1174 :
1175 : static JsonParseErrorType
1176 36 : get_object_end(void *state)
1177 : {
1178 36 : GetState *_state = (GetState *) state;
1179 36 : int lex_level = _state->lex->lex_level;
1180 :
1181 36 : if (lex_level == 0 && _state->npath == 0)
1182 : {
1183 : /* Special case: return the entire object */
1184 12 : char *start = _state->result_start;
1185 12 : int len = _state->lex->prev_token_terminator - start;
1186 :
1187 12 : _state->tresult = cstring_to_text_with_len(start, len);
1188 : }
1189 :
1190 36 : return JSON_SUCCESS;
1191 : }
1192 :
1193 : static JsonParseErrorType
1194 106960 : get_object_field_start(void *state, char *fname, bool isnull)
1195 : {
1196 106960 : GetState *_state = (GetState *) state;
1197 106960 : bool get_next = false;
1198 106960 : int lex_level = _state->lex->lex_level;
1199 :
1200 106960 : if (lex_level <= _state->npath &&
1201 28138 : _state->pathok[lex_level - 1] &&
1202 27898 : _state->path_names != NULL &&
1203 27898 : _state->path_names[lex_level - 1] != NULL &&
1204 27898 : strcmp(fname, _state->path_names[lex_level - 1]) == 0)
1205 : {
1206 2132 : if (lex_level < _state->npath)
1207 : {
1208 : /* if not at end of path just mark path ok */
1209 216 : _state->pathok[lex_level] = true;
1210 : }
1211 : else
1212 : {
1213 : /* end of path, so we want this value */
1214 1916 : get_next = true;
1215 : }
1216 : }
1217 :
1218 106960 : if (get_next)
1219 : {
1220 : /* this object overrides any previous matching object */
1221 1916 : _state->tresult = NULL;
1222 1916 : _state->result_start = NULL;
1223 :
1224 1916 : if (_state->normalize_results &&
1225 960 : _state->lex->token_type == JSON_TOKEN_STRING)
1226 : {
1227 : /* for as_text variants, tell get_scalar to set it for us */
1228 678 : _state->next_scalar = true;
1229 : }
1230 : else
1231 : {
1232 : /* for non-as_text variants, just note the json starting point */
1233 1238 : _state->result_start = _state->lex->token_start;
1234 : }
1235 : }
1236 :
1237 106960 : return JSON_SUCCESS;
1238 : }
1239 :
1240 : static JsonParseErrorType
1241 106960 : get_object_field_end(void *state, char *fname, bool isnull)
1242 : {
1243 106960 : GetState *_state = (GetState *) state;
1244 106960 : bool get_last = false;
1245 106960 : int lex_level = _state->lex->lex_level;
1246 :
1247 : /* same tests as in get_object_field_start */
1248 106960 : if (lex_level <= _state->npath &&
1249 28138 : _state->pathok[lex_level - 1] &&
1250 27898 : _state->path_names != NULL &&
1251 27898 : _state->path_names[lex_level - 1] != NULL &&
1252 27898 : strcmp(fname, _state->path_names[lex_level - 1]) == 0)
1253 : {
1254 2132 : if (lex_level < _state->npath)
1255 : {
1256 : /* done with this field so reset pathok */
1257 216 : _state->pathok[lex_level] = false;
1258 : }
1259 : else
1260 : {
1261 : /* end of path, so we want this value */
1262 1916 : get_last = true;
1263 : }
1264 : }
1265 :
1266 : /* for as_text scalar case, our work is already done */
1267 106960 : if (get_last && _state->result_start != NULL)
1268 : {
1269 : /*
1270 : * make a text object from the string from the previously noted json
1271 : * start up to the end of the previous token (the lexer is by now
1272 : * ahead of us on whatever came after what we're interested in).
1273 : */
1274 1238 : if (isnull && _state->normalize_results)
1275 24 : _state->tresult = (text *) NULL;
1276 : else
1277 : {
1278 1214 : char *start = _state->result_start;
1279 1214 : int len = _state->lex->prev_token_terminator - start;
1280 :
1281 1214 : _state->tresult = cstring_to_text_with_len(start, len);
1282 : }
1283 :
1284 : /* this should be unnecessary but let's do it for cleanliness: */
1285 1238 : _state->result_start = NULL;
1286 : }
1287 :
1288 106960 : return JSON_SUCCESS;
1289 : }
1290 :
1291 : static JsonParseErrorType
1292 1856 : get_array_start(void *state)
1293 : {
1294 1856 : GetState *_state = (GetState *) state;
1295 1856 : int lex_level = _state->lex->lex_level;
1296 :
1297 1856 : if (lex_level < _state->npath)
1298 : {
1299 : /* Initialize counting of elements in this array */
1300 514 : _state->array_cur_index[lex_level] = -1;
1301 :
1302 : /* INT_MIN value is reserved to represent invalid subscript */
1303 514 : if (_state->path_indexes[lex_level] < 0 &&
1304 30 : _state->path_indexes[lex_level] != INT_MIN)
1305 : {
1306 : /* Negative subscript -- convert to positive-wise subscript */
1307 : JsonParseErrorType error;
1308 : int nelements;
1309 :
1310 6 : error = json_count_array_elements(_state->lex, &nelements);
1311 6 : if (error != JSON_SUCCESS)
1312 0 : json_errsave_error(error, _state->lex, NULL);
1313 :
1314 6 : if (-_state->path_indexes[lex_level] <= nelements)
1315 6 : _state->path_indexes[lex_level] += nelements;
1316 : }
1317 : }
1318 1342 : else if (lex_level == 0 && _state->npath == 0)
1319 : {
1320 : /*
1321 : * Special case: we should match the entire array. We only need this
1322 : * at the outermost level because at nested levels the match will have
1323 : * been started by the outer field or array element callback.
1324 : */
1325 12 : _state->result_start = _state->lex->token_start;
1326 : }
1327 :
1328 1856 : return JSON_SUCCESS;
1329 : }
1330 :
1331 : static JsonParseErrorType
1332 12 : get_array_end(void *state)
1333 : {
1334 12 : GetState *_state = (GetState *) state;
1335 12 : int lex_level = _state->lex->lex_level;
1336 :
1337 12 : if (lex_level == 0 && _state->npath == 0)
1338 : {
1339 : /* Special case: return the entire array */
1340 12 : char *start = _state->result_start;
1341 12 : int len = _state->lex->prev_token_terminator - start;
1342 :
1343 12 : _state->tresult = cstring_to_text_with_len(start, len);
1344 : }
1345 :
1346 12 : return JSON_SUCCESS;
1347 : }
1348 :
1349 : static JsonParseErrorType
1350 1930 : get_array_element_start(void *state, bool isnull)
1351 : {
1352 1930 : GetState *_state = (GetState *) state;
1353 1930 : bool get_next = false;
1354 1930 : int lex_level = _state->lex->lex_level;
1355 :
1356 : /* Update array element counter */
1357 1930 : if (lex_level <= _state->npath)
1358 976 : _state->array_cur_index[lex_level - 1]++;
1359 :
1360 1930 : if (lex_level <= _state->npath &&
1361 976 : _state->pathok[lex_level - 1] &&
1362 976 : _state->path_indexes != NULL &&
1363 976 : _state->array_cur_index[lex_level - 1] == _state->path_indexes[lex_level - 1])
1364 : {
1365 478 : if (lex_level < _state->npath)
1366 : {
1367 : /* if not at end of path just mark path ok */
1368 144 : _state->pathok[lex_level] = true;
1369 : }
1370 : else
1371 : {
1372 : /* end of path, so we want this value */
1373 334 : get_next = true;
1374 : }
1375 : }
1376 :
1377 : /* same logic as for objects */
1378 1930 : if (get_next)
1379 : {
1380 334 : _state->tresult = NULL;
1381 334 : _state->result_start = NULL;
1382 :
1383 334 : if (_state->normalize_results &&
1384 60 : _state->lex->token_type == JSON_TOKEN_STRING)
1385 : {
1386 18 : _state->next_scalar = true;
1387 : }
1388 : else
1389 : {
1390 316 : _state->result_start = _state->lex->token_start;
1391 : }
1392 : }
1393 :
1394 1930 : return JSON_SUCCESS;
1395 : }
1396 :
1397 : static JsonParseErrorType
1398 1930 : get_array_element_end(void *state, bool isnull)
1399 : {
1400 1930 : GetState *_state = (GetState *) state;
1401 1930 : bool get_last = false;
1402 1930 : int lex_level = _state->lex->lex_level;
1403 :
1404 : /* same tests as in get_array_element_start */
1405 1930 : if (lex_level <= _state->npath &&
1406 976 : _state->pathok[lex_level - 1] &&
1407 976 : _state->path_indexes != NULL &&
1408 976 : _state->array_cur_index[lex_level - 1] == _state->path_indexes[lex_level - 1])
1409 : {
1410 478 : if (lex_level < _state->npath)
1411 : {
1412 : /* done with this element so reset pathok */
1413 144 : _state->pathok[lex_level] = false;
1414 : }
1415 : else
1416 : {
1417 : /* end of path, so we want this value */
1418 334 : get_last = true;
1419 : }
1420 : }
1421 :
1422 : /* same logic as for objects */
1423 1930 : if (get_last && _state->result_start != NULL)
1424 : {
1425 316 : if (isnull && _state->normalize_results)
1426 12 : _state->tresult = (text *) NULL;
1427 : else
1428 : {
1429 304 : char *start = _state->result_start;
1430 304 : int len = _state->lex->prev_token_terminator - start;
1431 :
1432 304 : _state->tresult = cstring_to_text_with_len(start, len);
1433 : }
1434 :
1435 316 : _state->result_start = NULL;
1436 : }
1437 :
1438 1930 : return JSON_SUCCESS;
1439 : }
1440 :
1441 : static JsonParseErrorType
1442 105486 : get_scalar(void *state, char *token, JsonTokenType tokentype)
1443 : {
1444 105486 : GetState *_state = (GetState *) state;
1445 105486 : int lex_level = _state->lex->lex_level;
1446 :
1447 : /* Check for whole-object match */
1448 105486 : if (lex_level == 0 && _state->npath == 0)
1449 : {
1450 36 : if (_state->normalize_results && tokentype == JSON_TOKEN_STRING)
1451 : {
1452 : /* we want the de-escaped string */
1453 6 : _state->next_scalar = true;
1454 : }
1455 30 : else if (_state->normalize_results && tokentype == JSON_TOKEN_NULL)
1456 : {
1457 6 : _state->tresult = (text *) NULL;
1458 : }
1459 : else
1460 : {
1461 : /*
1462 : * This is a bit hokey: we will suppress whitespace after the
1463 : * scalar token, but not whitespace before it. Probably not worth
1464 : * doing our own space-skipping to avoid that.
1465 : */
1466 24 : char *start = _state->lex->input;
1467 24 : int len = _state->lex->prev_token_terminator - start;
1468 :
1469 24 : _state->tresult = cstring_to_text_with_len(start, len);
1470 : }
1471 : }
1472 :
1473 105486 : if (_state->next_scalar)
1474 : {
1475 : /* a de-escaped text value is wanted, so supply it */
1476 702 : _state->tresult = cstring_to_text(token);
1477 : /* make sure the next call to get_scalar doesn't overwrite it */
1478 702 : _state->next_scalar = false;
1479 : }
1480 :
1481 105486 : return JSON_SUCCESS;
1482 : }
1483 :
1484 : Datum
1485 270 : jsonb_extract_path(PG_FUNCTION_ARGS)
1486 : {
1487 270 : return get_jsonb_path_all(fcinfo, false);
1488 : }
1489 :
1490 : Datum
1491 180 : jsonb_extract_path_text(PG_FUNCTION_ARGS)
1492 : {
1493 180 : return get_jsonb_path_all(fcinfo, true);
1494 : }
1495 :
1496 : static Datum
1497 450 : get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
1498 : {
1499 450 : Jsonb *jb = PG_GETARG_JSONB_P(0);
1500 450 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
1501 : Datum *pathtext;
1502 : bool *pathnulls;
1503 : bool isnull;
1504 : int npath;
1505 : Datum res;
1506 :
1507 : /*
1508 : * If the array contains any null elements, return NULL, on the grounds
1509 : * that you'd have gotten NULL if any RHS value were NULL in a nested
1510 : * series of applications of the -> operator. (Note: because we also
1511 : * return NULL for error cases such as no-such-field, this is true
1512 : * regardless of the contents of the rest of the array.)
1513 : */
1514 450 : if (array_contains_nulls(path))
1515 12 : PG_RETURN_NULL();
1516 :
1517 438 : deconstruct_array_builtin(path, TEXTOID, &pathtext, &pathnulls, &npath);
1518 :
1519 438 : res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
1520 :
1521 438 : if (isnull)
1522 138 : PG_RETURN_NULL();
1523 : else
1524 300 : PG_RETURN_DATUM(res);
1525 : }
1526 :
1527 : Datum
1528 630 : jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
1529 : {
1530 630 : JsonbContainer *container = &jb->root;
1531 630 : JsonbValue *jbvp = NULL;
1532 : int i;
1533 630 : bool have_object = false,
1534 630 : have_array = false;
1535 :
1536 630 : *isnull = false;
1537 :
1538 : /* Identify whether we have object, array, or scalar at top-level */
1539 630 : if (JB_ROOT_IS_OBJECT(jb))
1540 420 : have_object = true;
1541 210 : else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
1542 126 : have_array = true;
1543 : else
1544 : {
1545 : Assert(JB_ROOT_IS_ARRAY(jb) && JB_ROOT_IS_SCALAR(jb));
1546 : /* Extract the scalar value, if it is what we'll return */
1547 84 : if (npath <= 0)
1548 36 : jbvp = getIthJsonbValueFromContainer(container, 0);
1549 : }
1550 :
1551 : /*
1552 : * If the array is empty, return the entire LHS object, on the grounds
1553 : * that we should do zero field or element extractions. For the
1554 : * non-scalar case we can just hand back the object without much work. For
1555 : * the scalar case, fall through and deal with the value below the loop.
1556 : * (This inconsistency arises because there's no easy way to generate a
1557 : * JsonbValue directly for root-level containers.)
1558 : */
1559 630 : if (npath <= 0 && jbvp == NULL)
1560 : {
1561 24 : if (as_text)
1562 : {
1563 12 : return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
1564 : container,
1565 12 : VARSIZE(jb))));
1566 : }
1567 : else
1568 : {
1569 : /* not text mode - just hand back the jsonb */
1570 12 : PG_RETURN_JSONB_P(jb);
1571 : }
1572 : }
1573 :
1574 1008 : for (i = 0; i < npath; i++)
1575 : {
1576 972 : if (have_object)
1577 : {
1578 624 : text *subscr = DatumGetTextPP(path[i]);
1579 :
1580 1248 : jbvp = getKeyJsonValueFromContainer(container,
1581 624 : VARDATA_ANY(subscr),
1582 624 : VARSIZE_ANY_EXHDR(subscr),
1583 : NULL);
1584 : }
1585 348 : else if (have_array)
1586 : {
1587 : int lindex;
1588 : uint32 index;
1589 270 : char *indextext = TextDatumGetCString(path[i]);
1590 : char *endptr;
1591 :
1592 270 : errno = 0;
1593 270 : lindex = strtoint(indextext, &endptr, 10);
1594 270 : if (endptr == indextext || *endptr != '\0' || errno != 0)
1595 : {
1596 36 : *isnull = true;
1597 42 : return PointerGetDatum(NULL);
1598 : }
1599 :
1600 234 : if (lindex >= 0)
1601 : {
1602 210 : index = (uint32) lindex;
1603 : }
1604 : else
1605 : {
1606 : /* Handle negative subscript */
1607 : uint32 nelements;
1608 :
1609 : /* Container must be array, but make sure */
1610 24 : if (!JsonContainerIsArray(container))
1611 0 : elog(ERROR, "not a jsonb array");
1612 :
1613 24 : nelements = JsonContainerSize(container);
1614 :
1615 24 : if (lindex == INT_MIN || -lindex > nelements)
1616 : {
1617 6 : *isnull = true;
1618 6 : return PointerGetDatum(NULL);
1619 : }
1620 : else
1621 18 : index = nelements + lindex;
1622 : }
1623 :
1624 228 : jbvp = getIthJsonbValueFromContainer(container, index);
1625 : }
1626 : else
1627 : {
1628 : /* scalar, extraction yields a null */
1629 78 : *isnull = true;
1630 78 : return PointerGetDatum(NULL);
1631 : }
1632 :
1633 852 : if (jbvp == NULL)
1634 : {
1635 78 : *isnull = true;
1636 78 : return PointerGetDatum(NULL);
1637 : }
1638 774 : else if (i == npath - 1)
1639 372 : break;
1640 :
1641 402 : if (jbvp->type == jbvBinary)
1642 : {
1643 372 : container = jbvp->val.binary.data;
1644 372 : have_object = JsonContainerIsObject(container);
1645 372 : have_array = JsonContainerIsArray(container);
1646 : Assert(!JsonContainerIsScalar(container));
1647 : }
1648 : else
1649 : {
1650 : Assert(IsAJsonbScalar(jbvp));
1651 30 : have_object = false;
1652 30 : have_array = false;
1653 : }
1654 : }
1655 :
1656 408 : if (as_text)
1657 : {
1658 114 : if (jbvp->type == jbvNull)
1659 : {
1660 24 : *isnull = true;
1661 24 : return PointerGetDatum(NULL);
1662 : }
1663 :
1664 90 : return PointerGetDatum(JsonbValueAsText(jbvp));
1665 : }
1666 : else
1667 : {
1668 294 : Jsonb *res = JsonbValueToJsonb(jbvp);
1669 :
1670 : /* not text mode - just hand back the jsonb */
1671 294 : PG_RETURN_JSONB_P(res);
1672 : }
1673 : }
1674 :
1675 : Datum
1676 246 : jsonb_set_element(Jsonb *jb, Datum *path, int path_len,
1677 : JsonbValue *newval)
1678 : {
1679 : JsonbValue *res;
1680 246 : JsonbParseState *state = NULL;
1681 : JsonbIterator *it;
1682 246 : bool *path_nulls = palloc0(path_len * sizeof(bool));
1683 :
1684 246 : if (newval->type == jbvArray && newval->val.array.rawScalar)
1685 0 : *newval = newval->val.array.elems[0];
1686 :
1687 246 : it = JsonbIteratorInit(&jb->root);
1688 :
1689 246 : res = setPath(&it, path, path_nulls, path_len, &state, 0, newval,
1690 : JB_PATH_CREATE | JB_PATH_FILL_GAPS |
1691 : JB_PATH_CONSISTENT_POSITION);
1692 :
1693 198 : pfree(path_nulls);
1694 :
1695 198 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
1696 : }
1697 :
1698 : static void
1699 108 : push_null_elements(JsonbParseState **ps, int num)
1700 : {
1701 : JsonbValue null;
1702 :
1703 108 : null.type = jbvNull;
1704 :
1705 408 : while (num-- > 0)
1706 300 : pushJsonbValue(ps, WJB_ELEM, &null);
1707 108 : }
1708 :
1709 : /*
1710 : * Prepare a new structure containing nested empty objects and arrays
1711 : * corresponding to the specified path, and assign a new value at the end of
1712 : * this path. E.g. the path [a][0][b] with the new value 1 will produce the
1713 : * structure {a: [{b: 1}]}.
1714 : *
1715 : * Caller is responsible to make sure such path does not exist yet.
1716 : */
1717 : static void
1718 72 : push_path(JsonbParseState **st, int level, Datum *path_elems,
1719 : bool *path_nulls, int path_len, JsonbValue *newval)
1720 : {
1721 : /*
1722 : * tpath contains expected type of an empty jsonb created at each level
1723 : * higher or equal than the current one, either jbvObject or jbvArray.
1724 : * Since it contains only information about path slice from level to the
1725 : * end, the access index must be normalized by level.
1726 : */
1727 72 : enum jbvType *tpath = palloc0((path_len - level) * sizeof(enum jbvType));
1728 : JsonbValue newkey;
1729 :
1730 : /*
1731 : * Create first part of the chain with beginning tokens. For the current
1732 : * level WJB_BEGIN_OBJECT/WJB_BEGIN_ARRAY was already created, so start
1733 : * with the next one.
1734 : */
1735 216 : for (int i = level + 1; i < path_len; i++)
1736 : {
1737 : char *c,
1738 : *badp;
1739 : int lindex;
1740 :
1741 144 : if (path_nulls[i])
1742 0 : break;
1743 :
1744 : /*
1745 : * Try to convert to an integer to find out the expected type, object
1746 : * or array.
1747 : */
1748 144 : c = TextDatumGetCString(path_elems[i]);
1749 144 : errno = 0;
1750 144 : lindex = strtoint(c, &badp, 10);
1751 144 : if (badp == c || *badp != '\0' || errno != 0)
1752 : {
1753 : /* text, an object is expected */
1754 66 : newkey.type = jbvString;
1755 66 : newkey.val.string.val = c;
1756 66 : newkey.val.string.len = strlen(c);
1757 :
1758 66 : (void) pushJsonbValue(st, WJB_BEGIN_OBJECT, NULL);
1759 66 : (void) pushJsonbValue(st, WJB_KEY, &newkey);
1760 :
1761 66 : tpath[i - level] = jbvObject;
1762 : }
1763 : else
1764 : {
1765 : /* integer, an array is expected */
1766 78 : (void) pushJsonbValue(st, WJB_BEGIN_ARRAY, NULL);
1767 :
1768 78 : push_null_elements(st, lindex);
1769 :
1770 78 : tpath[i - level] = jbvArray;
1771 : }
1772 : }
1773 :
1774 : /* Insert an actual value for either an object or array */
1775 72 : if (tpath[(path_len - level) - 1] == jbvArray)
1776 : {
1777 48 : (void) pushJsonbValue(st, WJB_ELEM, newval);
1778 : }
1779 : else
1780 24 : (void) pushJsonbValue(st, WJB_VALUE, newval);
1781 :
1782 : /*
1783 : * Close everything up to the last but one level. The last one will be
1784 : * closed outside of this function.
1785 : */
1786 216 : for (int i = path_len - 1; i > level; i--)
1787 : {
1788 144 : if (path_nulls[i])
1789 0 : break;
1790 :
1791 144 : if (tpath[i - level] == jbvObject)
1792 66 : (void) pushJsonbValue(st, WJB_END_OBJECT, NULL);
1793 : else
1794 78 : (void) pushJsonbValue(st, WJB_END_ARRAY, NULL);
1795 : }
1796 72 : }
1797 :
1798 : /*
1799 : * Return the text representation of the given JsonbValue.
1800 : */
1801 : static text *
1802 420 : JsonbValueAsText(JsonbValue *v)
1803 : {
1804 420 : switch (v->type)
1805 : {
1806 0 : case jbvNull:
1807 0 : return NULL;
1808 :
1809 24 : case jbvBool:
1810 24 : return v->val.boolean ?
1811 36 : cstring_to_text_with_len("true", 4) :
1812 12 : cstring_to_text_with_len("false", 5);
1813 :
1814 228 : case jbvString:
1815 228 : return cstring_to_text_with_len(v->val.string.val,
1816 : v->val.string.len);
1817 :
1818 42 : case jbvNumeric:
1819 : {
1820 : Datum cstr;
1821 :
1822 42 : cstr = DirectFunctionCall1(numeric_out,
1823 : PointerGetDatum(v->val.numeric));
1824 :
1825 42 : return cstring_to_text(DatumGetCString(cstr));
1826 : }
1827 :
1828 126 : case jbvBinary:
1829 : {
1830 : StringInfoData jtext;
1831 :
1832 126 : initStringInfo(&jtext);
1833 126 : (void) JsonbToCString(&jtext, v->val.binary.data,
1834 : v->val.binary.len);
1835 :
1836 126 : return cstring_to_text_with_len(jtext.data, jtext.len);
1837 : }
1838 :
1839 0 : default:
1840 0 : elog(ERROR, "unrecognized jsonb type: %d", (int) v->type);
1841 : return NULL;
1842 : }
1843 : }
1844 :
1845 : /*
1846 : * SQL function json_array_length(json) -> int
1847 : */
1848 : Datum
1849 24 : json_array_length(PG_FUNCTION_ARGS)
1850 : {
1851 24 : text *json = PG_GETARG_TEXT_PP(0);
1852 : AlenState *state;
1853 : JsonLexContext lex;
1854 : JsonSemAction *sem;
1855 :
1856 24 : state = palloc0(sizeof(AlenState));
1857 24 : state->lex = makeJsonLexContext(&lex, json, false);
1858 : /* palloc0 does this for us */
1859 : #if 0
1860 : state->count = 0;
1861 : #endif
1862 :
1863 24 : sem = palloc0(sizeof(JsonSemAction));
1864 24 : sem->semstate = (void *) state;
1865 24 : sem->object_start = alen_object_start;
1866 24 : sem->scalar = alen_scalar;
1867 24 : sem->array_element_start = alen_array_element_start;
1868 :
1869 24 : pg_parse_json_or_ereport(state->lex, sem);
1870 :
1871 12 : PG_RETURN_INT32(state->count);
1872 : }
1873 :
1874 : Datum
1875 312 : jsonb_array_length(PG_FUNCTION_ARGS)
1876 : {
1877 312 : Jsonb *jb = PG_GETARG_JSONB_P(0);
1878 :
1879 312 : if (JB_ROOT_IS_SCALAR(jb))
1880 6 : ereport(ERROR,
1881 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1882 : errmsg("cannot get array length of a scalar")));
1883 306 : else if (!JB_ROOT_IS_ARRAY(jb))
1884 6 : ereport(ERROR,
1885 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1886 : errmsg("cannot get array length of a non-array")));
1887 :
1888 300 : PG_RETURN_INT32(JB_ROOT_COUNT(jb));
1889 : }
1890 :
1891 : /*
1892 : * These next two checks ensure that the json is an array (since it can't be
1893 : * a scalar or an object).
1894 : */
1895 :
1896 : static JsonParseErrorType
1897 12 : alen_object_start(void *state)
1898 : {
1899 12 : AlenState *_state = (AlenState *) state;
1900 :
1901 : /* json structure check */
1902 12 : if (_state->lex->lex_level == 0)
1903 6 : ereport(ERROR,
1904 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1905 : errmsg("cannot get array length of a non-array")));
1906 :
1907 6 : return JSON_SUCCESS;
1908 : }
1909 :
1910 : static JsonParseErrorType
1911 48 : alen_scalar(void *state, char *token, JsonTokenType tokentype)
1912 : {
1913 48 : AlenState *_state = (AlenState *) state;
1914 :
1915 : /* json structure check */
1916 48 : if (_state->lex->lex_level == 0)
1917 6 : ereport(ERROR,
1918 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1919 : errmsg("cannot get array length of a scalar")));
1920 :
1921 42 : return JSON_SUCCESS;
1922 : }
1923 :
1924 : static JsonParseErrorType
1925 42 : alen_array_element_start(void *state, bool isnull)
1926 : {
1927 42 : AlenState *_state = (AlenState *) state;
1928 :
1929 : /* just count up all the level 1 elements */
1930 42 : if (_state->lex->lex_level == 1)
1931 30 : _state->count++;
1932 :
1933 42 : return JSON_SUCCESS;
1934 : }
1935 :
1936 : /*
1937 : * SQL function json_each and json_each_text
1938 : *
1939 : * decompose a json object into key value pairs.
1940 : *
1941 : * Unlike json_object_keys() these SRFs operate in materialize mode,
1942 : * stashing results into a Tuplestore object as they go.
1943 : * The construction of tuples is done using a temporary memory context
1944 : * that is cleared out after each tuple is built.
1945 : */
1946 : Datum
1947 12 : json_each(PG_FUNCTION_ARGS)
1948 : {
1949 12 : return each_worker(fcinfo, false);
1950 : }
1951 :
1952 : Datum
1953 12168 : jsonb_each(PG_FUNCTION_ARGS)
1954 : {
1955 12168 : return each_worker_jsonb(fcinfo, "jsonb_each", false);
1956 : }
1957 :
1958 : Datum
1959 12 : json_each_text(PG_FUNCTION_ARGS)
1960 : {
1961 12 : return each_worker(fcinfo, true);
1962 : }
1963 :
1964 : Datum
1965 24 : jsonb_each_text(PG_FUNCTION_ARGS)
1966 : {
1967 24 : return each_worker_jsonb(fcinfo, "jsonb_each_text", true);
1968 : }
1969 :
1970 : static Datum
1971 12192 : each_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
1972 : {
1973 12192 : Jsonb *jb = PG_GETARG_JSONB_P(0);
1974 : ReturnSetInfo *rsi;
1975 : MemoryContext old_cxt,
1976 : tmp_cxt;
1977 12192 : bool skipNested = false;
1978 : JsonbIterator *it;
1979 : JsonbValue v;
1980 : JsonbIteratorToken r;
1981 :
1982 12192 : if (!JB_ROOT_IS_OBJECT(jb))
1983 0 : ereport(ERROR,
1984 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1985 : errmsg("cannot call %s on a non-object",
1986 : funcname)));
1987 :
1988 12192 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
1989 12192 : InitMaterializedSRF(fcinfo, MAT_SRF_BLESS);
1990 :
1991 12192 : tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
1992 : "jsonb_each temporary cxt",
1993 : ALLOCSET_DEFAULT_SIZES);
1994 :
1995 12192 : it = JsonbIteratorInit(&jb->root);
1996 :
1997 94290 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
1998 : {
1999 82098 : skipNested = true;
2000 :
2001 82098 : if (r == WJB_KEY)
2002 : {
2003 : text *key;
2004 : Datum values[2];
2005 57714 : bool nulls[2] = {false, false};
2006 :
2007 : /* Use the tmp context so we can clean up after each tuple is done */
2008 57714 : old_cxt = MemoryContextSwitchTo(tmp_cxt);
2009 :
2010 57714 : key = cstring_to_text_with_len(v.val.string.val, v.val.string.len);
2011 :
2012 : /*
2013 : * The next thing the iterator fetches should be the value, no
2014 : * matter what shape it is.
2015 : */
2016 57714 : r = JsonbIteratorNext(&it, &v, skipNested);
2017 : Assert(r != WJB_DONE);
2018 :
2019 57714 : values[0] = PointerGetDatum(key);
2020 :
2021 57714 : if (as_text)
2022 : {
2023 114 : if (v.type == jbvNull)
2024 : {
2025 : /* a json null is an sql null in text mode */
2026 24 : nulls[1] = true;
2027 24 : values[1] = (Datum) NULL;
2028 : }
2029 : else
2030 90 : values[1] = PointerGetDatum(JsonbValueAsText(&v));
2031 : }
2032 : else
2033 : {
2034 : /* Not in text mode, just return the Jsonb */
2035 57600 : Jsonb *val = JsonbValueToJsonb(&v);
2036 :
2037 57600 : values[1] = PointerGetDatum(val);
2038 : }
2039 :
2040 57714 : tuplestore_putvalues(rsi->setResult, rsi->setDesc, values, nulls);
2041 :
2042 : /* clean up and switch back */
2043 57714 : MemoryContextSwitchTo(old_cxt);
2044 57714 : MemoryContextReset(tmp_cxt);
2045 : }
2046 : }
2047 :
2048 12192 : MemoryContextDelete(tmp_cxt);
2049 :
2050 12192 : PG_RETURN_NULL();
2051 : }
2052 :
2053 :
2054 : static Datum
2055 24 : each_worker(FunctionCallInfo fcinfo, bool as_text)
2056 : {
2057 24 : text *json = PG_GETARG_TEXT_PP(0);
2058 : JsonLexContext lex;
2059 : JsonSemAction *sem;
2060 : ReturnSetInfo *rsi;
2061 : EachState *state;
2062 :
2063 24 : state = palloc0(sizeof(EachState));
2064 24 : sem = palloc0(sizeof(JsonSemAction));
2065 :
2066 24 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
2067 :
2068 24 : InitMaterializedSRF(fcinfo, MAT_SRF_BLESS);
2069 24 : state->tuple_store = rsi->setResult;
2070 24 : state->ret_tdesc = rsi->setDesc;
2071 :
2072 24 : sem->semstate = (void *) state;
2073 24 : sem->array_start = each_array_start;
2074 24 : sem->scalar = each_scalar;
2075 24 : sem->object_field_start = each_object_field_start;
2076 24 : sem->object_field_end = each_object_field_end;
2077 :
2078 24 : state->normalize_results = as_text;
2079 24 : state->next_scalar = false;
2080 24 : state->lex = makeJsonLexContext(&lex, json, true);
2081 24 : state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
2082 : "json_each temporary cxt",
2083 : ALLOCSET_DEFAULT_SIZES);
2084 :
2085 24 : pg_parse_json_or_ereport(&lex, sem);
2086 :
2087 24 : MemoryContextDelete(state->tmp_cxt);
2088 24 : freeJsonLexContext(&lex);
2089 :
2090 24 : PG_RETURN_NULL();
2091 : }
2092 :
2093 :
2094 : static JsonParseErrorType
2095 126 : each_object_field_start(void *state, char *fname, bool isnull)
2096 : {
2097 126 : EachState *_state = (EachState *) state;
2098 :
2099 : /* save a pointer to where the value starts */
2100 126 : if (_state->lex->lex_level == 1)
2101 : {
2102 : /*
2103 : * next_scalar will be reset in the object_field_end handler, and
2104 : * since we know the value is a scalar there is no danger of it being
2105 : * on while recursing down the tree.
2106 : */
2107 102 : if (_state->normalize_results && _state->lex->token_type == JSON_TOKEN_STRING)
2108 12 : _state->next_scalar = true;
2109 : else
2110 90 : _state->result_start = _state->lex->token_start;
2111 : }
2112 :
2113 126 : return JSON_SUCCESS;
2114 : }
2115 :
2116 : static JsonParseErrorType
2117 126 : each_object_field_end(void *state, char *fname, bool isnull)
2118 : {
2119 126 : EachState *_state = (EachState *) state;
2120 : MemoryContext old_cxt;
2121 : int len;
2122 : text *val;
2123 : HeapTuple tuple;
2124 : Datum values[2];
2125 126 : bool nulls[2] = {false, false};
2126 :
2127 : /* skip over nested objects */
2128 126 : if (_state->lex->lex_level != 1)
2129 24 : return JSON_SUCCESS;
2130 :
2131 : /* use the tmp context so we can clean up after each tuple is done */
2132 102 : old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
2133 :
2134 102 : values[0] = CStringGetTextDatum(fname);
2135 :
2136 102 : if (isnull && _state->normalize_results)
2137 : {
2138 12 : nulls[1] = true;
2139 12 : values[1] = (Datum) 0;
2140 : }
2141 90 : else if (_state->next_scalar)
2142 : {
2143 12 : values[1] = CStringGetTextDatum(_state->normalized_scalar);
2144 12 : _state->next_scalar = false;
2145 : }
2146 : else
2147 : {
2148 78 : len = _state->lex->prev_token_terminator - _state->result_start;
2149 78 : val = cstring_to_text_with_len(_state->result_start, len);
2150 78 : values[1] = PointerGetDatum(val);
2151 : }
2152 :
2153 102 : tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
2154 :
2155 102 : tuplestore_puttuple(_state->tuple_store, tuple);
2156 :
2157 : /* clean up and switch back */
2158 102 : MemoryContextSwitchTo(old_cxt);
2159 102 : MemoryContextReset(_state->tmp_cxt);
2160 :
2161 102 : return JSON_SUCCESS;
2162 : }
2163 :
2164 : static JsonParseErrorType
2165 24 : each_array_start(void *state)
2166 : {
2167 24 : EachState *_state = (EachState *) state;
2168 :
2169 : /* json structure check */
2170 24 : if (_state->lex->lex_level == 0)
2171 0 : ereport(ERROR,
2172 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2173 : errmsg("cannot deconstruct an array as an object")));
2174 :
2175 24 : return JSON_SUCCESS;
2176 : }
2177 :
2178 : static JsonParseErrorType
2179 150 : each_scalar(void *state, char *token, JsonTokenType tokentype)
2180 : {
2181 150 : EachState *_state = (EachState *) state;
2182 :
2183 : /* json structure check */
2184 150 : if (_state->lex->lex_level == 0)
2185 0 : ereport(ERROR,
2186 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2187 : errmsg("cannot deconstruct a scalar")));
2188 :
2189 : /* supply de-escaped value if required */
2190 150 : if (_state->next_scalar)
2191 12 : _state->normalized_scalar = token;
2192 :
2193 150 : return JSON_SUCCESS;
2194 : }
2195 :
2196 : /*
2197 : * SQL functions json_array_elements and json_array_elements_text
2198 : *
2199 : * get the elements from a json array
2200 : *
2201 : * a lot of this processing is similar to the json_each* functions
2202 : */
2203 :
2204 : Datum
2205 36 : jsonb_array_elements(PG_FUNCTION_ARGS)
2206 : {
2207 36 : return elements_worker_jsonb(fcinfo, "jsonb_array_elements", false);
2208 : }
2209 :
2210 : Datum
2211 12 : jsonb_array_elements_text(PG_FUNCTION_ARGS)
2212 : {
2213 12 : return elements_worker_jsonb(fcinfo, "jsonb_array_elements_text", true);
2214 : }
2215 :
2216 : static Datum
2217 48 : elements_worker_jsonb(FunctionCallInfo fcinfo, const char *funcname,
2218 : bool as_text)
2219 : {
2220 48 : Jsonb *jb = PG_GETARG_JSONB_P(0);
2221 : ReturnSetInfo *rsi;
2222 : MemoryContext old_cxt,
2223 : tmp_cxt;
2224 48 : bool skipNested = false;
2225 : JsonbIterator *it;
2226 : JsonbValue v;
2227 : JsonbIteratorToken r;
2228 :
2229 48 : if (JB_ROOT_IS_SCALAR(jb))
2230 0 : ereport(ERROR,
2231 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2232 : errmsg("cannot extract elements from a scalar")));
2233 48 : else if (!JB_ROOT_IS_ARRAY(jb))
2234 0 : ereport(ERROR,
2235 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2236 : errmsg("cannot extract elements from an object")));
2237 :
2238 48 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
2239 :
2240 48 : InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC | MAT_SRF_BLESS);
2241 :
2242 48 : tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
2243 : "jsonb_array_elements temporary cxt",
2244 : ALLOCSET_DEFAULT_SIZES);
2245 :
2246 48 : it = JsonbIteratorInit(&jb->root);
2247 :
2248 324 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
2249 : {
2250 276 : skipNested = true;
2251 :
2252 276 : if (r == WJB_ELEM)
2253 : {
2254 : Datum values[1];
2255 180 : bool nulls[1] = {false};
2256 :
2257 : /* use the tmp context so we can clean up after each tuple is done */
2258 180 : old_cxt = MemoryContextSwitchTo(tmp_cxt);
2259 :
2260 180 : if (as_text)
2261 : {
2262 84 : if (v.type == jbvNull)
2263 : {
2264 : /* a json null is an sql null in text mode */
2265 12 : nulls[0] = true;
2266 12 : values[0] = (Datum) NULL;
2267 : }
2268 : else
2269 72 : values[0] = PointerGetDatum(JsonbValueAsText(&v));
2270 : }
2271 : else
2272 : {
2273 : /* Not in text mode, just return the Jsonb */
2274 96 : Jsonb *val = JsonbValueToJsonb(&v);
2275 :
2276 96 : values[0] = PointerGetDatum(val);
2277 : }
2278 :
2279 180 : tuplestore_putvalues(rsi->setResult, rsi->setDesc, values, nulls);
2280 :
2281 : /* clean up and switch back */
2282 180 : MemoryContextSwitchTo(old_cxt);
2283 180 : MemoryContextReset(tmp_cxt);
2284 : }
2285 : }
2286 :
2287 48 : MemoryContextDelete(tmp_cxt);
2288 :
2289 48 : PG_RETURN_NULL();
2290 : }
2291 :
2292 : Datum
2293 390 : json_array_elements(PG_FUNCTION_ARGS)
2294 : {
2295 390 : return elements_worker(fcinfo, "json_array_elements", false);
2296 : }
2297 :
2298 : Datum
2299 12 : json_array_elements_text(PG_FUNCTION_ARGS)
2300 : {
2301 12 : return elements_worker(fcinfo, "json_array_elements_text", true);
2302 : }
2303 :
2304 : static Datum
2305 402 : elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
2306 : {
2307 402 : text *json = PG_GETARG_TEXT_PP(0);
2308 : JsonLexContext lex;
2309 : JsonSemAction *sem;
2310 : ReturnSetInfo *rsi;
2311 : ElementsState *state;
2312 :
2313 : /* elements only needs escaped strings when as_text */
2314 402 : makeJsonLexContext(&lex, json, as_text);
2315 :
2316 402 : state = palloc0(sizeof(ElementsState));
2317 402 : sem = palloc0(sizeof(JsonSemAction));
2318 :
2319 402 : InitMaterializedSRF(fcinfo, MAT_SRF_USE_EXPECTED_DESC | MAT_SRF_BLESS);
2320 402 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
2321 402 : state->tuple_store = rsi->setResult;
2322 402 : state->ret_tdesc = rsi->setDesc;
2323 :
2324 402 : sem->semstate = (void *) state;
2325 402 : sem->object_start = elements_object_start;
2326 402 : sem->scalar = elements_scalar;
2327 402 : sem->array_element_start = elements_array_element_start;
2328 402 : sem->array_element_end = elements_array_element_end;
2329 :
2330 402 : state->function_name = funcname;
2331 402 : state->normalize_results = as_text;
2332 402 : state->next_scalar = false;
2333 402 : state->lex = &lex;
2334 402 : state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
2335 : "json_array_elements temporary cxt",
2336 : ALLOCSET_DEFAULT_SIZES);
2337 :
2338 402 : pg_parse_json_or_ereport(&lex, sem);
2339 :
2340 402 : MemoryContextDelete(state->tmp_cxt);
2341 402 : freeJsonLexContext(&lex);
2342 :
2343 402 : PG_RETURN_NULL();
2344 : }
2345 :
2346 : static JsonParseErrorType
2347 2028 : elements_array_element_start(void *state, bool isnull)
2348 : {
2349 2028 : ElementsState *_state = (ElementsState *) state;
2350 :
2351 : /* save a pointer to where the value starts */
2352 2028 : if (_state->lex->lex_level == 1)
2353 : {
2354 : /*
2355 : * next_scalar will be reset in the array_element_end handler, and
2356 : * since we know the value is a scalar there is no danger of it being
2357 : * on while recursing down the tree.
2358 : */
2359 678 : if (_state->normalize_results && _state->lex->token_type == JSON_TOKEN_STRING)
2360 12 : _state->next_scalar = true;
2361 : else
2362 666 : _state->result_start = _state->lex->token_start;
2363 : }
2364 :
2365 2028 : return JSON_SUCCESS;
2366 : }
2367 :
2368 : static JsonParseErrorType
2369 2028 : elements_array_element_end(void *state, bool isnull)
2370 : {
2371 2028 : ElementsState *_state = (ElementsState *) state;
2372 : MemoryContext old_cxt;
2373 : int len;
2374 : text *val;
2375 : HeapTuple tuple;
2376 : Datum values[1];
2377 2028 : bool nulls[1] = {false};
2378 :
2379 : /* skip over nested objects */
2380 2028 : if (_state->lex->lex_level != 1)
2381 1350 : return JSON_SUCCESS;
2382 :
2383 : /* use the tmp context so we can clean up after each tuple is done */
2384 678 : old_cxt = MemoryContextSwitchTo(_state->tmp_cxt);
2385 :
2386 678 : if (isnull && _state->normalize_results)
2387 : {
2388 12 : nulls[0] = true;
2389 12 : values[0] = (Datum) NULL;
2390 : }
2391 666 : else if (_state->next_scalar)
2392 : {
2393 12 : values[0] = CStringGetTextDatum(_state->normalized_scalar);
2394 12 : _state->next_scalar = false;
2395 : }
2396 : else
2397 : {
2398 654 : len = _state->lex->prev_token_terminator - _state->result_start;
2399 654 : val = cstring_to_text_with_len(_state->result_start, len);
2400 654 : values[0] = PointerGetDatum(val);
2401 : }
2402 :
2403 678 : tuple = heap_form_tuple(_state->ret_tdesc, values, nulls);
2404 :
2405 678 : tuplestore_puttuple(_state->tuple_store, tuple);
2406 :
2407 : /* clean up and switch back */
2408 678 : MemoryContextSwitchTo(old_cxt);
2409 678 : MemoryContextReset(_state->tmp_cxt);
2410 :
2411 678 : return JSON_SUCCESS;
2412 : }
2413 :
2414 : static JsonParseErrorType
2415 1716 : elements_object_start(void *state)
2416 : {
2417 1716 : ElementsState *_state = (ElementsState *) state;
2418 :
2419 : /* json structure check */
2420 1716 : if (_state->lex->lex_level == 0)
2421 0 : ereport(ERROR,
2422 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2423 : errmsg("cannot call %s on a non-array",
2424 : _state->function_name)));
2425 :
2426 1716 : return JSON_SUCCESS;
2427 : }
2428 :
2429 : static JsonParseErrorType
2430 25536 : elements_scalar(void *state, char *token, JsonTokenType tokentype)
2431 : {
2432 25536 : ElementsState *_state = (ElementsState *) state;
2433 :
2434 : /* json structure check */
2435 25536 : if (_state->lex->lex_level == 0)
2436 0 : ereport(ERROR,
2437 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2438 : errmsg("cannot call %s on a scalar",
2439 : _state->function_name)));
2440 :
2441 : /* supply de-escaped value if required */
2442 25536 : if (_state->next_scalar)
2443 12 : _state->normalized_scalar = token;
2444 :
2445 25536 : return JSON_SUCCESS;
2446 : }
2447 :
2448 : /*
2449 : * SQL function json_populate_record
2450 : *
2451 : * set fields in a record from the argument json
2452 : *
2453 : * Code adapted shamelessly from hstore's populate_record
2454 : * which is in turn partly adapted from record_out.
2455 : *
2456 : * The json is decomposed into a hash table, in which each
2457 : * field in the record is then looked up by name. For jsonb
2458 : * we fetch the values direct from the object.
2459 : */
2460 : Datum
2461 882 : jsonb_populate_record(PG_FUNCTION_ARGS)
2462 : {
2463 882 : return populate_record_worker(fcinfo, "jsonb_populate_record",
2464 : false, true, NULL);
2465 : }
2466 :
2467 : /*
2468 : * SQL function that can be used for testing json_populate_record().
2469 : *
2470 : * Returns false if json_populate_record() encounters an error for the
2471 : * provided input JSON object, true otherwise.
2472 : */
2473 : Datum
2474 60 : jsonb_populate_record_valid(PG_FUNCTION_ARGS)
2475 : {
2476 60 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2477 :
2478 60 : (void) populate_record_worker(fcinfo, "jsonb_populate_record",
2479 : false, true, (Node *) &escontext);
2480 :
2481 60 : return BoolGetDatum(!escontext.error_occurred);
2482 : }
2483 :
2484 : Datum
2485 102 : jsonb_to_record(PG_FUNCTION_ARGS)
2486 : {
2487 102 : return populate_record_worker(fcinfo, "jsonb_to_record",
2488 : false, false, NULL);
2489 : }
2490 :
2491 : Datum
2492 822 : json_populate_record(PG_FUNCTION_ARGS)
2493 : {
2494 822 : return populate_record_worker(fcinfo, "json_populate_record",
2495 : true, true, NULL);
2496 : }
2497 :
2498 : Datum
2499 102 : json_to_record(PG_FUNCTION_ARGS)
2500 : {
2501 102 : return populate_record_worker(fcinfo, "json_to_record",
2502 : true, false, NULL);
2503 : }
2504 :
2505 : /* helper function for diagnostics */
2506 : static void
2507 414 : populate_array_report_expected_array(PopulateArrayContext *ctx, int ndim)
2508 : {
2509 414 : if (ndim <= 0)
2510 : {
2511 354 : if (ctx->colname)
2512 108 : errsave(ctx->escontext,
2513 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2514 : errmsg("expected JSON array"),
2515 : errhint("See the value of key \"%s\".", ctx->colname)));
2516 : else
2517 246 : errsave(ctx->escontext,
2518 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2519 : errmsg("expected JSON array")));
2520 246 : return;
2521 : }
2522 : else
2523 : {
2524 : StringInfoData indices;
2525 : int i;
2526 :
2527 60 : initStringInfo(&indices);
2528 :
2529 : Assert(ctx->ndims > 0 && ndim < ctx->ndims);
2530 :
2531 120 : for (i = 0; i < ndim; i++)
2532 60 : appendStringInfo(&indices, "[%d]", ctx->sizes[i]);
2533 :
2534 60 : if (ctx->colname)
2535 60 : errsave(ctx->escontext,
2536 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2537 : errmsg("expected JSON array"),
2538 : errhint("See the array element %s of key \"%s\".",
2539 : indices.data, ctx->colname)));
2540 : else
2541 0 : errsave(ctx->escontext,
2542 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2543 : errmsg("expected JSON array"),
2544 : errhint("See the array element %s.",
2545 : indices.data)));
2546 0 : return;
2547 : }
2548 : }
2549 :
2550 : /*
2551 : * Validate and set ndims for populating an array with some
2552 : * populate_array_*() function.
2553 : *
2554 : * Returns false if the input (ndims) is erroneous.
2555 : */
2556 : static bool
2557 1836 : populate_array_assign_ndims(PopulateArrayContext *ctx, int ndims)
2558 : {
2559 : int i;
2560 :
2561 : Assert(ctx->ndims <= 0);
2562 :
2563 1836 : if (ndims <= 0)
2564 : {
2565 48 : populate_array_report_expected_array(ctx, ndims);
2566 : /* Getting here means the error was reported softly. */
2567 : Assert(SOFT_ERROR_OCCURRED(ctx->escontext));
2568 0 : return false;
2569 : }
2570 :
2571 1788 : ctx->ndims = ndims;
2572 1788 : ctx->dims = palloc(sizeof(int) * ndims);
2573 1788 : ctx->sizes = palloc0(sizeof(int) * ndims);
2574 :
2575 3936 : for (i = 0; i < ndims; i++)
2576 2148 : ctx->dims[i] = -1; /* dimensions are unknown yet */
2577 :
2578 1788 : return true;
2579 : }
2580 :
2581 : /*
2582 : * Check the populated subarray dimension
2583 : *
2584 : * Returns false if the input (ndims) is erroneous.
2585 : */
2586 : static bool
2587 1554 : populate_array_check_dimension(PopulateArrayContext *ctx, int ndim)
2588 : {
2589 1554 : int dim = ctx->sizes[ndim]; /* current dimension counter */
2590 :
2591 1554 : if (ctx->dims[ndim] == -1)
2592 1146 : ctx->dims[ndim] = dim; /* assign dimension if not yet known */
2593 408 : else if (ctx->dims[ndim] != dim)
2594 60 : ereturn(ctx->escontext, false,
2595 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
2596 : errmsg("malformed JSON array"),
2597 : errdetail("Multidimensional arrays must have "
2598 : "sub-arrays with matching dimensions.")));
2599 :
2600 : /* reset the current array dimension size counter */
2601 1494 : ctx->sizes[ndim] = 0;
2602 :
2603 : /* increment the parent dimension counter if it is a nested sub-array */
2604 1494 : if (ndim > 0)
2605 708 : ctx->sizes[ndim - 1]++;
2606 :
2607 1494 : return true;
2608 : }
2609 :
2610 : /*
2611 : * Returns true if the array element value was successfully extracted from jsv
2612 : * and added to ctx->astate. False if an error occurred when doing so.
2613 : */
2614 : static bool
2615 6162 : populate_array_element(PopulateArrayContext *ctx, int ndim, JsValue *jsv)
2616 : {
2617 : Datum element;
2618 : bool element_isnull;
2619 :
2620 : /* populate the array element */
2621 6162 : element = populate_record_field(ctx->aio->element_info,
2622 6162 : ctx->aio->element_type,
2623 6162 : ctx->aio->element_typmod,
2624 : NULL, ctx->mcxt, PointerGetDatum(NULL),
2625 : jsv, &element_isnull, ctx->escontext);
2626 : /* Nothing to do on an error. */
2627 6132 : if (SOFT_ERROR_OCCURRED(ctx->escontext))
2628 6 : return false;
2629 :
2630 6126 : accumArrayResult(ctx->astate, element, element_isnull,
2631 6126 : ctx->aio->element_type, ctx->acxt);
2632 :
2633 : Assert(ndim > 0);
2634 6126 : ctx->sizes[ndim - 1]++; /* increment current dimension counter */
2635 :
2636 6126 : return true;
2637 : }
2638 :
2639 : /* json object start handler for populate_array_json() */
2640 : static JsonParseErrorType
2641 648 : populate_array_object_start(void *_state)
2642 : {
2643 648 : PopulateArrayState *state = (PopulateArrayState *) _state;
2644 648 : int ndim = state->lex->lex_level;
2645 :
2646 648 : if (state->ctx->ndims <= 0)
2647 : {
2648 312 : if (!populate_array_assign_ndims(state->ctx, ndim))
2649 0 : return JSON_SEM_ACTION_FAILED;
2650 : }
2651 336 : else if (ndim < state->ctx->ndims)
2652 : {
2653 12 : populate_array_report_expected_array(state->ctx, ndim);
2654 : /* Getting here means the error was reported softly. */
2655 : Assert(SOFT_ERROR_OCCURRED(state->ctx->escontext));
2656 0 : return JSON_SEM_ACTION_FAILED;
2657 : }
2658 :
2659 636 : return JSON_SUCCESS;
2660 : }
2661 :
2662 : /* json array end handler for populate_array_json() */
2663 : static JsonParseErrorType
2664 1152 : populate_array_array_end(void *_state)
2665 : {
2666 1152 : PopulateArrayState *state = (PopulateArrayState *) _state;
2667 1152 : PopulateArrayContext *ctx = state->ctx;
2668 1152 : int ndim = state->lex->lex_level;
2669 :
2670 1152 : if (ctx->ndims <= 0)
2671 : {
2672 12 : if (!populate_array_assign_ndims(ctx, ndim + 1))
2673 0 : return JSON_SEM_ACTION_FAILED;
2674 : }
2675 :
2676 1152 : if (ndim < ctx->ndims)
2677 : {
2678 : /* Report if an error occurred. */
2679 1146 : if (!populate_array_check_dimension(ctx, ndim))
2680 0 : return JSON_SEM_ACTION_FAILED;
2681 : }
2682 :
2683 1128 : return JSON_SUCCESS;
2684 : }
2685 :
2686 : /* json array element start handler for populate_array_json() */
2687 : static JsonParseErrorType
2688 3366 : populate_array_element_start(void *_state, bool isnull)
2689 : {
2690 3366 : PopulateArrayState *state = (PopulateArrayState *) _state;
2691 3366 : int ndim = state->lex->lex_level;
2692 :
2693 3366 : if (state->ctx->ndims <= 0 || ndim == state->ctx->ndims)
2694 : {
2695 : /* remember current array element start */
2696 3120 : state->element_start = state->lex->token_start;
2697 3120 : state->element_type = state->lex->token_type;
2698 3120 : state->element_scalar = NULL;
2699 : }
2700 :
2701 3366 : return JSON_SUCCESS;
2702 : }
2703 :
2704 : /* json array element end handler for populate_array_json() */
2705 : static JsonParseErrorType
2706 3312 : populate_array_element_end(void *_state, bool isnull)
2707 : {
2708 3312 : PopulateArrayState *state = (PopulateArrayState *) _state;
2709 3312 : PopulateArrayContext *ctx = state->ctx;
2710 3312 : int ndim = state->lex->lex_level;
2711 :
2712 : Assert(ctx->ndims > 0);
2713 :
2714 3312 : if (ndim == ctx->ndims)
2715 : {
2716 : JsValue jsv;
2717 :
2718 2952 : jsv.is_json = true;
2719 2952 : jsv.val.json.type = state->element_type;
2720 :
2721 2952 : if (isnull)
2722 : {
2723 : Assert(jsv.val.json.type == JSON_TOKEN_NULL);
2724 708 : jsv.val.json.str = NULL;
2725 708 : jsv.val.json.len = 0;
2726 : }
2727 2244 : else if (state->element_scalar)
2728 : {
2729 1608 : jsv.val.json.str = state->element_scalar;
2730 1608 : jsv.val.json.len = -1; /* null-terminated */
2731 : }
2732 : else
2733 : {
2734 636 : jsv.val.json.str = state->element_start;
2735 636 : jsv.val.json.len = (state->lex->prev_token_terminator -
2736 636 : state->element_start) * sizeof(char);
2737 : }
2738 :
2739 : /* Report if an error occurred. */
2740 2952 : if (!populate_array_element(ctx, ndim, &jsv))
2741 0 : return JSON_SEM_ACTION_FAILED;
2742 : }
2743 :
2744 3300 : return JSON_SUCCESS;
2745 : }
2746 :
2747 : /* json scalar handler for populate_array_json() */
2748 : static JsonParseErrorType
2749 3654 : populate_array_scalar(void *_state, char *token, JsonTokenType tokentype)
2750 : {
2751 3654 : PopulateArrayState *state = (PopulateArrayState *) _state;
2752 3654 : PopulateArrayContext *ctx = state->ctx;
2753 3654 : int ndim = state->lex->lex_level;
2754 :
2755 3654 : if (ctx->ndims <= 0)
2756 : {
2757 576 : if (!populate_array_assign_ndims(ctx, ndim))
2758 0 : return JSON_SEM_ACTION_FAILED;
2759 : }
2760 3078 : else if (ndim < ctx->ndims)
2761 : {
2762 18 : populate_array_report_expected_array(ctx, ndim);
2763 : /* Getting here means the error was reported softly. */
2764 : Assert(SOFT_ERROR_OCCURRED(ctx->escontext));
2765 0 : return JSON_SEM_ACTION_FAILED;
2766 : }
2767 :
2768 3588 : if (ndim == ctx->ndims)
2769 : {
2770 : /* remember the scalar element token */
2771 2316 : state->element_scalar = token;
2772 : /* element_type must already be set in populate_array_element_start() */
2773 : Assert(state->element_type == tokentype);
2774 : }
2775 :
2776 3588 : return JSON_SUCCESS;
2777 : }
2778 :
2779 : /*
2780 : * Parse a json array and populate array
2781 : *
2782 : * Returns false if an error occurs when parsing.
2783 : */
2784 : static bool
2785 900 : populate_array_json(PopulateArrayContext *ctx, char *json, int len)
2786 : {
2787 : PopulateArrayState state;
2788 : JsonSemAction sem;
2789 :
2790 900 : state.lex = makeJsonLexContextCstringLen(NULL, json, len,
2791 : GetDatabaseEncoding(), true);
2792 900 : state.ctx = ctx;
2793 :
2794 900 : memset(&sem, 0, sizeof(sem));
2795 900 : sem.semstate = (void *) &state;
2796 900 : sem.object_start = populate_array_object_start;
2797 900 : sem.array_end = populate_array_array_end;
2798 900 : sem.array_element_start = populate_array_element_start;
2799 900 : sem.array_element_end = populate_array_element_end;
2800 900 : sem.scalar = populate_array_scalar;
2801 :
2802 900 : if (pg_parse_json_or_errsave(state.lex, &sem, ctx->escontext))
2803 : {
2804 : /* number of dimensions should be already known */
2805 : Assert(ctx->ndims > 0 && ctx->dims);
2806 : }
2807 :
2808 786 : freeJsonLexContext(state.lex);
2809 :
2810 786 : return !SOFT_ERROR_OCCURRED(ctx->escontext);
2811 : }
2812 :
2813 : /*
2814 : * populate_array_dim_jsonb() -- Iterate recursively through jsonb sub-array
2815 : * elements and accumulate result using given ArrayBuildState.
2816 : *
2817 : * Returns false if we return partway through because of an error in a
2818 : * subroutine.
2819 : */
2820 : static bool
2821 1680 : populate_array_dim_jsonb(PopulateArrayContext *ctx, /* context */
2822 : JsonbValue *jbv, /* jsonb sub-array */
2823 : int ndim) /* current dimension */
2824 : {
2825 1680 : JsonbContainer *jbc = jbv->val.binary.data;
2826 : JsonbIterator *it;
2827 : JsonbIteratorToken tok;
2828 : JsonbValue val;
2829 : JsValue jsv;
2830 :
2831 1680 : check_stack_depth();
2832 :
2833 : /* Even scalars can end up here thanks to ExecEvalJsonCoercion(). */
2834 1680 : if (jbv->type != jbvBinary || !JsonContainerIsArray(jbc) ||
2835 1554 : JsonContainerIsScalar(jbc))
2836 : {
2837 336 : populate_array_report_expected_array(ctx, ndim - 1);
2838 : /* Getting here means the error was reported softly. */
2839 : Assert(SOFT_ERROR_OCCURRED(ctx->escontext));
2840 246 : return false;
2841 : }
2842 :
2843 1344 : it = JsonbIteratorInit(jbc);
2844 :
2845 1344 : tok = JsonbIteratorNext(&it, &val, true);
2846 : Assert(tok == WJB_BEGIN_ARRAY);
2847 :
2848 1344 : tok = JsonbIteratorNext(&it, &val, true);
2849 :
2850 : /*
2851 : * If the number of dimensions is not yet known and we have found end of
2852 : * the array, or the first child element is not an array, then assign the
2853 : * number of dimensions now.
2854 : */
2855 1344 : if (ctx->ndims <= 0 &&
2856 1116 : (tok == WJB_END_ARRAY ||
2857 1116 : (tok == WJB_ELEM &&
2858 1116 : (val.type != jbvBinary ||
2859 522 : !JsonContainerIsArray(val.val.binary.data)))))
2860 : {
2861 936 : if (!populate_array_assign_ndims(ctx, ndim))
2862 0 : return false;
2863 : }
2864 :
2865 1344 : jsv.is_json = false;
2866 1344 : jsv.val.jsonb = &val;
2867 :
2868 : /* process all the array elements */
2869 4902 : while (tok == WJB_ELEM)
2870 : {
2871 : /*
2872 : * Recurse only if the dimensions of dimensions is still unknown or if
2873 : * it is not the innermost dimension.
2874 : */
2875 3648 : if (ctx->ndims > 0 && ndim >= ctx->ndims)
2876 : {
2877 3210 : if (!populate_array_element(ctx, ndim, &jsv))
2878 6 : return false;
2879 : }
2880 : else
2881 : {
2882 : /* populate child sub-array */
2883 438 : if (!populate_array_dim_jsonb(ctx, &val, ndim + 1))
2884 0 : return false;
2885 :
2886 : /* number of dimensions should be already known */
2887 : Assert(ctx->ndims > 0 && ctx->dims);
2888 :
2889 408 : if (!populate_array_check_dimension(ctx, ndim))
2890 6 : return false;
2891 : }
2892 :
2893 3558 : tok = JsonbIteratorNext(&it, &val, true);
2894 : }
2895 :
2896 : Assert(tok == WJB_END_ARRAY);
2897 :
2898 : /* free iterator, iterating until WJB_DONE */
2899 1254 : tok = JsonbIteratorNext(&it, &val, true);
2900 : Assert(tok == WJB_DONE && !it);
2901 :
2902 1254 : return true;
2903 : }
2904 :
2905 : /*
2906 : * Recursively populate an array from json/jsonb
2907 : *
2908 : * *isnull is set to true if an error is reported during parsing.
2909 : */
2910 : static Datum
2911 2142 : populate_array(ArrayIOData *aio,
2912 : const char *colname,
2913 : MemoryContext mcxt,
2914 : JsValue *jsv,
2915 : bool *isnull,
2916 : Node *escontext)
2917 : {
2918 : PopulateArrayContext ctx;
2919 : Datum result;
2920 : int *lbs;
2921 : int i;
2922 :
2923 2142 : ctx.aio = aio;
2924 2142 : ctx.mcxt = mcxt;
2925 2142 : ctx.acxt = CurrentMemoryContext;
2926 2142 : ctx.astate = initArrayResult(aio->element_type, ctx.acxt, true);
2927 2142 : ctx.colname = colname;
2928 2142 : ctx.ndims = 0; /* unknown yet */
2929 2142 : ctx.dims = NULL;
2930 2142 : ctx.sizes = NULL;
2931 2142 : ctx.escontext = escontext;
2932 :
2933 2142 : if (jsv->is_json)
2934 : {
2935 : /* Return null if an error was found. */
2936 900 : if (!populate_array_json(&ctx, jsv->val.json.str,
2937 900 : jsv->val.json.len >= 0 ? jsv->val.json.len
2938 900 : : strlen(jsv->val.json.str)))
2939 : {
2940 0 : *isnull = true;
2941 0 : return (Datum) 0;
2942 : }
2943 : }
2944 : else
2945 : {
2946 : /* Return null if an error was found. */
2947 1242 : if (!populate_array_dim_jsonb(&ctx, jsv->val.jsonb, 1))
2948 : {
2949 258 : *isnull = true;
2950 258 : return (Datum) 0;
2951 : }
2952 846 : ctx.dims[0] = ctx.sizes[0];
2953 : }
2954 :
2955 : Assert(ctx.ndims > 0);
2956 :
2957 1632 : lbs = palloc(sizeof(int) * ctx.ndims);
2958 :
2959 3492 : for (i = 0; i < ctx.ndims; i++)
2960 1860 : lbs[i] = 1;
2961 :
2962 1632 : result = makeMdArrayResult(ctx.astate, ctx.ndims, ctx.dims, lbs,
2963 : ctx.acxt, true);
2964 :
2965 1632 : pfree(ctx.dims);
2966 1632 : pfree(ctx.sizes);
2967 1632 : pfree(lbs);
2968 :
2969 1632 : *isnull = false;
2970 1632 : return result;
2971 : }
2972 :
2973 : /*
2974 : * Returns false if an error occurs, provided escontext points to an
2975 : * ErrorSaveContext.
2976 : */
2977 : static bool
2978 3954 : JsValueToJsObject(JsValue *jsv, JsObject *jso, Node *escontext)
2979 : {
2980 3954 : jso->is_json = jsv->is_json;
2981 :
2982 3954 : if (jsv->is_json)
2983 : {
2984 : /* convert plain-text json into a hash table */
2985 1866 : jso->val.json_hash =
2986 1884 : get_json_object_as_hash(jsv->val.json.str,
2987 1884 : jsv->val.json.len >= 0
2988 : ? jsv->val.json.len
2989 342 : : strlen(jsv->val.json.str),
2990 : "populate_composite",
2991 : escontext);
2992 : Assert(jso->val.json_hash != NULL || SOFT_ERROR_OCCURRED(escontext));
2993 : }
2994 : else
2995 : {
2996 2070 : JsonbValue *jbv = jsv->val.jsonb;
2997 :
2998 2070 : if (jbv->type == jbvBinary &&
2999 2058 : JsonContainerIsObject(jbv->val.binary.data))
3000 : {
3001 2040 : jso->val.jsonb_cont = jbv->val.binary.data;
3002 : }
3003 : else
3004 : {
3005 : bool is_scalar;
3006 :
3007 48 : is_scalar = IsAJsonbScalar(jbv) ||
3008 18 : (jbv->type == jbvBinary &&
3009 18 : JsonContainerIsScalar(jbv->val.binary.data));
3010 30 : errsave(escontext,
3011 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3012 : is_scalar
3013 : ? errmsg("cannot call %s on a scalar",
3014 : "populate_composite")
3015 : : errmsg("cannot call %s on an array",
3016 : "populate_composite")));
3017 : }
3018 : }
3019 :
3020 3912 : return !SOFT_ERROR_OCCURRED(escontext);
3021 : }
3022 :
3023 : /* acquire or update cached tuple descriptor for a composite type */
3024 : static void
3025 4752 : update_cached_tupdesc(CompositeIOData *io, MemoryContext mcxt)
3026 : {
3027 4752 : if (!io->tupdesc ||
3028 2628 : io->tupdesc->tdtypeid != io->base_typid ||
3029 2628 : io->tupdesc->tdtypmod != io->base_typmod)
3030 : {
3031 2124 : TupleDesc tupdesc = lookup_rowtype_tupdesc(io->base_typid,
3032 : io->base_typmod);
3033 : MemoryContext oldcxt;
3034 :
3035 2124 : if (io->tupdesc)
3036 0 : FreeTupleDesc(io->tupdesc);
3037 :
3038 : /* copy tuple desc without constraints into cache memory context */
3039 2124 : oldcxt = MemoryContextSwitchTo(mcxt);
3040 2124 : io->tupdesc = CreateTupleDescCopy(tupdesc);
3041 2124 : MemoryContextSwitchTo(oldcxt);
3042 :
3043 2124 : ReleaseTupleDesc(tupdesc);
3044 : }
3045 4752 : }
3046 :
3047 : /*
3048 : * Recursively populate a composite (row type) value from json/jsonb
3049 : *
3050 : * Returns null if an error occurs in a subroutine, provided escontext points
3051 : * to an ErrorSaveContext.
3052 : */
3053 : static Datum
3054 3954 : populate_composite(CompositeIOData *io,
3055 : Oid typid,
3056 : const char *colname,
3057 : MemoryContext mcxt,
3058 : HeapTupleHeader defaultval,
3059 : JsValue *jsv,
3060 : bool *isnull,
3061 : Node *escontext)
3062 : {
3063 : Datum result;
3064 :
3065 : /* acquire/update cached tuple descriptor */
3066 3954 : update_cached_tupdesc(io, mcxt);
3067 :
3068 3954 : if (*isnull)
3069 0 : result = (Datum) 0;
3070 : else
3071 : {
3072 : HeapTupleHeader tuple;
3073 : JsObject jso;
3074 :
3075 : /* prepare input value */
3076 3954 : if (!JsValueToJsObject(jsv, &jso, escontext))
3077 : {
3078 6 : *isnull = true;
3079 42 : return (Datum) 0;
3080 : }
3081 :
3082 : /* populate resulting record tuple */
3083 3906 : tuple = populate_record(io->tupdesc, &io->record_io,
3084 : defaultval, mcxt, &jso, escontext);
3085 :
3086 3546 : if (SOFT_ERROR_OCCURRED(escontext))
3087 : {
3088 36 : *isnull = true;
3089 36 : return (Datum) 0;
3090 : }
3091 3510 : result = HeapTupleHeaderGetDatum(tuple);
3092 :
3093 3510 : JsObjectFree(&jso);
3094 : }
3095 :
3096 : /*
3097 : * If it's domain over composite, check domain constraints. (This should
3098 : * probably get refactored so that we can see the TYPECAT value, but for
3099 : * now, we can tell by comparing typid to base_typid.)
3100 : */
3101 3510 : if (typid != io->base_typid && typid != RECORDOID)
3102 : {
3103 36 : if (!domain_check_safe(result, *isnull, typid, &io->domain_info, mcxt,
3104 : escontext))
3105 : {
3106 0 : *isnull = true;
3107 0 : return (Datum) 0;
3108 : }
3109 : }
3110 :
3111 3498 : return result;
3112 : }
3113 :
3114 : /*
3115 : * Populate non-null scalar value from json/jsonb value.
3116 : *
3117 : * Returns null if an error occurs during the call to type input function,
3118 : * provided escontext is valid.
3119 : */
3120 : static Datum
3121 8832 : populate_scalar(ScalarIOData *io, Oid typid, int32 typmod, JsValue *jsv,
3122 : bool *isnull, Node *escontext)
3123 : {
3124 : Datum res;
3125 8832 : char *str = NULL;
3126 8832 : char *json = NULL;
3127 :
3128 8832 : if (jsv->is_json)
3129 : {
3130 3816 : int len = jsv->val.json.len;
3131 :
3132 3816 : json = jsv->val.json.str;
3133 : Assert(json);
3134 3816 : if (len >= 0)
3135 : {
3136 : /* Need to copy non-null-terminated string */
3137 12 : str = palloc(len + 1 * sizeof(char));
3138 12 : memcpy(str, json, len);
3139 12 : str[len] = '\0';
3140 : }
3141 : else
3142 3804 : str = json; /* string is already null-terminated */
3143 :
3144 : /* If converting to json/jsonb, make string into valid JSON literal */
3145 3816 : if ((typid == JSONOID || typid == JSONBOID) &&
3146 1092 : jsv->val.json.type == JSON_TOKEN_STRING)
3147 : {
3148 : StringInfoData buf;
3149 :
3150 354 : initStringInfo(&buf);
3151 354 : escape_json(&buf, str);
3152 : /* free temporary buffer */
3153 354 : if (str != json)
3154 0 : pfree(str);
3155 354 : str = buf.data;
3156 : }
3157 : }
3158 : else
3159 : {
3160 5016 : JsonbValue *jbv = jsv->val.jsonb;
3161 :
3162 5016 : if (typid == JSONBOID)
3163 : {
3164 900 : Jsonb *jsonb = JsonbValueToJsonb(jbv); /* directly use jsonb */
3165 :
3166 900 : return JsonbPGetDatum(jsonb);
3167 : }
3168 : /* convert jsonb to string for typio call */
3169 4116 : else if (typid == JSONOID && jbv->type != jbvBinary)
3170 978 : {
3171 : /*
3172 : * Convert scalar jsonb (non-scalars are passed here as jbvBinary)
3173 : * to json string, preserving quotes around top-level strings.
3174 : */
3175 978 : Jsonb *jsonb = JsonbValueToJsonb(jbv);
3176 :
3177 978 : str = JsonbToCString(NULL, &jsonb->root, VARSIZE(jsonb));
3178 : }
3179 3138 : else if (jbv->type == jbvString) /* quotes are stripped */
3180 1608 : str = pnstrdup(jbv->val.string.val, jbv->val.string.len);
3181 1530 : else if (jbv->type == jbvBool)
3182 6 : str = pstrdup(jbv->val.boolean ? "true" : "false");
3183 1524 : else if (jbv->type == jbvNumeric)
3184 1338 : str = DatumGetCString(DirectFunctionCall1(numeric_out,
3185 : PointerGetDatum(jbv->val.numeric)));
3186 186 : else if (jbv->type == jbvBinary)
3187 186 : str = JsonbToCString(NULL, jbv->val.binary.data,
3188 : jbv->val.binary.len);
3189 : else
3190 0 : elog(ERROR, "unrecognized jsonb type: %d", (int) jbv->type);
3191 : }
3192 :
3193 7932 : if (!InputFunctionCallSafe(&io->typiofunc, str, io->typioparam, typmod,
3194 : escontext, &res))
3195 : {
3196 42 : res = (Datum) 0;
3197 42 : *isnull = true;
3198 : }
3199 :
3200 : /* free temporary buffer */
3201 7812 : if (str != json)
3202 4386 : pfree(str);
3203 :
3204 7812 : return res;
3205 : }
3206 :
3207 : static Datum
3208 2790 : populate_domain(DomainIOData *io,
3209 : Oid typid,
3210 : const char *colname,
3211 : MemoryContext mcxt,
3212 : JsValue *jsv,
3213 : bool *isnull,
3214 : Node *escontext)
3215 : {
3216 : Datum res;
3217 :
3218 2790 : if (*isnull)
3219 2682 : res = (Datum) 0;
3220 : else
3221 : {
3222 108 : res = populate_record_field(io->base_io,
3223 : io->base_typid, io->base_typmod,
3224 : colname, mcxt, PointerGetDatum(NULL),
3225 : jsv, isnull, escontext);
3226 : Assert(!*isnull || SOFT_ERROR_OCCURRED(escontext));
3227 : }
3228 :
3229 2778 : if (!domain_check_safe(res, *isnull, typid, &io->domain_info, mcxt,
3230 : escontext))
3231 : {
3232 36 : *isnull = true;
3233 36 : return (Datum) 0;
3234 : }
3235 :
3236 2676 : return res;
3237 : }
3238 :
3239 : /* prepare column metadata cache for the given type */
3240 : static void
3241 21420 : prepare_column_cache(ColumnIOData *column,
3242 : Oid typid,
3243 : int32 typmod,
3244 : MemoryContext mcxt,
3245 : bool need_scalar)
3246 : {
3247 : HeapTuple tup;
3248 : Form_pg_type type;
3249 :
3250 21420 : column->typid = typid;
3251 21420 : column->typmod = typmod;
3252 :
3253 21420 : tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
3254 21420 : if (!HeapTupleIsValid(tup))
3255 0 : elog(ERROR, "cache lookup failed for type %u", typid);
3256 :
3257 21420 : type = (Form_pg_type) GETSTRUCT(tup);
3258 :
3259 21420 : if (type->typtype == TYPTYPE_DOMAIN)
3260 : {
3261 : /*
3262 : * We can move directly to the bottom base type; domain_check() will
3263 : * take care of checking all constraints for a stack of domains.
3264 : */
3265 : Oid base_typid;
3266 2034 : int32 base_typmod = typmod;
3267 :
3268 2034 : base_typid = getBaseTypeAndTypmod(typid, &base_typmod);
3269 2034 : if (get_typtype(base_typid) == TYPTYPE_COMPOSITE)
3270 : {
3271 : /* domain over composite has its own code path */
3272 72 : column->typcat = TYPECAT_COMPOSITE_DOMAIN;
3273 72 : column->io.composite.record_io = NULL;
3274 72 : column->io.composite.tupdesc = NULL;
3275 72 : column->io.composite.base_typid = base_typid;
3276 72 : column->io.composite.base_typmod = base_typmod;
3277 72 : column->io.composite.domain_info = NULL;
3278 : }
3279 : else
3280 : {
3281 : /* domain over anything else */
3282 1962 : column->typcat = TYPECAT_DOMAIN;
3283 1962 : column->io.domain.base_typid = base_typid;
3284 1962 : column->io.domain.base_typmod = base_typmod;
3285 1962 : column->io.domain.base_io =
3286 1962 : MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
3287 1962 : column->io.domain.domain_info = NULL;
3288 : }
3289 : }
3290 19386 : else if (type->typtype == TYPTYPE_COMPOSITE || typid == RECORDOID)
3291 : {
3292 2724 : column->typcat = TYPECAT_COMPOSITE;
3293 2724 : column->io.composite.record_io = NULL;
3294 2724 : column->io.composite.tupdesc = NULL;
3295 2724 : column->io.composite.base_typid = typid;
3296 2724 : column->io.composite.base_typmod = typmod;
3297 2724 : column->io.composite.domain_info = NULL;
3298 : }
3299 16662 : else if (IsTrueArrayType(type))
3300 : {
3301 7674 : column->typcat = TYPECAT_ARRAY;
3302 7674 : column->io.array.element_info = MemoryContextAllocZero(mcxt,
3303 : sizeof(ColumnIOData));
3304 7674 : column->io.array.element_type = type->typelem;
3305 : /* array element typemod stored in attribute's typmod */
3306 7674 : column->io.array.element_typmod = typmod;
3307 : }
3308 : else
3309 : {
3310 8988 : column->typcat = TYPECAT_SCALAR;
3311 8988 : need_scalar = true;
3312 : }
3313 :
3314 : /* caller can force us to look up scalar_io info even for non-scalars */
3315 21420 : if (need_scalar)
3316 : {
3317 : Oid typioproc;
3318 :
3319 19770 : getTypeInputInfo(typid, &typioproc, &column->scalar_io.typioparam);
3320 19770 : fmgr_info_cxt(typioproc, &column->scalar_io.typiofunc, mcxt);
3321 : }
3322 :
3323 21420 : ReleaseSysCache(tup);
3324 21420 : }
3325 :
3326 : /*
3327 : * Populate and return the value of specified type from a given json/jsonb
3328 : * value 'json_val'. 'cache' is caller-specified pointer to save the
3329 : * ColumnIOData that will be initialized on the 1st call and then reused
3330 : * during any subsequent calls. 'mcxt' gives the memory context to allocate
3331 : * the ColumnIOData and any other subsidiary memory in. 'escontext',
3332 : * if not NULL, tells that any errors that occur should be handled softly.
3333 : */
3334 : Datum
3335 2100 : json_populate_type(Datum json_val, Oid json_type,
3336 : Oid typid, int32 typmod,
3337 : void **cache, MemoryContext mcxt,
3338 : bool *isnull,
3339 : Node *escontext)
3340 : {
3341 2100 : JsValue jsv = {0};
3342 : JsonbValue jbv;
3343 :
3344 2100 : jsv.is_json = json_type == JSONOID;
3345 :
3346 2100 : if (*isnull)
3347 : {
3348 900 : if (jsv.is_json)
3349 0 : jsv.val.json.str = NULL;
3350 : else
3351 900 : jsv.val.jsonb = NULL;
3352 : }
3353 1200 : else if (jsv.is_json)
3354 : {
3355 0 : text *json = DatumGetTextPP(json_val);
3356 :
3357 0 : jsv.val.json.str = VARDATA_ANY(json);
3358 0 : jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
3359 0 : jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in
3360 : * populate_composite() */
3361 : }
3362 : else
3363 : {
3364 1200 : Jsonb *jsonb = DatumGetJsonbP(json_val);
3365 :
3366 1200 : jsv.val.jsonb = &jbv;
3367 :
3368 : /* fill binary jsonb value pointing to jb */
3369 1200 : jbv.type = jbvBinary;
3370 1200 : jbv.val.binary.data = &jsonb->root;
3371 1200 : jbv.val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
3372 : }
3373 :
3374 2100 : if (*cache == NULL)
3375 642 : *cache = MemoryContextAllocZero(mcxt, sizeof(ColumnIOData));
3376 :
3377 2100 : return populate_record_field(*cache, typid, typmod, NULL, mcxt,
3378 : PointerGetDatum(NULL), &jsv, isnull,
3379 : escontext);
3380 : }
3381 :
3382 : /* recursively populate a record field or an array element from a json/jsonb value */
3383 : static Datum
3384 37974 : populate_record_field(ColumnIOData *col,
3385 : Oid typid,
3386 : int32 typmod,
3387 : const char *colname,
3388 : MemoryContext mcxt,
3389 : Datum defaultval,
3390 : JsValue *jsv,
3391 : bool *isnull,
3392 : Node *escontext)
3393 : {
3394 : TypeCat typcat;
3395 :
3396 37974 : check_stack_depth();
3397 :
3398 : /*
3399 : * Prepare column metadata cache for the given type. Force lookup of the
3400 : * scalar_io data so that the json string hack below will work.
3401 : */
3402 37974 : if (col->typid != typid || col->typmod != typmod)
3403 19770 : prepare_column_cache(col, typid, typmod, mcxt, true);
3404 :
3405 37974 : *isnull = JsValueIsNull(jsv);
3406 :
3407 37974 : typcat = col->typcat;
3408 :
3409 : /* try to convert json string to a non-scalar type through input function */
3410 37974 : if (JsValueIsString(jsv) &&
3411 3828 : (typcat == TYPECAT_ARRAY ||
3412 3804 : typcat == TYPECAT_COMPOSITE ||
3413 : typcat == TYPECAT_COMPOSITE_DOMAIN))
3414 48 : typcat = TYPECAT_SCALAR;
3415 :
3416 : /* we must perform domain checks for NULLs, otherwise exit immediately */
3417 37974 : if (*isnull &&
3418 22212 : typcat != TYPECAT_DOMAIN &&
3419 : typcat != TYPECAT_COMPOSITE_DOMAIN)
3420 22212 : return (Datum) 0;
3421 :
3422 15762 : switch (typcat)
3423 : {
3424 8832 : case TYPECAT_SCALAR:
3425 8832 : return populate_scalar(&col->scalar_io, typid, typmod, jsv,
3426 : isnull, escontext);
3427 :
3428 2142 : case TYPECAT_ARRAY:
3429 2142 : return populate_array(&col->io.array, colname, mcxt, jsv,
3430 : isnull, escontext);
3431 :
3432 1998 : case TYPECAT_COMPOSITE:
3433 : case TYPECAT_COMPOSITE_DOMAIN:
3434 2010 : return populate_composite(&col->io.composite, typid,
3435 : colname, mcxt,
3436 1998 : DatumGetPointer(defaultval)
3437 12 : ? DatumGetHeapTupleHeader(defaultval)
3438 : : NULL,
3439 : jsv, isnull,
3440 : escontext);
3441 :
3442 2790 : case TYPECAT_DOMAIN:
3443 2790 : return populate_domain(&col->io.domain, typid, colname, mcxt,
3444 : jsv, isnull, escontext);
3445 :
3446 0 : default:
3447 0 : elog(ERROR, "unrecognized type category '%c'", typcat);
3448 : return (Datum) 0;
3449 : }
3450 : }
3451 :
3452 : static RecordIOData *
3453 2304 : allocate_record_info(MemoryContext mcxt, int ncolumns)
3454 : {
3455 : RecordIOData *data = (RecordIOData *)
3456 2304 : MemoryContextAlloc(mcxt,
3457 : offsetof(RecordIOData, columns) +
3458 2304 : ncolumns * sizeof(ColumnIOData));
3459 :
3460 2304 : data->record_type = InvalidOid;
3461 2304 : data->record_typmod = 0;
3462 2304 : data->ncolumns = ncolumns;
3463 42318 : MemSet(data->columns, 0, sizeof(ColumnIOData) * ncolumns);
3464 :
3465 2304 : return data;
3466 : }
3467 :
3468 : static bool
3469 30360 : JsObjectGetField(JsObject *obj, char *field, JsValue *jsv)
3470 : {
3471 30360 : jsv->is_json = obj->is_json;
3472 :
3473 30360 : if (jsv->is_json)
3474 : {
3475 15036 : JsonHashEntry *hashentry = hash_search(obj->val.json_hash, field,
3476 : HASH_FIND, NULL);
3477 :
3478 15036 : jsv->val.json.type = hashentry ? hashentry->type : JSON_TOKEN_NULL;
3479 15036 : jsv->val.json.str = jsv->val.json.type == JSON_TOKEN_NULL ? NULL :
3480 : hashentry->val;
3481 15036 : jsv->val.json.len = jsv->val.json.str ? -1 : 0; /* null-terminated */
3482 :
3483 15036 : return hashentry != NULL;
3484 : }
3485 : else
3486 : {
3487 15324 : jsv->val.jsonb = !obj->val.jsonb_cont ? NULL :
3488 15324 : getKeyJsonValueFromContainer(obj->val.jsonb_cont, field, strlen(field),
3489 : NULL);
3490 :
3491 15324 : return jsv->val.jsonb != NULL;
3492 : }
3493 : }
3494 :
3495 : /* populate a record tuple from json/jsonb value */
3496 : static HeapTupleHeader
3497 4386 : populate_record(TupleDesc tupdesc,
3498 : RecordIOData **record_p,
3499 : HeapTupleHeader defaultval,
3500 : MemoryContext mcxt,
3501 : JsObject *obj,
3502 : Node *escontext)
3503 : {
3504 4386 : RecordIOData *record = *record_p;
3505 : Datum *values;
3506 : bool *nulls;
3507 : HeapTuple res;
3508 4386 : int ncolumns = tupdesc->natts;
3509 : int i;
3510 :
3511 : /*
3512 : * if the input json is empty, we can only skip the rest if we were passed
3513 : * in a non-null record, since otherwise there may be issues with domain
3514 : * nulls.
3515 : */
3516 4386 : if (defaultval && JsObjectIsEmpty(obj))
3517 12 : return defaultval;
3518 :
3519 : /* (re)allocate metadata cache */
3520 4374 : if (record == NULL ||
3521 2070 : record->ncolumns != ncolumns)
3522 2304 : *record_p = record = allocate_record_info(mcxt, ncolumns);
3523 :
3524 : /* invalidate metadata cache if the record type has changed */
3525 4374 : if (record->record_type != tupdesc->tdtypeid ||
3526 2070 : record->record_typmod != tupdesc->tdtypmod)
3527 : {
3528 44862 : MemSet(record, 0, offsetof(RecordIOData, columns) +
3529 : ncolumns * sizeof(ColumnIOData));
3530 2304 : record->record_type = tupdesc->tdtypeid;
3531 2304 : record->record_typmod = tupdesc->tdtypmod;
3532 2304 : record->ncolumns = ncolumns;
3533 : }
3534 :
3535 4374 : values = (Datum *) palloc(ncolumns * sizeof(Datum));
3536 4374 : nulls = (bool *) palloc(ncolumns * sizeof(bool));
3537 :
3538 4374 : if (defaultval)
3539 : {
3540 : HeapTupleData tuple;
3541 :
3542 : /* Build a temporary HeapTuple control structure */
3543 432 : tuple.t_len = HeapTupleHeaderGetDatumLength(defaultval);
3544 432 : ItemPointerSetInvalid(&(tuple.t_self));
3545 432 : tuple.t_tableOid = InvalidOid;
3546 432 : tuple.t_data = defaultval;
3547 :
3548 : /* Break down the tuple into fields */
3549 432 : heap_deform_tuple(&tuple, tupdesc, values, nulls);
3550 : }
3551 : else
3552 : {
3553 35178 : for (i = 0; i < ncolumns; ++i)
3554 : {
3555 31236 : values[i] = (Datum) 0;
3556 31236 : nulls[i] = true;
3557 : }
3558 : }
3559 :
3560 34362 : for (i = 0; i < ncolumns; ++i)
3561 : {
3562 30360 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
3563 30360 : char *colname = NameStr(att->attname);
3564 30360 : JsValue field = {0};
3565 : bool found;
3566 :
3567 : /* Ignore dropped columns in datatype */
3568 30360 : if (att->attisdropped)
3569 : {
3570 0 : nulls[i] = true;
3571 756 : continue;
3572 : }
3573 :
3574 30360 : found = JsObjectGetField(obj, colname, &field);
3575 :
3576 : /*
3577 : * we can't just skip here if the key wasn't found since we might have
3578 : * a domain to deal with. If we were passed in a non-null record
3579 : * datum, we assume that the existing values are valid (if they're
3580 : * not, then it's not our fault), but if we were passed in a null,
3581 : * then every field which we don't populate needs to be run through
3582 : * the input function just in case it's a domain type.
3583 : */
3584 30360 : if (defaultval && !found)
3585 756 : continue;
3586 :
3587 29604 : values[i] = populate_record_field(&record->columns[i],
3588 : att->atttypid,
3589 : att->atttypmod,
3590 : colname,
3591 : mcxt,
3592 29604 : nulls[i] ? (Datum) 0 : values[i],
3593 : &field,
3594 : &nulls[i],
3595 : escontext);
3596 : }
3597 :
3598 4002 : res = heap_form_tuple(tupdesc, values, nulls);
3599 :
3600 4002 : pfree(values);
3601 4002 : pfree(nulls);
3602 :
3603 4002 : return res->t_data;
3604 : }
3605 :
3606 : /*
3607 : * Setup for json{b}_populate_record{set}: result type will be same as first
3608 : * argument's type --- unless first argument is "null::record", which we can't
3609 : * extract type info from; we handle that later.
3610 : */
3611 : static void
3612 1650 : get_record_type_from_argument(FunctionCallInfo fcinfo,
3613 : const char *funcname,
3614 : PopulateRecordCache *cache)
3615 : {
3616 1650 : cache->argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
3617 1650 : prepare_column_cache(&cache->c,
3618 : cache->argtype, -1,
3619 : cache->fn_mcxt, false);
3620 1650 : if (cache->c.typcat != TYPECAT_COMPOSITE &&
3621 72 : cache->c.typcat != TYPECAT_COMPOSITE_DOMAIN)
3622 0 : ereport(ERROR,
3623 : (errcode(ERRCODE_DATATYPE_MISMATCH),
3624 : /* translator: %s is a function name, eg json_to_record */
3625 : errmsg("first argument of %s must be a row type",
3626 : funcname)));
3627 1650 : }
3628 :
3629 : /*
3630 : * Setup for json{b}_to_record{set}: result type is specified by calling
3631 : * query. We'll also use this code for json{b}_populate_record{set},
3632 : * if we discover that the first argument is a null of type RECORD.
3633 : *
3634 : * Here it is syntactically impossible to specify the target type
3635 : * as domain-over-composite.
3636 : */
3637 : static void
3638 312 : get_record_type_from_query(FunctionCallInfo fcinfo,
3639 : const char *funcname,
3640 : PopulateRecordCache *cache)
3641 : {
3642 : TupleDesc tupdesc;
3643 : MemoryContext old_cxt;
3644 :
3645 312 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
3646 36 : ereport(ERROR,
3647 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3648 : /* translator: %s is a function name, eg json_to_record */
3649 : errmsg("could not determine row type for result of %s",
3650 : funcname),
3651 : errhint("Provide a non-null record argument, "
3652 : "or call the function in the FROM clause "
3653 : "using a column definition list.")));
3654 :
3655 : Assert(tupdesc);
3656 276 : cache->argtype = tupdesc->tdtypeid;
3657 :
3658 : /* If we go through this more than once, avoid memory leak */
3659 276 : if (cache->c.io.composite.tupdesc)
3660 0 : FreeTupleDesc(cache->c.io.composite.tupdesc);
3661 :
3662 : /* Save identified tupdesc */
3663 276 : old_cxt = MemoryContextSwitchTo(cache->fn_mcxt);
3664 276 : cache->c.io.composite.tupdesc = CreateTupleDescCopy(tupdesc);
3665 276 : cache->c.io.composite.base_typid = tupdesc->tdtypeid;
3666 276 : cache->c.io.composite.base_typmod = tupdesc->tdtypmod;
3667 276 : MemoryContextSwitchTo(old_cxt);
3668 276 : }
3669 :
3670 : /*
3671 : * common worker for json{b}_populate_record() and json{b}_to_record()
3672 : * is_json and have_record_arg identify the specific function
3673 : */
3674 : static Datum
3675 1968 : populate_record_worker(FunctionCallInfo fcinfo, const char *funcname,
3676 : bool is_json, bool have_record_arg,
3677 : Node *escontext)
3678 : {
3679 1968 : int json_arg_num = have_record_arg ? 1 : 0;
3680 1968 : JsValue jsv = {0};
3681 : HeapTupleHeader rec;
3682 : Datum rettuple;
3683 : bool isnull;
3684 : JsonbValue jbv;
3685 1968 : MemoryContext fnmcxt = fcinfo->flinfo->fn_mcxt;
3686 1968 : PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
3687 :
3688 : /*
3689 : * If first time through, identify input/result record type. Note that
3690 : * this stanza looks only at fcinfo context, which can't change during the
3691 : * query; so we may not be able to fully resolve a RECORD input type yet.
3692 : */
3693 1968 : if (!cache)
3694 : {
3695 1560 : fcinfo->flinfo->fn_extra = cache =
3696 1560 : MemoryContextAllocZero(fnmcxt, sizeof(*cache));
3697 1560 : cache->fn_mcxt = fnmcxt;
3698 :
3699 1560 : if (have_record_arg)
3700 1356 : get_record_type_from_argument(fcinfo, funcname, cache);
3701 : else
3702 204 : get_record_type_from_query(fcinfo, funcname, cache);
3703 : }
3704 :
3705 : /* Collect record arg if we have one */
3706 1968 : if (!have_record_arg)
3707 204 : rec = NULL; /* it's json{b}_to_record() */
3708 1764 : else if (!PG_ARGISNULL(0))
3709 : {
3710 108 : rec = PG_GETARG_HEAPTUPLEHEADER(0);
3711 :
3712 : /*
3713 : * When declared arg type is RECORD, identify actual record type from
3714 : * the tuple itself.
3715 : */
3716 108 : if (cache->argtype == RECORDOID)
3717 : {
3718 12 : cache->c.io.composite.base_typid = HeapTupleHeaderGetTypeId(rec);
3719 12 : cache->c.io.composite.base_typmod = HeapTupleHeaderGetTypMod(rec);
3720 : }
3721 : }
3722 : else
3723 : {
3724 1656 : rec = NULL;
3725 :
3726 : /*
3727 : * When declared arg type is RECORD, identify actual record type from
3728 : * calling query, or fail if we can't.
3729 : */
3730 1656 : if (cache->argtype == RECORDOID)
3731 : {
3732 24 : get_record_type_from_query(fcinfo, funcname, cache);
3733 : /* This can't change argtype, which is important for next time */
3734 : Assert(cache->argtype == RECORDOID);
3735 : }
3736 : }
3737 :
3738 : /* If no JSON argument, just return the record (if any) unchanged */
3739 1956 : if (PG_ARGISNULL(json_arg_num))
3740 : {
3741 0 : if (rec)
3742 0 : PG_RETURN_POINTER(rec);
3743 : else
3744 0 : PG_RETURN_NULL();
3745 : }
3746 :
3747 1956 : jsv.is_json = is_json;
3748 :
3749 1956 : if (is_json)
3750 : {
3751 918 : text *json = PG_GETARG_TEXT_PP(json_arg_num);
3752 :
3753 918 : jsv.val.json.str = VARDATA_ANY(json);
3754 918 : jsv.val.json.len = VARSIZE_ANY_EXHDR(json);
3755 918 : jsv.val.json.type = JSON_TOKEN_INVALID; /* not used in
3756 : * populate_composite() */
3757 : }
3758 : else
3759 : {
3760 1038 : Jsonb *jb = PG_GETARG_JSONB_P(json_arg_num);
3761 :
3762 1038 : jsv.val.jsonb = &jbv;
3763 :
3764 : /* fill binary jsonb value pointing to jb */
3765 1038 : jbv.type = jbvBinary;
3766 1038 : jbv.val.binary.data = &jb->root;
3767 1038 : jbv.val.binary.len = VARSIZE(jb) - VARHDRSZ;
3768 : }
3769 :
3770 1956 : isnull = false;
3771 1956 : rettuple = populate_composite(&cache->c.io.composite, cache->argtype,
3772 : NULL, fnmcxt, rec, &jsv, &isnull,
3773 : escontext);
3774 : Assert(!isnull || SOFT_ERROR_OCCURRED(escontext));
3775 :
3776 1590 : PG_RETURN_DATUM(rettuple);
3777 : }
3778 :
3779 : /*
3780 : * get_json_object_as_hash
3781 : *
3782 : * Decomposes a json object into a hash table.
3783 : *
3784 : * Returns the hash table if the json is parsed successfully, NULL otherwise.
3785 : */
3786 : static HTAB *
3787 1884 : get_json_object_as_hash(char *json, int len, const char *funcname,
3788 : Node *escontext)
3789 : {
3790 : HASHCTL ctl;
3791 : HTAB *tab;
3792 : JHashState *state;
3793 : JsonSemAction *sem;
3794 :
3795 1884 : ctl.keysize = NAMEDATALEN;
3796 1884 : ctl.entrysize = sizeof(JsonHashEntry);
3797 1884 : ctl.hcxt = CurrentMemoryContext;
3798 1884 : tab = hash_create("json object hashtable",
3799 : 100,
3800 : &ctl,
3801 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
3802 :
3803 1884 : state = palloc0(sizeof(JHashState));
3804 1884 : sem = palloc0(sizeof(JsonSemAction));
3805 :
3806 1884 : state->function_name = funcname;
3807 1884 : state->hash = tab;
3808 1884 : state->lex = makeJsonLexContextCstringLen(NULL, json, len,
3809 : GetDatabaseEncoding(), true);
3810 :
3811 1884 : sem->semstate = (void *) state;
3812 1884 : sem->array_start = hash_array_start;
3813 1884 : sem->scalar = hash_scalar;
3814 1884 : sem->object_field_start = hash_object_field_start;
3815 1884 : sem->object_field_end = hash_object_field_end;
3816 :
3817 1884 : if (!pg_parse_json_or_errsave(state->lex, sem, escontext))
3818 : {
3819 0 : hash_destroy(state->hash);
3820 0 : tab = NULL;
3821 : }
3822 :
3823 1866 : freeJsonLexContext(state->lex);
3824 :
3825 1866 : return tab;
3826 : }
3827 :
3828 : static JsonParseErrorType
3829 6156 : hash_object_field_start(void *state, char *fname, bool isnull)
3830 : {
3831 6156 : JHashState *_state = (JHashState *) state;
3832 :
3833 6156 : if (_state->lex->lex_level > 1)
3834 2316 : return JSON_SUCCESS;
3835 :
3836 : /* remember token type */
3837 3840 : _state->saved_token_type = _state->lex->token_type;
3838 :
3839 3840 : if (_state->lex->token_type == JSON_TOKEN_ARRAY_START ||
3840 2940 : _state->lex->token_type == JSON_TOKEN_OBJECT_START)
3841 : {
3842 : /* remember start position of the whole text of the subobject */
3843 1254 : _state->save_json_start = _state->lex->token_start;
3844 : }
3845 : else
3846 : {
3847 : /* must be a scalar */
3848 2586 : _state->save_json_start = NULL;
3849 : }
3850 :
3851 3840 : return JSON_SUCCESS;
3852 : }
3853 :
3854 : static JsonParseErrorType
3855 6156 : hash_object_field_end(void *state, char *fname, bool isnull)
3856 : {
3857 6156 : JHashState *_state = (JHashState *) state;
3858 : JsonHashEntry *hashentry;
3859 : bool found;
3860 :
3861 : /*
3862 : * Ignore nested fields.
3863 : */
3864 6156 : if (_state->lex->lex_level > 1)
3865 2316 : return JSON_SUCCESS;
3866 :
3867 : /*
3868 : * Ignore field names >= NAMEDATALEN - they can't match a record field.
3869 : * (Note: without this test, the hash code would truncate the string at
3870 : * NAMEDATALEN-1, and could then match against a similarly-truncated
3871 : * record field name. That would be a reasonable behavior, but this code
3872 : * has previously insisted on exact equality, so we keep this behavior.)
3873 : */
3874 3840 : if (strlen(fname) >= NAMEDATALEN)
3875 0 : return JSON_SUCCESS;
3876 :
3877 3840 : hashentry = hash_search(_state->hash, fname, HASH_ENTER, &found);
3878 :
3879 : /*
3880 : * found being true indicates a duplicate. We don't do anything about
3881 : * that, a later field with the same name overrides the earlier field.
3882 : */
3883 :
3884 3840 : hashentry->type = _state->saved_token_type;
3885 : Assert(isnull == (hashentry->type == JSON_TOKEN_NULL));
3886 :
3887 3840 : if (_state->save_json_start != NULL)
3888 : {
3889 1254 : int len = _state->lex->prev_token_terminator - _state->save_json_start;
3890 1254 : char *val = palloc((len + 1) * sizeof(char));
3891 :
3892 1254 : memcpy(val, _state->save_json_start, len);
3893 1254 : val[len] = '\0';
3894 1254 : hashentry->val = val;
3895 : }
3896 : else
3897 : {
3898 : /* must have had a scalar instead */
3899 2586 : hashentry->val = _state->saved_scalar;
3900 : }
3901 :
3902 3840 : return JSON_SUCCESS;
3903 : }
3904 :
3905 : static JsonParseErrorType
3906 1272 : hash_array_start(void *state)
3907 : {
3908 1272 : JHashState *_state = (JHashState *) state;
3909 :
3910 1272 : if (_state->lex->lex_level == 0)
3911 6 : ereport(ERROR,
3912 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3913 : errmsg("cannot call %s on an array", _state->function_name)));
3914 :
3915 1266 : return JSON_SUCCESS;
3916 : }
3917 :
3918 : static JsonParseErrorType
3919 7380 : hash_scalar(void *state, char *token, JsonTokenType tokentype)
3920 : {
3921 7380 : JHashState *_state = (JHashState *) state;
3922 :
3923 7380 : if (_state->lex->lex_level == 0)
3924 12 : ereport(ERROR,
3925 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3926 : errmsg("cannot call %s on a scalar", _state->function_name)));
3927 :
3928 7368 : if (_state->lex->lex_level == 1)
3929 : {
3930 2586 : _state->saved_scalar = token;
3931 : /* saved_token_type must already be set in hash_object_field_start() */
3932 : Assert(_state->saved_token_type == tokentype);
3933 : }
3934 :
3935 7368 : return JSON_SUCCESS;
3936 : }
3937 :
3938 :
3939 : /*
3940 : * SQL function json_populate_recordset
3941 : *
3942 : * set fields in a set of records from the argument json,
3943 : * which must be an array of objects.
3944 : *
3945 : * similar to json_populate_record, but the tuple-building code
3946 : * is pushed down into the semantic action handlers so it's done
3947 : * per object in the array.
3948 : */
3949 : Datum
3950 150 : jsonb_populate_recordset(PG_FUNCTION_ARGS)
3951 : {
3952 150 : return populate_recordset_worker(fcinfo, "jsonb_populate_recordset",
3953 : false, true);
3954 : }
3955 :
3956 : Datum
3957 18 : jsonb_to_recordset(PG_FUNCTION_ARGS)
3958 : {
3959 18 : return populate_recordset_worker(fcinfo, "jsonb_to_recordset",
3960 : false, false);
3961 : }
3962 :
3963 : Datum
3964 156 : json_populate_recordset(PG_FUNCTION_ARGS)
3965 : {
3966 156 : return populate_recordset_worker(fcinfo, "json_populate_recordset",
3967 : true, true);
3968 : }
3969 :
3970 : Datum
3971 18 : json_to_recordset(PG_FUNCTION_ARGS)
3972 : {
3973 18 : return populate_recordset_worker(fcinfo, "json_to_recordset",
3974 : true, false);
3975 : }
3976 :
3977 : static void
3978 480 : populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
3979 : {
3980 480 : PopulateRecordCache *cache = state->cache;
3981 : HeapTupleHeader tuphead;
3982 : HeapTupleData tuple;
3983 :
3984 : /* acquire/update cached tuple descriptor */
3985 480 : update_cached_tupdesc(&cache->c.io.composite, cache->fn_mcxt);
3986 :
3987 : /* replace record fields from json */
3988 480 : tuphead = populate_record(cache->c.io.composite.tupdesc,
3989 : &cache->c.io.composite.record_io,
3990 : state->rec,
3991 : cache->fn_mcxt,
3992 : obj,
3993 : NULL);
3994 :
3995 : /* if it's domain over composite, check domain constraints */
3996 468 : if (cache->c.typcat == TYPECAT_COMPOSITE_DOMAIN)
3997 48 : (void) domain_check_safe(HeapTupleHeaderGetDatum(tuphead), false,
3998 : cache->argtype,
3999 : &cache->c.io.composite.domain_info,
4000 : cache->fn_mcxt,
4001 : NULL);
4002 :
4003 : /* ok, save into tuplestore */
4004 456 : tuple.t_len = HeapTupleHeaderGetDatumLength(tuphead);
4005 456 : ItemPointerSetInvalid(&(tuple.t_self));
4006 456 : tuple.t_tableOid = InvalidOid;
4007 456 : tuple.t_data = tuphead;
4008 :
4009 456 : tuplestore_puttuple(state->tuple_store, &tuple);
4010 456 : }
4011 :
4012 : /*
4013 : * common worker for json{b}_populate_recordset() and json{b}_to_recordset()
4014 : * is_json and have_record_arg identify the specific function
4015 : */
4016 : static Datum
4017 342 : populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
4018 : bool is_json, bool have_record_arg)
4019 : {
4020 342 : int json_arg_num = have_record_arg ? 1 : 0;
4021 : ReturnSetInfo *rsi;
4022 : MemoryContext old_cxt;
4023 : HeapTupleHeader rec;
4024 342 : PopulateRecordCache *cache = fcinfo->flinfo->fn_extra;
4025 : PopulateRecordsetState *state;
4026 :
4027 342 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
4028 :
4029 342 : if (!rsi || !IsA(rsi, ReturnSetInfo))
4030 0 : ereport(ERROR,
4031 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4032 : errmsg("set-valued function called in context that cannot accept a set")));
4033 :
4034 342 : if (!(rsi->allowedModes & SFRM_Materialize))
4035 0 : ereport(ERROR,
4036 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4037 : errmsg("materialize mode required, but it is not allowed in this context")));
4038 :
4039 342 : rsi->returnMode = SFRM_Materialize;
4040 :
4041 : /*
4042 : * If first time through, identify input/result record type. Note that
4043 : * this stanza looks only at fcinfo context, which can't change during the
4044 : * query; so we may not be able to fully resolve a RECORD input type yet.
4045 : */
4046 342 : if (!cache)
4047 : {
4048 330 : fcinfo->flinfo->fn_extra = cache =
4049 330 : MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt, sizeof(*cache));
4050 330 : cache->fn_mcxt = fcinfo->flinfo->fn_mcxt;
4051 :
4052 330 : if (have_record_arg)
4053 294 : get_record_type_from_argument(fcinfo, funcname, cache);
4054 : else
4055 36 : get_record_type_from_query(fcinfo, funcname, cache);
4056 : }
4057 :
4058 : /* Collect record arg if we have one */
4059 342 : if (!have_record_arg)
4060 36 : rec = NULL; /* it's json{b}_to_recordset() */
4061 306 : else if (!PG_ARGISNULL(0))
4062 : {
4063 192 : rec = PG_GETARG_HEAPTUPLEHEADER(0);
4064 :
4065 : /*
4066 : * When declared arg type is RECORD, identify actual record type from
4067 : * the tuple itself.
4068 : */
4069 192 : if (cache->argtype == RECORDOID)
4070 : {
4071 96 : cache->c.io.composite.base_typid = HeapTupleHeaderGetTypeId(rec);
4072 96 : cache->c.io.composite.base_typmod = HeapTupleHeaderGetTypMod(rec);
4073 : }
4074 : }
4075 : else
4076 : {
4077 114 : rec = NULL;
4078 :
4079 : /*
4080 : * When declared arg type is RECORD, identify actual record type from
4081 : * calling query, or fail if we can't.
4082 : */
4083 114 : if (cache->argtype == RECORDOID)
4084 : {
4085 48 : get_record_type_from_query(fcinfo, funcname, cache);
4086 : /* This can't change argtype, which is important for next time */
4087 : Assert(cache->argtype == RECORDOID);
4088 : }
4089 : }
4090 :
4091 : /* if the json is null send back an empty set */
4092 318 : if (PG_ARGISNULL(json_arg_num))
4093 0 : PG_RETURN_NULL();
4094 :
4095 : /*
4096 : * Forcibly update the cached tupdesc, to ensure we have the right tupdesc
4097 : * to return even if the JSON contains no rows.
4098 : */
4099 318 : update_cached_tupdesc(&cache->c.io.composite, cache->fn_mcxt);
4100 :
4101 318 : state = palloc0(sizeof(PopulateRecordsetState));
4102 :
4103 : /* make tuplestore in a sufficiently long-lived memory context */
4104 318 : old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
4105 318 : state->tuple_store = tuplestore_begin_heap(rsi->allowedModes &
4106 : SFRM_Materialize_Random,
4107 : false, work_mem);
4108 318 : MemoryContextSwitchTo(old_cxt);
4109 :
4110 318 : state->function_name = funcname;
4111 318 : state->cache = cache;
4112 318 : state->rec = rec;
4113 :
4114 318 : if (is_json)
4115 : {
4116 162 : text *json = PG_GETARG_TEXT_PP(json_arg_num);
4117 : JsonLexContext lex;
4118 : JsonSemAction *sem;
4119 :
4120 162 : sem = palloc0(sizeof(JsonSemAction));
4121 :
4122 162 : makeJsonLexContext(&lex, json, true);
4123 :
4124 162 : sem->semstate = (void *) state;
4125 162 : sem->array_start = populate_recordset_array_start;
4126 162 : sem->array_element_start = populate_recordset_array_element_start;
4127 162 : sem->scalar = populate_recordset_scalar;
4128 162 : sem->object_field_start = populate_recordset_object_field_start;
4129 162 : sem->object_field_end = populate_recordset_object_field_end;
4130 162 : sem->object_start = populate_recordset_object_start;
4131 162 : sem->object_end = populate_recordset_object_end;
4132 :
4133 162 : state->lex = &lex;
4134 :
4135 162 : pg_parse_json_or_ereport(&lex, sem);
4136 :
4137 150 : freeJsonLexContext(&lex);
4138 150 : state->lex = NULL;
4139 : }
4140 : else
4141 : {
4142 156 : Jsonb *jb = PG_GETARG_JSONB_P(json_arg_num);
4143 : JsonbIterator *it;
4144 : JsonbValue v;
4145 156 : bool skipNested = false;
4146 : JsonbIteratorToken r;
4147 :
4148 156 : if (JB_ROOT_IS_SCALAR(jb) || !JB_ROOT_IS_ARRAY(jb))
4149 0 : ereport(ERROR,
4150 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4151 : errmsg("cannot call %s on a non-array",
4152 : funcname)));
4153 :
4154 156 : it = JsonbIteratorInit(&jb->root);
4155 :
4156 678 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
4157 : {
4158 534 : skipNested = true;
4159 :
4160 534 : if (r == WJB_ELEM)
4161 : {
4162 : JsObject obj;
4163 :
4164 234 : if (v.type != jbvBinary ||
4165 234 : !JsonContainerIsObject(v.val.binary.data))
4166 0 : ereport(ERROR,
4167 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4168 : errmsg("argument of %s must be an array of objects",
4169 : funcname)));
4170 :
4171 234 : obj.is_json = false;
4172 234 : obj.val.jsonb_cont = v.val.binary.data;
4173 :
4174 234 : populate_recordset_record(state, &obj);
4175 : }
4176 : }
4177 : }
4178 :
4179 : /*
4180 : * Note: we must copy the cached tupdesc because the executor will free
4181 : * the passed-back setDesc, but we want to hang onto the cache in case
4182 : * we're called again in the same query.
4183 : */
4184 294 : rsi->setResult = state->tuple_store;
4185 294 : rsi->setDesc = CreateTupleDescCopy(cache->c.io.composite.tupdesc);
4186 :
4187 294 : PG_RETURN_NULL();
4188 : }
4189 :
4190 : static JsonParseErrorType
4191 282 : populate_recordset_object_start(void *state)
4192 : {
4193 282 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
4194 282 : int lex_level = _state->lex->lex_level;
4195 : HASHCTL ctl;
4196 :
4197 : /* Reject object at top level: we must have an array at level 0 */
4198 282 : if (lex_level == 0)
4199 0 : ereport(ERROR,
4200 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4201 : errmsg("cannot call %s on an object",
4202 : _state->function_name)));
4203 :
4204 : /* Nested objects require no special processing */
4205 282 : if (lex_level > 1)
4206 36 : return JSON_SUCCESS;
4207 :
4208 : /* Object at level 1: set up a new hash table for this object */
4209 246 : ctl.keysize = NAMEDATALEN;
4210 246 : ctl.entrysize = sizeof(JsonHashEntry);
4211 246 : ctl.hcxt = CurrentMemoryContext;
4212 246 : _state->json_hash = hash_create("json object hashtable",
4213 : 100,
4214 : &ctl,
4215 : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
4216 :
4217 246 : return JSON_SUCCESS;
4218 : }
4219 :
4220 : static JsonParseErrorType
4221 282 : populate_recordset_object_end(void *state)
4222 : {
4223 282 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
4224 : JsObject obj;
4225 :
4226 : /* Nested objects require no special processing */
4227 282 : if (_state->lex->lex_level > 1)
4228 36 : return JSON_SUCCESS;
4229 :
4230 246 : obj.is_json = true;
4231 246 : obj.val.json_hash = _state->json_hash;
4232 :
4233 : /* Otherwise, construct and return a tuple based on this level-1 object */
4234 246 : populate_recordset_record(_state, &obj);
4235 :
4236 : /* Done with hash for this object */
4237 234 : hash_destroy(_state->json_hash);
4238 234 : _state->json_hash = NULL;
4239 :
4240 234 : return JSON_SUCCESS;
4241 : }
4242 :
4243 : static JsonParseErrorType
4244 300 : populate_recordset_array_element_start(void *state, bool isnull)
4245 : {
4246 300 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
4247 :
4248 300 : if (_state->lex->lex_level == 1 &&
4249 246 : _state->lex->token_type != JSON_TOKEN_OBJECT_START)
4250 0 : ereport(ERROR,
4251 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4252 : errmsg("argument of %s must be an array of objects",
4253 : _state->function_name)));
4254 :
4255 300 : return JSON_SUCCESS;
4256 : }
4257 :
4258 : static JsonParseErrorType
4259 180 : populate_recordset_array_start(void *state)
4260 : {
4261 : /* nothing to do */
4262 180 : return JSON_SUCCESS;
4263 : }
4264 :
4265 : static JsonParseErrorType
4266 516 : populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype)
4267 : {
4268 516 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
4269 :
4270 516 : if (_state->lex->lex_level == 0)
4271 0 : ereport(ERROR,
4272 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4273 : errmsg("cannot call %s on a scalar",
4274 : _state->function_name)));
4275 :
4276 516 : if (_state->lex->lex_level == 2)
4277 420 : _state->saved_scalar = token;
4278 :
4279 516 : return JSON_SUCCESS;
4280 : }
4281 :
4282 : static JsonParseErrorType
4283 516 : populate_recordset_object_field_start(void *state, char *fname, bool isnull)
4284 : {
4285 516 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
4286 :
4287 516 : if (_state->lex->lex_level > 2)
4288 42 : return JSON_SUCCESS;
4289 :
4290 474 : _state->saved_token_type = _state->lex->token_type;
4291 :
4292 474 : if (_state->lex->token_type == JSON_TOKEN_ARRAY_START ||
4293 456 : _state->lex->token_type == JSON_TOKEN_OBJECT_START)
4294 : {
4295 54 : _state->save_json_start = _state->lex->token_start;
4296 : }
4297 : else
4298 : {
4299 420 : _state->save_json_start = NULL;
4300 : }
4301 :
4302 474 : return JSON_SUCCESS;
4303 : }
4304 :
4305 : static JsonParseErrorType
4306 516 : populate_recordset_object_field_end(void *state, char *fname, bool isnull)
4307 : {
4308 516 : PopulateRecordsetState *_state = (PopulateRecordsetState *) state;
4309 : JsonHashEntry *hashentry;
4310 : bool found;
4311 :
4312 : /*
4313 : * Ignore nested fields.
4314 : */
4315 516 : if (_state->lex->lex_level > 2)
4316 42 : return JSON_SUCCESS;
4317 :
4318 : /*
4319 : * Ignore field names >= NAMEDATALEN - they can't match a record field.
4320 : * (Note: without this test, the hash code would truncate the string at
4321 : * NAMEDATALEN-1, and could then match against a similarly-truncated
4322 : * record field name. That would be a reasonable behavior, but this code
4323 : * has previously insisted on exact equality, so we keep this behavior.)
4324 : */
4325 474 : if (strlen(fname) >= NAMEDATALEN)
4326 0 : return JSON_SUCCESS;
4327 :
4328 474 : hashentry = hash_search(_state->json_hash, fname, HASH_ENTER, &found);
4329 :
4330 : /*
4331 : * found being true indicates a duplicate. We don't do anything about
4332 : * that, a later field with the same name overrides the earlier field.
4333 : */
4334 :
4335 474 : hashentry->type = _state->saved_token_type;
4336 : Assert(isnull == (hashentry->type == JSON_TOKEN_NULL));
4337 :
4338 474 : if (_state->save_json_start != NULL)
4339 : {
4340 54 : int len = _state->lex->prev_token_terminator - _state->save_json_start;
4341 54 : char *val = palloc((len + 1) * sizeof(char));
4342 :
4343 54 : memcpy(val, _state->save_json_start, len);
4344 54 : val[len] = '\0';
4345 54 : hashentry->val = val;
4346 : }
4347 : else
4348 : {
4349 : /* must have had a scalar instead */
4350 420 : hashentry->val = _state->saved_scalar;
4351 : }
4352 :
4353 474 : return JSON_SUCCESS;
4354 : }
4355 :
4356 : /*
4357 : * Semantic actions for json_strip_nulls.
4358 : *
4359 : * Simply repeat the input on the output unless we encounter
4360 : * a null object field. State for this is set when the field
4361 : * is started and reset when the scalar action (which must be next)
4362 : * is called.
4363 : */
4364 :
4365 : static JsonParseErrorType
4366 36 : sn_object_start(void *state)
4367 : {
4368 36 : StripnullState *_state = (StripnullState *) state;
4369 :
4370 36 : appendStringInfoCharMacro(_state->strval, '{');
4371 :
4372 36 : return JSON_SUCCESS;
4373 : }
4374 :
4375 : static JsonParseErrorType
4376 36 : sn_object_end(void *state)
4377 : {
4378 36 : StripnullState *_state = (StripnullState *) state;
4379 :
4380 36 : appendStringInfoCharMacro(_state->strval, '}');
4381 :
4382 36 : return JSON_SUCCESS;
4383 : }
4384 :
4385 : static JsonParseErrorType
4386 18 : sn_array_start(void *state)
4387 : {
4388 18 : StripnullState *_state = (StripnullState *) state;
4389 :
4390 18 : appendStringInfoCharMacro(_state->strval, '[');
4391 :
4392 18 : return JSON_SUCCESS;
4393 : }
4394 :
4395 : static JsonParseErrorType
4396 18 : sn_array_end(void *state)
4397 : {
4398 18 : StripnullState *_state = (StripnullState *) state;
4399 :
4400 18 : appendStringInfoCharMacro(_state->strval, ']');
4401 :
4402 18 : return JSON_SUCCESS;
4403 : }
4404 :
4405 : static JsonParseErrorType
4406 78 : sn_object_field_start(void *state, char *fname, bool isnull)
4407 : {
4408 78 : StripnullState *_state = (StripnullState *) state;
4409 :
4410 78 : if (isnull)
4411 : {
4412 : /*
4413 : * The next thing must be a scalar or isnull couldn't be true, so
4414 : * there is no danger of this state being carried down into a nested
4415 : * object or array. The flag will be reset in the scalar action.
4416 : */
4417 30 : _state->skip_next_null = true;
4418 30 : return JSON_SUCCESS;
4419 : }
4420 :
4421 48 : if (_state->strval->data[_state->strval->len - 1] != '{')
4422 24 : appendStringInfoCharMacro(_state->strval, ',');
4423 :
4424 : /*
4425 : * Unfortunately we don't have the quoted and escaped string any more, so
4426 : * we have to re-escape it.
4427 : */
4428 48 : escape_json(_state->strval, fname);
4429 :
4430 48 : appendStringInfoCharMacro(_state->strval, ':');
4431 :
4432 48 : return JSON_SUCCESS;
4433 : }
4434 :
4435 : static JsonParseErrorType
4436 66 : sn_array_element_start(void *state, bool isnull)
4437 : {
4438 66 : StripnullState *_state = (StripnullState *) state;
4439 :
4440 66 : if (_state->strval->data[_state->strval->len - 1] != '[')
4441 48 : appendStringInfoCharMacro(_state->strval, ',');
4442 :
4443 66 : return JSON_SUCCESS;
4444 : }
4445 :
4446 : static JsonParseErrorType
4447 132 : sn_scalar(void *state, char *token, JsonTokenType tokentype)
4448 : {
4449 132 : StripnullState *_state = (StripnullState *) state;
4450 :
4451 132 : if (_state->skip_next_null)
4452 : {
4453 : Assert(tokentype == JSON_TOKEN_NULL);
4454 30 : _state->skip_next_null = false;
4455 30 : return JSON_SUCCESS;
4456 : }
4457 :
4458 102 : if (tokentype == JSON_TOKEN_STRING)
4459 6 : escape_json(_state->strval, token);
4460 : else
4461 96 : appendStringInfoString(_state->strval, token);
4462 :
4463 102 : return JSON_SUCCESS;
4464 : }
4465 :
4466 : /*
4467 : * SQL function json_strip_nulls(json) -> json
4468 : */
4469 : Datum
4470 42 : json_strip_nulls(PG_FUNCTION_ARGS)
4471 : {
4472 42 : text *json = PG_GETARG_TEXT_PP(0);
4473 : StripnullState *state;
4474 : JsonLexContext lex;
4475 : JsonSemAction *sem;
4476 :
4477 42 : state = palloc0(sizeof(StripnullState));
4478 42 : sem = palloc0(sizeof(JsonSemAction));
4479 :
4480 42 : state->lex = makeJsonLexContext(&lex, json, true);
4481 42 : state->strval = makeStringInfo();
4482 42 : state->skip_next_null = false;
4483 :
4484 42 : sem->semstate = (void *) state;
4485 42 : sem->object_start = sn_object_start;
4486 42 : sem->object_end = sn_object_end;
4487 42 : sem->array_start = sn_array_start;
4488 42 : sem->array_end = sn_array_end;
4489 42 : sem->scalar = sn_scalar;
4490 42 : sem->array_element_start = sn_array_element_start;
4491 42 : sem->object_field_start = sn_object_field_start;
4492 :
4493 42 : pg_parse_json_or_ereport(&lex, sem);
4494 :
4495 42 : PG_RETURN_TEXT_P(cstring_to_text_with_len(state->strval->data,
4496 : state->strval->len));
4497 : }
4498 :
4499 : /*
4500 : * SQL function jsonb_strip_nulls(jsonb) -> jsonb
4501 : */
4502 : Datum
4503 42 : jsonb_strip_nulls(PG_FUNCTION_ARGS)
4504 : {
4505 42 : Jsonb *jb = PG_GETARG_JSONB_P(0);
4506 : JsonbIterator *it;
4507 42 : JsonbParseState *parseState = NULL;
4508 42 : JsonbValue *res = NULL;
4509 : JsonbValue v,
4510 : k;
4511 : JsonbIteratorToken type;
4512 42 : bool last_was_key = false;
4513 :
4514 42 : if (JB_ROOT_IS_SCALAR(jb))
4515 18 : PG_RETURN_POINTER(jb);
4516 :
4517 24 : it = JsonbIteratorInit(&jb->root);
4518 :
4519 324 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
4520 : {
4521 : Assert(!(type == WJB_KEY && last_was_key));
4522 :
4523 300 : if (type == WJB_KEY)
4524 : {
4525 : /* stash the key until we know if it has a null value */
4526 78 : k = v;
4527 78 : last_was_key = true;
4528 78 : continue;
4529 : }
4530 :
4531 222 : if (last_was_key)
4532 : {
4533 : /* if the last element was a key this one can't be */
4534 78 : last_was_key = false;
4535 :
4536 : /* skip this field if value is null */
4537 78 : if (type == WJB_VALUE && v.type == jbvNull)
4538 30 : continue;
4539 :
4540 : /* otherwise, do a delayed push of the key */
4541 48 : (void) pushJsonbValue(&parseState, WJB_KEY, &k);
4542 : }
4543 :
4544 192 : if (type == WJB_VALUE || type == WJB_ELEM)
4545 84 : res = pushJsonbValue(&parseState, type, &v);
4546 : else
4547 108 : res = pushJsonbValue(&parseState, type, NULL);
4548 : }
4549 :
4550 : Assert(res != NULL);
4551 :
4552 24 : PG_RETURN_POINTER(JsonbValueToJsonb(res));
4553 : }
4554 :
4555 : /*
4556 : * SQL function jsonb_pretty (jsonb)
4557 : *
4558 : * Pretty-printed text for the jsonb
4559 : */
4560 : Datum
4561 36 : jsonb_pretty(PG_FUNCTION_ARGS)
4562 : {
4563 36 : Jsonb *jb = PG_GETARG_JSONB_P(0);
4564 36 : StringInfo str = makeStringInfo();
4565 :
4566 36 : JsonbToCStringIndent(str, &jb->root, VARSIZE(jb));
4567 :
4568 36 : PG_RETURN_TEXT_P(cstring_to_text_with_len(str->data, str->len));
4569 : }
4570 :
4571 : /*
4572 : * SQL function jsonb_concat (jsonb, jsonb)
4573 : *
4574 : * function for || operator
4575 : */
4576 : Datum
4577 378 : jsonb_concat(PG_FUNCTION_ARGS)
4578 : {
4579 378 : Jsonb *jb1 = PG_GETARG_JSONB_P(0);
4580 378 : Jsonb *jb2 = PG_GETARG_JSONB_P(1);
4581 378 : JsonbParseState *state = NULL;
4582 : JsonbValue *res;
4583 : JsonbIterator *it1,
4584 : *it2;
4585 :
4586 : /*
4587 : * If one of the jsonb is empty, just return the other if it's not scalar
4588 : * and both are of the same kind. If it's a scalar or they are of
4589 : * different kinds we need to perform the concatenation even if one is
4590 : * empty.
4591 : */
4592 378 : if (JB_ROOT_IS_OBJECT(jb1) == JB_ROOT_IS_OBJECT(jb2))
4593 : {
4594 294 : if (JB_ROOT_COUNT(jb1) == 0 && !JB_ROOT_IS_SCALAR(jb2))
4595 198 : PG_RETURN_JSONB_P(jb2);
4596 96 : else if (JB_ROOT_COUNT(jb2) == 0 && !JB_ROOT_IS_SCALAR(jb1))
4597 12 : PG_RETURN_JSONB_P(jb1);
4598 : }
4599 :
4600 168 : it1 = JsonbIteratorInit(&jb1->root);
4601 168 : it2 = JsonbIteratorInit(&jb2->root);
4602 :
4603 168 : res = IteratorConcat(&it1, &it2, &state);
4604 :
4605 : Assert(res != NULL);
4606 :
4607 168 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
4608 : }
4609 :
4610 :
4611 : /*
4612 : * SQL function jsonb_delete (jsonb, text)
4613 : *
4614 : * return a copy of the jsonb with the indicated item
4615 : * removed.
4616 : */
4617 : Datum
4618 180 : jsonb_delete(PG_FUNCTION_ARGS)
4619 : {
4620 180 : Jsonb *in = PG_GETARG_JSONB_P(0);
4621 180 : text *key = PG_GETARG_TEXT_PP(1);
4622 180 : char *keyptr = VARDATA_ANY(key);
4623 180 : int keylen = VARSIZE_ANY_EXHDR(key);
4624 180 : JsonbParseState *state = NULL;
4625 : JsonbIterator *it;
4626 : JsonbValue v,
4627 180 : *res = NULL;
4628 180 : bool skipNested = false;
4629 : JsonbIteratorToken r;
4630 :
4631 180 : if (JB_ROOT_IS_SCALAR(in))
4632 6 : ereport(ERROR,
4633 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4634 : errmsg("cannot delete from scalar")));
4635 :
4636 174 : if (JB_ROOT_COUNT(in) == 0)
4637 12 : PG_RETURN_JSONB_P(in);
4638 :
4639 162 : it = JsonbIteratorInit(&in->root);
4640 :
4641 2148 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
4642 : {
4643 1986 : skipNested = true;
4644 :
4645 1986 : if ((r == WJB_ELEM || r == WJB_KEY) &&
4646 906 : (v.type == jbvString && keylen == v.val.string.len &&
4647 294 : memcmp(keyptr, v.val.string.val, keylen) == 0))
4648 : {
4649 : /* skip corresponding value as well */
4650 150 : if (r == WJB_KEY)
4651 150 : (void) JsonbIteratorNext(&it, &v, true);
4652 :
4653 150 : continue;
4654 : }
4655 :
4656 1836 : res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4657 : }
4658 :
4659 : Assert(res != NULL);
4660 :
4661 162 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
4662 : }
4663 :
4664 : /*
4665 : * SQL function jsonb_delete (jsonb, variadic text[])
4666 : *
4667 : * return a copy of the jsonb with the indicated items
4668 : * removed.
4669 : */
4670 : Datum
4671 18 : jsonb_delete_array(PG_FUNCTION_ARGS)
4672 : {
4673 18 : Jsonb *in = PG_GETARG_JSONB_P(0);
4674 18 : ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
4675 : Datum *keys_elems;
4676 : bool *keys_nulls;
4677 : int keys_len;
4678 18 : JsonbParseState *state = NULL;
4679 : JsonbIterator *it;
4680 : JsonbValue v,
4681 18 : *res = NULL;
4682 18 : bool skipNested = false;
4683 : JsonbIteratorToken r;
4684 :
4685 18 : if (ARR_NDIM(keys) > 1)
4686 0 : ereport(ERROR,
4687 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4688 : errmsg("wrong number of array subscripts")));
4689 :
4690 18 : if (JB_ROOT_IS_SCALAR(in))
4691 0 : ereport(ERROR,
4692 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4693 : errmsg("cannot delete from scalar")));
4694 :
4695 18 : if (JB_ROOT_COUNT(in) == 0)
4696 0 : PG_RETURN_JSONB_P(in);
4697 :
4698 18 : deconstruct_array_builtin(keys, TEXTOID, &keys_elems, &keys_nulls, &keys_len);
4699 :
4700 18 : if (keys_len == 0)
4701 6 : PG_RETURN_JSONB_P(in);
4702 :
4703 12 : it = JsonbIteratorInit(&in->root);
4704 :
4705 90 : while ((r = JsonbIteratorNext(&it, &v, skipNested)) != WJB_DONE)
4706 : {
4707 78 : skipNested = true;
4708 :
4709 78 : if ((r == WJB_ELEM || r == WJB_KEY) && v.type == jbvString)
4710 : {
4711 : int i;
4712 36 : bool found = false;
4713 :
4714 66 : for (i = 0; i < keys_len; i++)
4715 : {
4716 : char *keyptr;
4717 : int keylen;
4718 :
4719 48 : if (keys_nulls[i])
4720 0 : continue;
4721 :
4722 : /* We rely on the array elements not being toasted */
4723 48 : keyptr = VARDATA_ANY(keys_elems[i]);
4724 48 : keylen = VARSIZE_ANY_EXHDR(keys_elems[i]);
4725 48 : if (keylen == v.val.string.len &&
4726 48 : memcmp(keyptr, v.val.string.val, keylen) == 0)
4727 : {
4728 18 : found = true;
4729 18 : break;
4730 : }
4731 : }
4732 36 : if (found)
4733 : {
4734 : /* skip corresponding value as well */
4735 18 : if (r == WJB_KEY)
4736 18 : (void) JsonbIteratorNext(&it, &v, true);
4737 :
4738 18 : continue;
4739 : }
4740 : }
4741 :
4742 60 : res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4743 : }
4744 :
4745 : Assert(res != NULL);
4746 :
4747 12 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
4748 : }
4749 :
4750 : /*
4751 : * SQL function jsonb_delete (jsonb, int)
4752 : *
4753 : * return a copy of the jsonb with the indicated item
4754 : * removed. Negative int means count back from the
4755 : * end of the items.
4756 : */
4757 : Datum
4758 258 : jsonb_delete_idx(PG_FUNCTION_ARGS)
4759 : {
4760 258 : Jsonb *in = PG_GETARG_JSONB_P(0);
4761 258 : int idx = PG_GETARG_INT32(1);
4762 258 : JsonbParseState *state = NULL;
4763 : JsonbIterator *it;
4764 258 : uint32 i = 0,
4765 : n;
4766 : JsonbValue v,
4767 258 : *res = NULL;
4768 : JsonbIteratorToken r;
4769 :
4770 258 : if (JB_ROOT_IS_SCALAR(in))
4771 6 : ereport(ERROR,
4772 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4773 : errmsg("cannot delete from scalar")));
4774 :
4775 252 : if (JB_ROOT_IS_OBJECT(in))
4776 6 : ereport(ERROR,
4777 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4778 : errmsg("cannot delete from object using integer index")));
4779 :
4780 246 : if (JB_ROOT_COUNT(in) == 0)
4781 6 : PG_RETURN_JSONB_P(in);
4782 :
4783 240 : it = JsonbIteratorInit(&in->root);
4784 :
4785 240 : r = JsonbIteratorNext(&it, &v, false);
4786 : Assert(r == WJB_BEGIN_ARRAY);
4787 240 : n = v.val.array.nElems;
4788 :
4789 240 : if (idx < 0)
4790 : {
4791 24 : if (-idx > n)
4792 6 : idx = n;
4793 : else
4794 18 : idx = n + idx;
4795 : }
4796 :
4797 240 : if (idx >= n)
4798 12 : PG_RETURN_JSONB_P(in);
4799 :
4800 228 : pushJsonbValue(&state, r, NULL);
4801 :
4802 756 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
4803 : {
4804 528 : if (r == WJB_ELEM)
4805 : {
4806 300 : if (i++ == idx)
4807 228 : continue;
4808 : }
4809 :
4810 300 : res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
4811 : }
4812 :
4813 : Assert(res != NULL);
4814 :
4815 228 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
4816 : }
4817 :
4818 : /*
4819 : * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
4820 : */
4821 : Datum
4822 288 : jsonb_set(PG_FUNCTION_ARGS)
4823 : {
4824 288 : Jsonb *in = PG_GETARG_JSONB_P(0);
4825 288 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
4826 288 : Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
4827 : JsonbValue newval;
4828 288 : bool create = PG_GETARG_BOOL(3);
4829 288 : JsonbValue *res = NULL;
4830 : Datum *path_elems;
4831 : bool *path_nulls;
4832 : int path_len;
4833 : JsonbIterator *it;
4834 288 : JsonbParseState *st = NULL;
4835 :
4836 288 : JsonbToJsonbValue(newjsonb, &newval);
4837 :
4838 288 : if (ARR_NDIM(path) > 1)
4839 0 : ereport(ERROR,
4840 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4841 : errmsg("wrong number of array subscripts")));
4842 :
4843 288 : if (JB_ROOT_IS_SCALAR(in))
4844 6 : ereport(ERROR,
4845 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4846 : errmsg("cannot set path in scalar")));
4847 :
4848 282 : if (JB_ROOT_COUNT(in) == 0 && !create)
4849 12 : PG_RETURN_JSONB_P(in);
4850 :
4851 270 : deconstruct_array_builtin(path, TEXTOID, &path_elems, &path_nulls, &path_len);
4852 :
4853 270 : if (path_len == 0)
4854 0 : PG_RETURN_JSONB_P(in);
4855 :
4856 270 : it = JsonbIteratorInit(&in->root);
4857 :
4858 270 : res = setPath(&it, path_elems, path_nulls, path_len, &st,
4859 : 0, &newval, create ? JB_PATH_CREATE : JB_PATH_REPLACE);
4860 :
4861 : Assert(res != NULL);
4862 :
4863 240 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
4864 : }
4865 :
4866 :
4867 : /*
4868 : * SQL function jsonb_set_lax(jsonb, text[], jsonb, boolean, text)
4869 : */
4870 : Datum
4871 60 : jsonb_set_lax(PG_FUNCTION_ARGS)
4872 : {
4873 : /* Jsonb *in = PG_GETARG_JSONB_P(0); */
4874 : /* ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); */
4875 : /* Jsonb *newval = PG_GETARG_JSONB_P(2); */
4876 : /* bool create = PG_GETARG_BOOL(3); */
4877 : text *handle_null;
4878 : char *handle_val;
4879 :
4880 60 : if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(3))
4881 0 : PG_RETURN_NULL();
4882 :
4883 : /* could happen if they pass in an explicit NULL */
4884 60 : if (PG_ARGISNULL(4))
4885 6 : ereport(ERROR,
4886 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4887 : errmsg("null_value_treatment must be \"delete_key\", \"return_target\", \"use_json_null\", or \"raise_exception\"")));
4888 :
4889 : /* if the new value isn't an SQL NULL just call jsonb_set */
4890 54 : if (!PG_ARGISNULL(2))
4891 12 : return jsonb_set(fcinfo);
4892 :
4893 42 : handle_null = PG_GETARG_TEXT_P(4);
4894 42 : handle_val = text_to_cstring(handle_null);
4895 :
4896 42 : if (strcmp(handle_val, "raise_exception") == 0)
4897 : {
4898 6 : ereport(ERROR,
4899 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4900 : errmsg("JSON value must not be null"),
4901 : errdetail("Exception was raised because null_value_treatment is \"raise_exception\"."),
4902 : errhint("To avoid, either change the null_value_treatment argument or ensure that an SQL NULL is not passed.")));
4903 : return (Datum) 0; /* silence stupider compilers */
4904 : }
4905 36 : else if (strcmp(handle_val, "use_json_null") == 0)
4906 : {
4907 : Datum newval;
4908 :
4909 18 : newval = DirectFunctionCall1(jsonb_in, CStringGetDatum("null"));
4910 :
4911 18 : fcinfo->args[2].value = newval;
4912 18 : fcinfo->args[2].isnull = false;
4913 18 : return jsonb_set(fcinfo);
4914 : }
4915 18 : else if (strcmp(handle_val, "delete_key") == 0)
4916 : {
4917 6 : return jsonb_delete_path(fcinfo);
4918 : }
4919 12 : else if (strcmp(handle_val, "return_target") == 0)
4920 : {
4921 6 : Jsonb *in = PG_GETARG_JSONB_P(0);
4922 :
4923 6 : PG_RETURN_JSONB_P(in);
4924 : }
4925 : else
4926 : {
4927 6 : ereport(ERROR,
4928 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4929 : errmsg("null_value_treatment must be \"delete_key\", \"return_target\", \"use_json_null\", or \"raise_exception\"")));
4930 : return (Datum) 0; /* silence stupider compilers */
4931 : }
4932 : }
4933 :
4934 : /*
4935 : * SQL function jsonb_delete_path(jsonb, text[])
4936 : */
4937 : Datum
4938 90 : jsonb_delete_path(PG_FUNCTION_ARGS)
4939 : {
4940 90 : Jsonb *in = PG_GETARG_JSONB_P(0);
4941 90 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
4942 90 : JsonbValue *res = NULL;
4943 : Datum *path_elems;
4944 : bool *path_nulls;
4945 : int path_len;
4946 : JsonbIterator *it;
4947 90 : JsonbParseState *st = NULL;
4948 :
4949 90 : if (ARR_NDIM(path) > 1)
4950 0 : ereport(ERROR,
4951 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
4952 : errmsg("wrong number of array subscripts")));
4953 :
4954 90 : if (JB_ROOT_IS_SCALAR(in))
4955 6 : ereport(ERROR,
4956 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4957 : errmsg("cannot delete path in scalar")));
4958 :
4959 84 : if (JB_ROOT_COUNT(in) == 0)
4960 12 : PG_RETURN_JSONB_P(in);
4961 :
4962 72 : deconstruct_array_builtin(path, TEXTOID, &path_elems, &path_nulls, &path_len);
4963 :
4964 72 : if (path_len == 0)
4965 0 : PG_RETURN_JSONB_P(in);
4966 :
4967 72 : it = JsonbIteratorInit(&in->root);
4968 :
4969 72 : res = setPath(&it, path_elems, path_nulls, path_len, &st,
4970 : 0, NULL, JB_PATH_DELETE);
4971 :
4972 : Assert(res != NULL);
4973 :
4974 66 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
4975 : }
4976 :
4977 : /*
4978 : * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
4979 : */
4980 : Datum
4981 132 : jsonb_insert(PG_FUNCTION_ARGS)
4982 : {
4983 132 : Jsonb *in = PG_GETARG_JSONB_P(0);
4984 132 : ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
4985 132 : Jsonb *newjsonb = PG_GETARG_JSONB_P(2);
4986 : JsonbValue newval;
4987 132 : bool after = PG_GETARG_BOOL(3);
4988 132 : JsonbValue *res = NULL;
4989 : Datum *path_elems;
4990 : bool *path_nulls;
4991 : int path_len;
4992 : JsonbIterator *it;
4993 132 : JsonbParseState *st = NULL;
4994 :
4995 132 : JsonbToJsonbValue(newjsonb, &newval);
4996 :
4997 132 : if (ARR_NDIM(path) > 1)
4998 0 : ereport(ERROR,
4999 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
5000 : errmsg("wrong number of array subscripts")));
5001 :
5002 132 : if (JB_ROOT_IS_SCALAR(in))
5003 0 : ereport(ERROR,
5004 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5005 : errmsg("cannot set path in scalar")));
5006 :
5007 132 : deconstruct_array_builtin(path, TEXTOID, &path_elems, &path_nulls, &path_len);
5008 :
5009 132 : if (path_len == 0)
5010 0 : PG_RETURN_JSONB_P(in);
5011 :
5012 132 : it = JsonbIteratorInit(&in->root);
5013 :
5014 132 : res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, &newval,
5015 : after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
5016 :
5017 : Assert(res != NULL);
5018 :
5019 120 : PG_RETURN_JSONB_P(JsonbValueToJsonb(res));
5020 : }
5021 :
5022 : /*
5023 : * Iterate over all jsonb objects and merge them into one.
5024 : * The logic of this function copied from the same hstore function,
5025 : * except the case, when it1 & it2 represents jbvObject.
5026 : * In that case we just append the content of it2 to it1 without any
5027 : * verifications.
5028 : */
5029 : static JsonbValue *
5030 168 : IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
5031 : JsonbParseState **state)
5032 : {
5033 : JsonbValue v1,
5034 : v2,
5035 168 : *res = NULL;
5036 : JsonbIteratorToken r1,
5037 : r2,
5038 : rk1,
5039 : rk2;
5040 :
5041 168 : rk1 = JsonbIteratorNext(it1, &v1, false);
5042 168 : rk2 = JsonbIteratorNext(it2, &v2, false);
5043 :
5044 : /*
5045 : * JsonbIteratorNext reports raw scalars as if they were single-element
5046 : * arrays; hence we only need consider "object" and "array" cases here.
5047 : */
5048 168 : if (rk1 == WJB_BEGIN_OBJECT && rk2 == WJB_BEGIN_OBJECT)
5049 : {
5050 : /*
5051 : * Both inputs are objects.
5052 : *
5053 : * Append all the tokens from v1 to res, except last WJB_END_OBJECT
5054 : * (because res will not be finished yet).
5055 : */
5056 30 : pushJsonbValue(state, rk1, NULL);
5057 174 : while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_OBJECT)
5058 144 : pushJsonbValue(state, r1, &v1);
5059 :
5060 : /*
5061 : * Append all the tokens from v2 to res, including last WJB_END_OBJECT
5062 : * (the concatenation will be completed). Any duplicate keys will
5063 : * automatically override the value from the first object.
5064 : */
5065 156 : while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
5066 126 : res = pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
5067 : }
5068 138 : else if (rk1 == WJB_BEGIN_ARRAY && rk2 == WJB_BEGIN_ARRAY)
5069 : {
5070 : /*
5071 : * Both inputs are arrays.
5072 : */
5073 54 : pushJsonbValue(state, rk1, NULL);
5074 :
5075 120 : while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
5076 : {
5077 : Assert(r1 == WJB_ELEM);
5078 66 : pushJsonbValue(state, r1, &v1);
5079 : }
5080 :
5081 120 : while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_END_ARRAY)
5082 : {
5083 : Assert(r2 == WJB_ELEM);
5084 66 : pushJsonbValue(state, WJB_ELEM, &v2);
5085 : }
5086 :
5087 54 : res = pushJsonbValue(state, WJB_END_ARRAY, NULL /* signal to sort */ );
5088 : }
5089 84 : else if (rk1 == WJB_BEGIN_OBJECT)
5090 : {
5091 : /*
5092 : * We have object || array.
5093 : */
5094 : Assert(rk2 == WJB_BEGIN_ARRAY);
5095 :
5096 18 : pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
5097 :
5098 18 : pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
5099 72 : while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_DONE)
5100 54 : pushJsonbValue(state, r1, r1 != WJB_END_OBJECT ? &v1 : NULL);
5101 :
5102 54 : while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
5103 36 : res = pushJsonbValue(state, r2, r2 != WJB_END_ARRAY ? &v2 : NULL);
5104 : }
5105 : else
5106 : {
5107 : /*
5108 : * We have array || object.
5109 : */
5110 : Assert(rk1 == WJB_BEGIN_ARRAY);
5111 : Assert(rk2 == WJB_BEGIN_OBJECT);
5112 :
5113 66 : pushJsonbValue(state, WJB_BEGIN_ARRAY, NULL);
5114 :
5115 96 : while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_ARRAY)
5116 30 : pushJsonbValue(state, r1, &v1);
5117 :
5118 66 : pushJsonbValue(state, WJB_BEGIN_OBJECT, NULL);
5119 852 : while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_DONE)
5120 786 : pushJsonbValue(state, r2, r2 != WJB_END_OBJECT ? &v2 : NULL);
5121 :
5122 66 : res = pushJsonbValue(state, WJB_END_ARRAY, NULL);
5123 : }
5124 :
5125 168 : return res;
5126 : }
5127 :
5128 : /*
5129 : * Do most of the heavy work for jsonb_set/jsonb_insert
5130 : *
5131 : * If JB_PATH_DELETE bit is set in op_type, the element is to be removed.
5132 : *
5133 : * If any bit mentioned in JB_PATH_CREATE_OR_INSERT is set in op_type,
5134 : * we create the new value if the key or array index does not exist.
5135 : *
5136 : * Bits JB_PATH_INSERT_BEFORE and JB_PATH_INSERT_AFTER in op_type
5137 : * behave as JB_PATH_CREATE if new value is inserted in JsonbObject.
5138 : *
5139 : * If JB_PATH_FILL_GAPS bit is set, this will change an assignment logic in
5140 : * case if target is an array. The assignment index will not be restricted by
5141 : * number of elements in the array, and if there are any empty slots between
5142 : * last element of the array and a new one they will be filled with nulls. If
5143 : * the index is negative, it still will be considered an index from the end
5144 : * of the array. Of a part of the path is not present and this part is more
5145 : * than just one last element, this flag will instruct to create the whole
5146 : * chain of corresponding objects and insert the value.
5147 : *
5148 : * JB_PATH_CONSISTENT_POSITION for an array indicates that the caller wants to
5149 : * keep values with fixed indices. Indices for existing elements could be
5150 : * changed (shifted forward) in case if the array is prepended with a new value
5151 : * and a negative index out of the range, so this behavior will be prevented
5152 : * and return an error.
5153 : *
5154 : * All path elements before the last must already exist
5155 : * whatever bits in op_type are set, or nothing is done.
5156 : */
5157 : static JsonbValue *
5158 1314 : setPath(JsonbIterator **it, Datum *path_elems,
5159 : bool *path_nulls, int path_len,
5160 : JsonbParseState **st, int level, JsonbValue *newval, int op_type)
5161 : {
5162 : JsonbValue v;
5163 : JsonbIteratorToken r;
5164 : JsonbValue *res;
5165 :
5166 1314 : check_stack_depth();
5167 :
5168 1314 : if (path_nulls[level])
5169 18 : ereport(ERROR,
5170 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
5171 : errmsg("path element at position %d is null",
5172 : level + 1)));
5173 :
5174 1296 : r = JsonbIteratorNext(it, &v, false);
5175 :
5176 1296 : switch (r)
5177 : {
5178 378 : case WJB_BEGIN_ARRAY:
5179 :
5180 : /*
5181 : * If instructed complain about attempts to replace within a raw
5182 : * scalar value. This happens even when current level is equal to
5183 : * path_len, because the last path key should also correspond to
5184 : * an object or an array, not raw scalar.
5185 : */
5186 378 : if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1) &&
5187 90 : v.val.array.rawScalar)
5188 12 : ereport(ERROR,
5189 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5190 : errmsg("cannot replace existing key"),
5191 : errdetail("The path assumes key is a composite object, "
5192 : "but it is a scalar value.")));
5193 :
5194 366 : (void) pushJsonbValue(st, r, NULL);
5195 366 : setPathArray(it, path_elems, path_nulls, path_len, st, level,
5196 366 : newval, v.val.array.nElems, op_type);
5197 342 : r = JsonbIteratorNext(it, &v, false);
5198 : Assert(r == WJB_END_ARRAY);
5199 342 : res = pushJsonbValue(st, r, NULL);
5200 342 : break;
5201 888 : case WJB_BEGIN_OBJECT:
5202 888 : (void) pushJsonbValue(st, r, NULL);
5203 888 : setPathObject(it, path_elems, path_nulls, path_len, st, level,
5204 888 : newval, v.val.object.nPairs, op_type);
5205 786 : r = JsonbIteratorNext(it, &v, true);
5206 : Assert(r == WJB_END_OBJECT);
5207 786 : res = pushJsonbValue(st, r, NULL);
5208 786 : break;
5209 30 : case WJB_ELEM:
5210 : case WJB_VALUE:
5211 :
5212 : /*
5213 : * If instructed complain about attempts to replace within a
5214 : * scalar value. This happens even when current level is equal to
5215 : * path_len, because the last path key should also correspond to
5216 : * an object or an array, not an element or value.
5217 : */
5218 30 : if ((op_type & JB_PATH_FILL_GAPS) && (level <= path_len - 1))
5219 30 : ereport(ERROR,
5220 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5221 : errmsg("cannot replace existing key"),
5222 : errdetail("The path assumes key is a composite object, "
5223 : "but it is a scalar value.")));
5224 :
5225 0 : res = pushJsonbValue(st, r, &v);
5226 0 : break;
5227 0 : default:
5228 0 : elog(ERROR, "unrecognized iterator result: %d", (int) r);
5229 : res = NULL; /* keep compiler quiet */
5230 : break;
5231 : }
5232 :
5233 1128 : return res;
5234 : }
5235 :
5236 : /*
5237 : * Object walker for setPath
5238 : */
5239 : static void
5240 888 : setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
5241 : int path_len, JsonbParseState **st, int level,
5242 : JsonbValue *newval, uint32 npairs, int op_type)
5243 : {
5244 888 : text *pathelem = NULL;
5245 : int i;
5246 : JsonbValue k,
5247 : v;
5248 888 : bool done = false;
5249 :
5250 888 : if (level >= path_len || path_nulls[level])
5251 0 : done = true;
5252 : else
5253 : {
5254 : /* The path Datum could be toasted, in which case we must detoast it */
5255 888 : pathelem = DatumGetTextPP(path_elems[level]);
5256 : }
5257 :
5258 : /* empty object is a special case for create */
5259 888 : if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) &&
5260 54 : (level == path_len - 1))
5261 : {
5262 : JsonbValue newkey;
5263 :
5264 18 : newkey.type = jbvString;
5265 18 : newkey.val.string.val = VARDATA_ANY(pathelem);
5266 18 : newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
5267 :
5268 18 : (void) pushJsonbValue(st, WJB_KEY, &newkey);
5269 18 : (void) pushJsonbValue(st, WJB_VALUE, newval);
5270 : }
5271 :
5272 4626 : for (i = 0; i < npairs; i++)
5273 : {
5274 3840 : JsonbIteratorToken r = JsonbIteratorNext(it, &k, true);
5275 :
5276 : Assert(r == WJB_KEY);
5277 :
5278 6066 : if (!done &&
5279 2226 : k.val.string.len == VARSIZE_ANY_EXHDR(pathelem) &&
5280 1128 : memcmp(k.val.string.val, VARDATA_ANY(pathelem),
5281 1128 : k.val.string.len) == 0)
5282 : {
5283 690 : done = true;
5284 :
5285 690 : if (level == path_len - 1)
5286 : {
5287 : /*
5288 : * called from jsonb_insert(), it forbids redefining an
5289 : * existing value
5290 : */
5291 168 : if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER))
5292 12 : ereport(ERROR,
5293 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5294 : errmsg("cannot replace existing key"),
5295 : errhint("Try using the function jsonb_set "
5296 : "to replace key value.")));
5297 :
5298 156 : r = JsonbIteratorNext(it, &v, true); /* skip value */
5299 156 : if (!(op_type & JB_PATH_DELETE))
5300 : {
5301 114 : (void) pushJsonbValue(st, WJB_KEY, &k);
5302 114 : (void) pushJsonbValue(st, WJB_VALUE, newval);
5303 : }
5304 : }
5305 : else
5306 : {
5307 522 : (void) pushJsonbValue(st, r, &k);
5308 522 : setPath(it, path_elems, path_nulls, path_len,
5309 : st, level + 1, newval, op_type);
5310 : }
5311 : }
5312 : else
5313 : {
5314 3150 : if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
5315 336 : level == path_len - 1 && i == npairs - 1)
5316 : {
5317 : JsonbValue newkey;
5318 :
5319 60 : newkey.type = jbvString;
5320 60 : newkey.val.string.val = VARDATA_ANY(pathelem);
5321 60 : newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
5322 :
5323 60 : (void) pushJsonbValue(st, WJB_KEY, &newkey);
5324 60 : (void) pushJsonbValue(st, WJB_VALUE, newval);
5325 : }
5326 :
5327 3150 : (void) pushJsonbValue(st, r, &k);
5328 3150 : r = JsonbIteratorNext(it, &v, false);
5329 3150 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
5330 3150 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
5331 : {
5332 816 : int walking_level = 1;
5333 :
5334 7590 : while (walking_level != 0)
5335 : {
5336 6774 : r = JsonbIteratorNext(it, &v, false);
5337 :
5338 6774 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
5339 264 : ++walking_level;
5340 6774 : if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
5341 1080 : --walking_level;
5342 :
5343 6774 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
5344 : }
5345 : }
5346 : }
5347 : }
5348 :
5349 : /*--
5350 : * If we got here there are only few possibilities:
5351 : * - no target path was found, and an open object with some keys/values was
5352 : * pushed into the state
5353 : * - an object is empty, only WJB_BEGIN_OBJECT is pushed
5354 : *
5355 : * In both cases if instructed to create the path when not present,
5356 : * generate the whole chain of empty objects and insert the new value
5357 : * there.
5358 : */
5359 786 : if (!done && (op_type & JB_PATH_FILL_GAPS) && (level < path_len - 1))
5360 : {
5361 : JsonbValue newkey;
5362 :
5363 48 : newkey.type = jbvString;
5364 48 : newkey.val.string.val = VARDATA_ANY(pathelem);
5365 48 : newkey.val.string.len = VARSIZE_ANY_EXHDR(pathelem);
5366 :
5367 48 : (void) pushJsonbValue(st, WJB_KEY, &newkey);
5368 48 : (void) push_path(st, level, path_elems, path_nulls,
5369 : path_len, newval);
5370 :
5371 : /* Result is closed with WJB_END_OBJECT outside of this function */
5372 : }
5373 786 : }
5374 :
5375 : /*
5376 : * Array walker for setPath
5377 : */
5378 : static void
5379 366 : setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
5380 : int path_len, JsonbParseState **st, int level,
5381 : JsonbValue *newval, uint32 nelems, int op_type)
5382 : {
5383 : JsonbValue v;
5384 : int idx,
5385 : i;
5386 366 : bool done = false;
5387 :
5388 : /* pick correct index */
5389 366 : if (level < path_len && !path_nulls[level])
5390 348 : {
5391 366 : char *c = TextDatumGetCString(path_elems[level]);
5392 : char *badp;
5393 :
5394 366 : errno = 0;
5395 366 : idx = strtoint(c, &badp, 10);
5396 366 : if (badp == c || *badp != '\0' || errno != 0)
5397 18 : ereport(ERROR,
5398 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
5399 : errmsg("path element at position %d is not an integer: \"%s\"",
5400 : level + 1, c)));
5401 : }
5402 : else
5403 0 : idx = nelems;
5404 :
5405 348 : if (idx < 0)
5406 : {
5407 78 : if (-idx > nelems)
5408 : {
5409 : /*
5410 : * If asked to keep elements position consistent, it's not allowed
5411 : * to prepend the array.
5412 : */
5413 24 : if (op_type & JB_PATH_CONSISTENT_POSITION)
5414 6 : ereport(ERROR,
5415 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5416 : errmsg("path element at position %d is out of range: %d",
5417 : level + 1, idx)));
5418 : else
5419 18 : idx = INT_MIN;
5420 : }
5421 : else
5422 54 : idx = nelems + idx;
5423 : }
5424 :
5425 : /*
5426 : * Filling the gaps means there are no limits on the positive index are
5427 : * imposed, we can set any element. Otherwise limit the index by nelems.
5428 : */
5429 342 : if (!(op_type & JB_PATH_FILL_GAPS))
5430 : {
5431 270 : if (idx > 0 && idx > nelems)
5432 48 : idx = nelems;
5433 : }
5434 :
5435 : /*
5436 : * if we're creating, and idx == INT_MIN, we prepend the new value to the
5437 : * array also if the array is empty - in which case we don't really care
5438 : * what the idx value is
5439 : */
5440 342 : if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) &&
5441 66 : (op_type & JB_PATH_CREATE_OR_INSERT))
5442 : {
5443 : Assert(newval != NULL);
5444 :
5445 66 : if (op_type & JB_PATH_FILL_GAPS && nelems == 0 && idx > 0)
5446 6 : push_null_elements(st, idx);
5447 :
5448 66 : (void) pushJsonbValue(st, WJB_ELEM, newval);
5449 :
5450 66 : done = true;
5451 : }
5452 :
5453 : /* iterate over the array elements */
5454 966 : for (i = 0; i < nelems; i++)
5455 : {
5456 : JsonbIteratorToken r;
5457 :
5458 624 : if (i == idx && level < path_len)
5459 : {
5460 216 : done = true;
5461 :
5462 216 : if (level == path_len - 1)
5463 : {
5464 144 : r = JsonbIteratorNext(it, &v, true); /* skip */
5465 :
5466 144 : if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
5467 84 : (void) pushJsonbValue(st, WJB_ELEM, newval);
5468 :
5469 : /*
5470 : * We should keep current value only in case of
5471 : * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER because
5472 : * otherwise it should be deleted or replaced
5473 : */
5474 144 : if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE))
5475 72 : (void) pushJsonbValue(st, r, &v);
5476 :
5477 144 : if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
5478 36 : (void) pushJsonbValue(st, WJB_ELEM, newval);
5479 : }
5480 : else
5481 72 : (void) setPath(it, path_elems, path_nulls, path_len,
5482 : st, level + 1, newval, op_type);
5483 : }
5484 : else
5485 : {
5486 408 : r = JsonbIteratorNext(it, &v, false);
5487 :
5488 408 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
5489 :
5490 408 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
5491 : {
5492 6 : int walking_level = 1;
5493 :
5494 24 : while (walking_level != 0)
5495 : {
5496 18 : r = JsonbIteratorNext(it, &v, false);
5497 :
5498 18 : if (r == WJB_BEGIN_ARRAY || r == WJB_BEGIN_OBJECT)
5499 0 : ++walking_level;
5500 18 : if (r == WJB_END_ARRAY || r == WJB_END_OBJECT)
5501 6 : --walking_level;
5502 :
5503 18 : (void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
5504 : }
5505 : }
5506 : }
5507 : }
5508 :
5509 342 : if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done && level == path_len - 1)
5510 : {
5511 : /*
5512 : * If asked to fill the gaps, idx could be bigger than nelems, so
5513 : * prepend the new element with nulls if that's the case.
5514 : */
5515 36 : if (op_type & JB_PATH_FILL_GAPS && idx > nelems)
5516 12 : push_null_elements(st, idx - nelems);
5517 :
5518 36 : (void) pushJsonbValue(st, WJB_ELEM, newval);
5519 36 : done = true;
5520 : }
5521 :
5522 : /*--
5523 : * If we got here there are only few possibilities:
5524 : * - no target path was found, and an open array with some keys/values was
5525 : * pushed into the state
5526 : * - an array is empty, only WJB_BEGIN_ARRAY is pushed
5527 : *
5528 : * In both cases if instructed to create the path when not present,
5529 : * generate the whole chain of empty objects and insert the new value
5530 : * there.
5531 : */
5532 342 : if (!done && (op_type & JB_PATH_FILL_GAPS) && (level < path_len - 1))
5533 : {
5534 24 : if (idx > 0)
5535 12 : push_null_elements(st, idx - nelems);
5536 :
5537 24 : (void) push_path(st, level, path_elems, path_nulls,
5538 : path_len, newval);
5539 :
5540 : /* Result is closed with WJB_END_OBJECT outside of this function */
5541 : }
5542 342 : }
5543 :
5544 : /*
5545 : * Parse information about what elements of a jsonb document we want to iterate
5546 : * in functions iterate_json(b)_values. This information is presented in jsonb
5547 : * format, so that it can be easily extended in the future.
5548 : */
5549 : uint32
5550 252 : parse_jsonb_index_flags(Jsonb *jb)
5551 : {
5552 : JsonbIterator *it;
5553 : JsonbValue v;
5554 : JsonbIteratorToken type;
5555 252 : uint32 flags = 0;
5556 :
5557 252 : it = JsonbIteratorInit(&jb->root);
5558 :
5559 252 : type = JsonbIteratorNext(&it, &v, false);
5560 :
5561 : /*
5562 : * We iterate over array (scalar internally is represented as array, so,
5563 : * we will accept it too) to check all its elements. Flag names are
5564 : * chosen the same as jsonb_typeof uses.
5565 : */
5566 252 : if (type != WJB_BEGIN_ARRAY)
5567 12 : ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5568 : errmsg("wrong flag type, only arrays and scalars are allowed")));
5569 :
5570 468 : while ((type = JsonbIteratorNext(&it, &v, false)) == WJB_ELEM)
5571 : {
5572 264 : if (v.type != jbvString)
5573 24 : ereport(ERROR,
5574 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5575 : errmsg("flag array element is not a string"),
5576 : errhint("Possible values are: \"string\", \"numeric\", \"boolean\", \"key\", and \"all\".")));
5577 :
5578 348 : if (v.val.string.len == 3 &&
5579 108 : pg_strncasecmp(v.val.string.val, "all", 3) == 0)
5580 84 : flags |= jtiAll;
5581 180 : else if (v.val.string.len == 3 &&
5582 24 : pg_strncasecmp(v.val.string.val, "key", 3) == 0)
5583 24 : flags |= jtiKey;
5584 180 : else if (v.val.string.len == 6 &&
5585 48 : pg_strncasecmp(v.val.string.val, "string", 6) == 0)
5586 48 : flags |= jtiString;
5587 156 : else if (v.val.string.len == 7 &&
5588 72 : pg_strncasecmp(v.val.string.val, "numeric", 7) == 0)
5589 48 : flags |= jtiNumeric;
5590 60 : else if (v.val.string.len == 7 &&
5591 24 : pg_strncasecmp(v.val.string.val, "boolean", 7) == 0)
5592 24 : flags |= jtiBool;
5593 : else
5594 12 : ereport(ERROR,
5595 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
5596 : errmsg("wrong flag in flag array: \"%s\"",
5597 : pnstrdup(v.val.string.val, v.val.string.len)),
5598 : errhint("Possible values are: \"string\", \"numeric\", \"boolean\", \"key\", and \"all\".")));
5599 : }
5600 :
5601 : /* expect end of array now */
5602 204 : if (type != WJB_END_ARRAY)
5603 0 : elog(ERROR, "unexpected end of flag array");
5604 :
5605 : /* get final WJB_DONE and free iterator */
5606 204 : type = JsonbIteratorNext(&it, &v, false);
5607 204 : if (type != WJB_DONE)
5608 0 : elog(ERROR, "unexpected end of flag array");
5609 :
5610 204 : return flags;
5611 : }
5612 :
5613 : /*
5614 : * Iterate over jsonb values or elements, specified by flags, and pass them
5615 : * together with an iteration state to a specified JsonIterateStringValuesAction.
5616 : */
5617 : void
5618 150 : iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
5619 : JsonIterateStringValuesAction action)
5620 : {
5621 : JsonbIterator *it;
5622 : JsonbValue v;
5623 : JsonbIteratorToken type;
5624 :
5625 150 : it = JsonbIteratorInit(&jb->root);
5626 :
5627 : /*
5628 : * Just recursively iterating over jsonb and call callback on all
5629 : * corresponding elements
5630 : */
5631 1644 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
5632 : {
5633 1494 : if (type == WJB_KEY)
5634 : {
5635 558 : if (flags & jtiKey)
5636 144 : action(state, v.val.string.val, v.val.string.len);
5637 :
5638 558 : continue;
5639 : }
5640 936 : else if (!(type == WJB_VALUE || type == WJB_ELEM))
5641 : {
5642 : /* do not call callback for composite JsonbValue */
5643 372 : continue;
5644 : }
5645 :
5646 : /* JsonbValue is a value of object or element of array */
5647 564 : switch (v.type)
5648 : {
5649 150 : case jbvString:
5650 150 : if (flags & jtiString)
5651 108 : action(state, v.val.string.val, v.val.string.len);
5652 150 : break;
5653 168 : case jbvNumeric:
5654 168 : if (flags & jtiNumeric)
5655 : {
5656 : char *val;
5657 :
5658 72 : val = DatumGetCString(DirectFunctionCall1(numeric_out,
5659 : NumericGetDatum(v.val.numeric)));
5660 :
5661 72 : action(state, val, strlen(val));
5662 72 : pfree(val);
5663 : }
5664 168 : break;
5665 156 : case jbvBool:
5666 156 : if (flags & jtiBool)
5667 : {
5668 48 : if (v.val.boolean)
5669 24 : action(state, "true", 4);
5670 : else
5671 24 : action(state, "false", 5);
5672 : }
5673 156 : break;
5674 90 : default:
5675 : /* do not call callback for composite JsonbValue */
5676 90 : break;
5677 : }
5678 : }
5679 150 : }
5680 :
5681 : /*
5682 : * Iterate over json values and elements, specified by flags, and pass them
5683 : * together with an iteration state to a specified JsonIterateStringValuesAction.
5684 : */
5685 : void
5686 150 : iterate_json_values(text *json, uint32 flags, void *action_state,
5687 : JsonIterateStringValuesAction action)
5688 : {
5689 : JsonLexContext lex;
5690 150 : JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
5691 150 : IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
5692 :
5693 150 : state->lex = makeJsonLexContext(&lex, json, true);
5694 150 : state->action = action;
5695 150 : state->action_state = action_state;
5696 150 : state->flags = flags;
5697 :
5698 150 : sem->semstate = (void *) state;
5699 150 : sem->scalar = iterate_values_scalar;
5700 150 : sem->object_field_start = iterate_values_object_field_start;
5701 :
5702 150 : pg_parse_json_or_ereport(&lex, sem);
5703 150 : freeJsonLexContext(&lex);
5704 150 : }
5705 :
5706 : /*
5707 : * An auxiliary function for iterate_json_values to invoke a specified
5708 : * JsonIterateStringValuesAction for specified values.
5709 : */
5710 : static JsonParseErrorType
5711 564 : iterate_values_scalar(void *state, char *token, JsonTokenType tokentype)
5712 : {
5713 564 : IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
5714 :
5715 564 : switch (tokentype)
5716 : {
5717 150 : case JSON_TOKEN_STRING:
5718 150 : if (_state->flags & jtiString)
5719 108 : _state->action(_state->action_state, token, strlen(token));
5720 150 : break;
5721 168 : case JSON_TOKEN_NUMBER:
5722 168 : if (_state->flags & jtiNumeric)
5723 72 : _state->action(_state->action_state, token, strlen(token));
5724 168 : break;
5725 156 : case JSON_TOKEN_TRUE:
5726 : case JSON_TOKEN_FALSE:
5727 156 : if (_state->flags & jtiBool)
5728 48 : _state->action(_state->action_state, token, strlen(token));
5729 156 : break;
5730 90 : default:
5731 : /* do not call callback for any other token */
5732 90 : break;
5733 : }
5734 :
5735 564 : return JSON_SUCCESS;
5736 : }
5737 :
5738 : static JsonParseErrorType
5739 558 : iterate_values_object_field_start(void *state, char *fname, bool isnull)
5740 : {
5741 558 : IterateJsonStringValuesState *_state = (IterateJsonStringValuesState *) state;
5742 :
5743 558 : if (_state->flags & jtiKey)
5744 : {
5745 144 : char *val = pstrdup(fname);
5746 :
5747 144 : _state->action(_state->action_state, val, strlen(val));
5748 : }
5749 :
5750 558 : return JSON_SUCCESS;
5751 : }
5752 :
5753 : /*
5754 : * Iterate over a jsonb, and apply a specified JsonTransformStringValuesAction
5755 : * to every string value or element. Any necessary context for a
5756 : * JsonTransformStringValuesAction can be passed in the action_state variable.
5757 : * Function returns a copy of an original jsonb object with transformed values.
5758 : */
5759 : Jsonb *
5760 42 : transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
5761 : JsonTransformStringValuesAction transform_action)
5762 : {
5763 : JsonbIterator *it;
5764 : JsonbValue v,
5765 42 : *res = NULL;
5766 : JsonbIteratorToken type;
5767 42 : JsonbParseState *st = NULL;
5768 : text *out;
5769 42 : bool is_scalar = false;
5770 :
5771 42 : it = JsonbIteratorInit(&jsonb->root);
5772 42 : is_scalar = it->isScalar;
5773 :
5774 456 : while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
5775 : {
5776 414 : if ((type == WJB_VALUE || type == WJB_ELEM) && v.type == jbvString)
5777 : {
5778 114 : out = transform_action(action_state, v.val.string.val, v.val.string.len);
5779 : /* out is probably not toasted, but let's be sure */
5780 114 : out = pg_detoast_datum_packed(out);
5781 114 : v.val.string.val = VARDATA_ANY(out);
5782 114 : v.val.string.len = VARSIZE_ANY_EXHDR(out);
5783 114 : res = pushJsonbValue(&st, type, type < WJB_BEGIN_ARRAY ? &v : NULL);
5784 : }
5785 : else
5786 : {
5787 486 : res = pushJsonbValue(&st, type, (type == WJB_KEY ||
5788 186 : type == WJB_VALUE ||
5789 : type == WJB_ELEM) ? &v : NULL);
5790 : }
5791 : }
5792 :
5793 42 : if (res->type == jbvArray)
5794 12 : res->val.array.rawScalar = is_scalar;
5795 :
5796 42 : return JsonbValueToJsonb(res);
5797 : }
5798 :
5799 : /*
5800 : * Iterate over a json, and apply a specified JsonTransformStringValuesAction
5801 : * to every string value or element. Any necessary context for a
5802 : * JsonTransformStringValuesAction can be passed in the action_state variable.
5803 : * Function returns a StringInfo, which is a copy of an original json with
5804 : * transformed values.
5805 : */
5806 : text *
5807 42 : transform_json_string_values(text *json, void *action_state,
5808 : JsonTransformStringValuesAction transform_action)
5809 : {
5810 : JsonLexContext lex;
5811 42 : JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
5812 42 : TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
5813 :
5814 42 : state->lex = makeJsonLexContext(&lex, json, true);
5815 42 : state->strval = makeStringInfo();
5816 42 : state->action = transform_action;
5817 42 : state->action_state = action_state;
5818 :
5819 42 : sem->semstate = (void *) state;
5820 42 : sem->object_start = transform_string_values_object_start;
5821 42 : sem->object_end = transform_string_values_object_end;
5822 42 : sem->array_start = transform_string_values_array_start;
5823 42 : sem->array_end = transform_string_values_array_end;
5824 42 : sem->scalar = transform_string_values_scalar;
5825 42 : sem->array_element_start = transform_string_values_array_element_start;
5826 42 : sem->object_field_start = transform_string_values_object_field_start;
5827 :
5828 42 : pg_parse_json_or_ereport(&lex, sem);
5829 42 : freeJsonLexContext(&lex);
5830 :
5831 42 : return cstring_to_text_with_len(state->strval->data, state->strval->len);
5832 : }
5833 :
5834 : /*
5835 : * Set of auxiliary functions for transform_json_string_values to invoke a
5836 : * specified JsonTransformStringValuesAction for all values and left everything
5837 : * else untouched.
5838 : */
5839 : static JsonParseErrorType
5840 54 : transform_string_values_object_start(void *state)
5841 : {
5842 54 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5843 :
5844 54 : appendStringInfoCharMacro(_state->strval, '{');
5845 :
5846 54 : return JSON_SUCCESS;
5847 : }
5848 :
5849 : static JsonParseErrorType
5850 54 : transform_string_values_object_end(void *state)
5851 : {
5852 54 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5853 :
5854 54 : appendStringInfoCharMacro(_state->strval, '}');
5855 :
5856 54 : return JSON_SUCCESS;
5857 : }
5858 :
5859 : static JsonParseErrorType
5860 30 : transform_string_values_array_start(void *state)
5861 : {
5862 30 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5863 :
5864 30 : appendStringInfoCharMacro(_state->strval, '[');
5865 :
5866 30 : return JSON_SUCCESS;
5867 : }
5868 :
5869 : static JsonParseErrorType
5870 30 : transform_string_values_array_end(void *state)
5871 : {
5872 30 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5873 :
5874 30 : appendStringInfoCharMacro(_state->strval, ']');
5875 :
5876 30 : return JSON_SUCCESS;
5877 : }
5878 :
5879 : static JsonParseErrorType
5880 114 : transform_string_values_object_field_start(void *state, char *fname, bool isnull)
5881 : {
5882 114 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5883 :
5884 114 : if (_state->strval->data[_state->strval->len - 1] != '{')
5885 66 : appendStringInfoCharMacro(_state->strval, ',');
5886 :
5887 : /*
5888 : * Unfortunately we don't have the quoted and escaped string any more, so
5889 : * we have to re-escape it.
5890 : */
5891 114 : escape_json(_state->strval, fname);
5892 114 : appendStringInfoCharMacro(_state->strval, ':');
5893 :
5894 114 : return JSON_SUCCESS;
5895 : }
5896 :
5897 : static JsonParseErrorType
5898 48 : transform_string_values_array_element_start(void *state, bool isnull)
5899 : {
5900 48 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5901 :
5902 48 : if (_state->strval->data[_state->strval->len - 1] != '[')
5903 24 : appendStringInfoCharMacro(_state->strval, ',');
5904 :
5905 48 : return JSON_SUCCESS;
5906 : }
5907 :
5908 : static JsonParseErrorType
5909 120 : transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype)
5910 : {
5911 120 : TransformJsonStringValuesState *_state = (TransformJsonStringValuesState *) state;
5912 :
5913 120 : if (tokentype == JSON_TOKEN_STRING)
5914 : {
5915 114 : text *out = _state->action(_state->action_state, token, strlen(token));
5916 :
5917 114 : escape_json(_state->strval, text_to_cstring(out));
5918 : }
5919 : else
5920 6 : appendStringInfoString(_state->strval, token);
5921 :
5922 120 : return JSON_SUCCESS;
5923 : }
5924 :
5925 : JsonTokenType
5926 678 : json_get_first_token(text *json, bool throw_error)
5927 : {
5928 : JsonLexContext lex;
5929 : JsonParseErrorType result;
5930 :
5931 678 : makeJsonLexContext(&lex, json, false);
5932 :
5933 : /* Lex exactly one token from the input and check its type. */
5934 678 : result = json_lex(&lex);
5935 :
5936 678 : if (result == JSON_SUCCESS)
5937 660 : return lex.token_type;
5938 :
5939 18 : if (throw_error)
5940 0 : json_errsave_error(result, &lex, NULL);
5941 :
5942 18 : return JSON_TOKEN_INVALID; /* invalid json */
5943 : }
5944 :
5945 : /*
5946 : * Determine how we want to print values of a given type in datum_to_json(b).
5947 : *
5948 : * Given the datatype OID, return its JsonTypeCategory, as well as the type's
5949 : * output function OID. If the returned category is JSONTYPE_CAST, we return
5950 : * the OID of the type->JSON cast function instead.
5951 : */
5952 : void
5953 6526 : json_categorize_type(Oid typoid, bool is_jsonb,
5954 : JsonTypeCategory *tcategory, Oid *outfuncoid)
5955 : {
5956 : bool typisvarlena;
5957 :
5958 : /* Look through any domain */
5959 6526 : typoid = getBaseType(typoid);
5960 :
5961 6526 : *outfuncoid = InvalidOid;
5962 :
5963 6526 : switch (typoid)
5964 : {
5965 82 : case BOOLOID:
5966 82 : *outfuncoid = F_BOOLOUT;
5967 82 : *tcategory = JSONTYPE_BOOL;
5968 82 : break;
5969 :
5970 3074 : case INT2OID:
5971 : case INT4OID:
5972 : case INT8OID:
5973 : case FLOAT4OID:
5974 : case FLOAT8OID:
5975 : case NUMERICOID:
5976 3074 : getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
5977 3074 : *tcategory = JSONTYPE_NUMERIC;
5978 3074 : break;
5979 :
5980 42 : case DATEOID:
5981 42 : *outfuncoid = F_DATE_OUT;
5982 42 : *tcategory = JSONTYPE_DATE;
5983 42 : break;
5984 :
5985 46 : case TIMESTAMPOID:
5986 46 : *outfuncoid = F_TIMESTAMP_OUT;
5987 46 : *tcategory = JSONTYPE_TIMESTAMP;
5988 46 : break;
5989 :
5990 48 : case TIMESTAMPTZOID:
5991 48 : *outfuncoid = F_TIMESTAMPTZ_OUT;
5992 48 : *tcategory = JSONTYPE_TIMESTAMPTZ;
5993 48 : break;
5994 :
5995 160 : case JSONOID:
5996 160 : getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
5997 160 : *tcategory = JSONTYPE_JSON;
5998 160 : break;
5999 :
6000 382 : case JSONBOID:
6001 382 : getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
6002 382 : *tcategory = is_jsonb ? JSONTYPE_JSONB : JSONTYPE_JSON;
6003 382 : break;
6004 :
6005 2692 : default:
6006 : /* Check for arrays and composites */
6007 2692 : if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
6008 2206 : || typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
6009 : {
6010 486 : *outfuncoid = F_ARRAY_OUT;
6011 486 : *tcategory = JSONTYPE_ARRAY;
6012 : }
6013 2206 : else if (type_is_rowtype(typoid)) /* includes RECORDOID */
6014 : {
6015 334 : *outfuncoid = F_RECORD_OUT;
6016 334 : *tcategory = JSONTYPE_COMPOSITE;
6017 : }
6018 : else
6019 : {
6020 : /*
6021 : * It's probably the general case. But let's look for a cast
6022 : * to json (note: not to jsonb even if is_jsonb is true), if
6023 : * it's not built-in.
6024 : */
6025 1872 : *tcategory = JSONTYPE_OTHER;
6026 1872 : if (typoid >= FirstNormalObjectId)
6027 : {
6028 : Oid castfunc;
6029 : CoercionPathType ctype;
6030 :
6031 4 : ctype = find_coercion_pathway(JSONOID, typoid,
6032 : COERCION_EXPLICIT,
6033 : &castfunc);
6034 4 : if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
6035 : {
6036 4 : *outfuncoid = castfunc;
6037 4 : *tcategory = JSONTYPE_CAST;
6038 : }
6039 : else
6040 : {
6041 : /* non builtin type with no cast */
6042 0 : getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
6043 : }
6044 : }
6045 : else
6046 : {
6047 : /* any other builtin type */
6048 1868 : getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
6049 : }
6050 : }
6051 2692 : break;
6052 : }
6053 6526 : }
|