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