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