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