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