Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * jsonpath_exec.c
4 : * Routines for SQL/JSON path execution.
5 : *
6 : * Jsonpath is executed in the global context stored in JsonPathExecContext,
7 : * which is passed to almost every function involved into execution. Entry
8 : * point for jsonpath execution is executeJsonPath() function, which
9 : * initializes execution context including initial JsonPathItem and JsonbValue,
10 : * flags, stack for calculation of @ in filters.
11 : *
12 : * The result of jsonpath query execution is enum JsonPathExecResult and
13 : * if succeeded sequence of JsonbValue, written to JsonValueList *found, which
14 : * is passed through the jsonpath items. When found == NULL, we're inside
15 : * exists-query and we're interested only in whether result is empty. In this
16 : * case execution is stopped once first result item is found, and the only
17 : * execution result is JsonPathExecResult. The values of JsonPathExecResult
18 : * are following:
19 : * - jperOk -- result sequence is not empty
20 : * - jperNotFound -- result sequence is empty
21 : * - jperError -- error occurred during execution
22 : *
23 : * Jsonpath is executed recursively (see executeItem()) starting form the
24 : * first path item (which in turn might be, for instance, an arithmetic
25 : * expression evaluated separately). On each step single JsonbValue obtained
26 : * from previous path item is processed. The result of processing is a
27 : * sequence of JsonbValue (probably empty), which is passed to the next path
28 : * item one by one. When there is no next path item, then JsonbValue is added
29 : * to the 'found' list. When found == NULL, then execution functions just
30 : * return jperOk (see executeNextItem()).
31 : *
32 : * Many of jsonpath operations require automatic unwrapping of arrays in lax
33 : * mode. So, if input value is array, then corresponding operation is
34 : * processed not on array itself, but on all of its members one by one.
35 : * executeItemOptUnwrapTarget() function have 'unwrap' argument, which indicates
36 : * whether unwrapping of array is needed. When unwrap == true, each of array
37 : * members is passed to executeItemOptUnwrapTarget() again but with unwrap == false
38 : * in order to avoid subsequent array unwrapping.
39 : *
40 : * All boolean expressions (predicates) are evaluated by executeBoolItem()
41 : * function, which returns tri-state JsonPathBool. When error is occurred
42 : * during predicate execution, it returns jpbUnknown. According to standard
43 : * predicates can be only inside filters. But we support their usage as
44 : * jsonpath expression. This helps us to implement @@ operator. In this case
45 : * resulting JsonPathBool is transformed into jsonb bool or null.
46 : *
47 : * Arithmetic and boolean expression are evaluated recursively from expression
48 : * tree top down to the leaves. Therefore, for binary arithmetic expressions
49 : * we calculate operands first. Then we check that results are numeric
50 : * singleton lists, calculate the result and pass it to the next path item.
51 : *
52 : * Copyright (c) 2019-2022, PostgreSQL Global Development Group
53 : *
54 : * IDENTIFICATION
55 : * src/backend/utils/adt/jsonpath_exec.c
56 : *
57 : *-------------------------------------------------------------------------
58 : */
59 :
60 : #include "postgres.h"
61 :
62 : #include "catalog/pg_collation.h"
63 : #include "catalog/pg_type.h"
64 : #include "executor/execExpr.h"
65 : #include "funcapi.h"
66 : #include "lib/stringinfo.h"
67 : #include "miscadmin.h"
68 : #include "nodes/nodeFuncs.h"
69 : #include "regex/regex.h"
70 : #include "utils/builtins.h"
71 : #include "utils/date.h"
72 : #include "utils/datetime.h"
73 : #include "utils/datum.h"
74 : #include "utils/float.h"
75 : #include "utils/formatting.h"
76 : #include "utils/guc.h"
77 : #include "utils/json.h"
78 : #include "utils/jsonpath.h"
79 : #include "utils/lsyscache.h"
80 : #include "utils/memutils.h"
81 : #include "utils/timestamp.h"
82 : #include "utils/varlena.h"
83 :
84 : /*
85 : * Represents "base object" and it's "id" for .keyvalue() evaluation.
86 : */
87 : typedef struct JsonBaseObjectInfo
88 : {
89 : JsonbContainer *jbc;
90 : int id;
91 : } JsonBaseObjectInfo;
92 :
93 : typedef int (*JsonPathVarCallback) (void *vars, char *varName, int varNameLen,
94 : JsonbValue *val, JsonbValue *baseObject);
95 :
96 : /*
97 : * Context of jsonpath execution.
98 : */
99 : typedef struct JsonPathExecContext
100 : {
101 : void *vars; /* variables to substitute into jsonpath */
102 : JsonPathVarCallback getVar;
103 : JsonbValue *root; /* for $ evaluation */
104 : JsonbValue *current; /* for @ evaluation */
105 : JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
106 : * evaluation */
107 : int lastGeneratedObjectId; /* "id" counter for .keyvalue()
108 : * evaluation */
109 : int innermostArraySize; /* for LAST array index evaluation */
110 : bool laxMode; /* true for "lax" mode, false for "strict"
111 : * mode */
112 : bool ignoreStructuralErrors; /* with "true" structural errors such
113 : * as absence of required json item or
114 : * unexpected json item type are
115 : * ignored */
116 : bool throwErrors; /* with "false" all suppressible errors are
117 : * suppressed */
118 : bool useTz;
119 : } JsonPathExecContext;
120 :
121 : /* Context for LIKE_REGEX execution. */
122 : typedef struct JsonLikeRegexContext
123 : {
124 : text *regex;
125 : int cflags;
126 : } JsonLikeRegexContext;
127 :
128 : /* Result of jsonpath predicate evaluation */
129 : typedef enum JsonPathBool
130 : {
131 : jpbFalse = 0,
132 : jpbTrue = 1,
133 : jpbUnknown = 2
134 : } JsonPathBool;
135 :
136 : /* Result of jsonpath expression evaluation */
137 : typedef enum JsonPathExecResult
138 : {
139 : jperOk = 0,
140 : jperNotFound = 1,
141 : jperError = 2
142 : } JsonPathExecResult;
143 :
144 : #define jperIsError(jper) ((jper) == jperError)
145 :
146 : /*
147 : * List of jsonb values with shortcut for single-value list.
148 : */
149 : typedef struct JsonValueList
150 : {
151 : JsonbValue *singleton;
152 : List *list;
153 : } JsonValueList;
154 :
155 : typedef struct JsonValueListIterator
156 : {
157 : JsonbValue *value;
158 : List *list;
159 : ListCell *next;
160 : } JsonValueListIterator;
161 :
162 : /* Structures for JSON_TABLE execution */
163 : typedef struct JsonTableScanState JsonTableScanState;
164 : typedef struct JsonTableJoinState JsonTableJoinState;
165 :
166 : struct JsonTableScanState
167 : {
168 : JsonTableScanState *parent;
169 : JsonTableJoinState *nested;
170 : MemoryContext mcxt;
171 : JsonPath *path;
172 : List *args;
173 : JsonValueList found;
174 : JsonValueListIterator iter;
175 : Datum current;
176 : int ordinal;
177 : bool currentIsNull;
178 : bool outerJoin;
179 : bool errorOnError;
180 : bool advanceNested;
181 : bool reset;
182 : };
183 :
184 : struct JsonTableJoinState
185 : {
186 : union
187 : {
188 : struct
189 : {
190 : JsonTableJoinState *left;
191 : JsonTableJoinState *right;
192 : bool cross;
193 : bool advanceRight;
194 : } join;
195 : JsonTableScanState scan;
196 : } u;
197 : bool is_join;
198 : };
199 :
200 : /* random number to identify JsonTableContext */
201 : #define JSON_TABLE_CONTEXT_MAGIC 418352867
202 :
203 : typedef struct JsonTableContext
204 : {
205 : int magic;
206 : struct
207 : {
208 : ExprState *expr;
209 : JsonTableScanState *scan;
210 : } *colexprs;
211 : JsonTableScanState root;
212 : bool empty;
213 : } JsonTableContext;
214 :
215 : /* strict/lax flags is decomposed into four [un]wrap/error flags */
216 : #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
217 : #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
218 : #define jspAutoWrap(cxt) ((cxt)->laxMode)
219 : #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
220 : #define jspThrowErrors(cxt) ((cxt)->throwErrors)
221 :
222 : /* Convenience macro: return or throw error depending on context */
223 : #define RETURN_ERROR(throw_error) \
224 : do { \
225 : if (jspThrowErrors(cxt)) \
226 : throw_error; \
227 : else \
228 : return jperError; \
229 : } while (0)
230 :
231 : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
232 : JsonbValue *larg,
233 : JsonbValue *rarg,
234 : void *param);
235 : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
236 :
237 : static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
238 : JsonPathVarCallback getVar,
239 : Jsonb *json, bool throwErrors,
240 : JsonValueList *result, bool useTz);
241 : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
242 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
243 : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
244 : JsonPathItem *jsp, JsonbValue *jb,
245 : JsonValueList *found, bool unwrap);
246 : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
247 : JsonPathItem *jsp, JsonbValue *jb,
248 : JsonValueList *found, bool unwrapElements);
249 : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
250 : JsonPathItem *cur, JsonPathItem *next,
251 : JsonbValue *v, JsonValueList *found, bool copy);
252 : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
253 : bool unwrap, JsonValueList *found);
254 : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
255 : JsonbValue *jb, bool unwrap, JsonValueList *found);
256 : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
257 : JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
258 : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
259 : JsonPathItem *jsp, JsonbValue *jb);
260 : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
261 : JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
262 : uint32 level, uint32 first, uint32 last,
263 : bool ignoreStructuralErrors, bool unwrapNext);
264 : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
265 : JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
266 : JsonbValue *jb, bool unwrapRightArg,
267 : JsonPathPredicateCallback exec, void *param);
268 : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
269 : JsonPathItem *jsp, JsonbValue *jb,
270 : BinaryArithmFunc func, JsonValueList *found);
271 : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
272 : JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
273 : JsonValueList *found);
274 : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
275 : JsonbValue *whole, JsonbValue *initial, void *param);
276 : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
277 : JsonbValue *rarg, void *param);
278 : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
279 : JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
280 : JsonValueList *found);
281 : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
282 : JsonbValue *jb, JsonValueList *found);
283 : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
284 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
285 : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
286 : JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
287 : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
288 : JsonbValue *value);
289 : static void getJsonPathVariable(JsonPathExecContext *cxt,
290 : JsonPathItem *variable, JsonbValue *value);
291 : static int getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
292 : int varNameLen, JsonbValue *val,
293 : JsonbValue *baseObject);
294 : static int JsonbArraySize(JsonbValue *jb);
295 : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
296 : JsonbValue *rv, void *p);
297 : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
298 : bool useTz);
299 : static int compareNumeric(Numeric a, Numeric b);
300 : static JsonbValue *copyJsonbValue(JsonbValue *src);
301 : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
302 : JsonPathItem *jsp, JsonbValue *jb, int32 *index);
303 : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
304 : JsonbValue *jbv, int32 id);
305 : static void JsonValueListClear(JsonValueList *jvl);
306 : static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
307 : static int JsonValueListLength(const JsonValueList *jvl);
308 : static bool JsonValueListIsEmpty(JsonValueList *jvl);
309 : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
310 : static List *JsonValueListGetList(JsonValueList *jvl);
311 : static void JsonValueListInitIterator(const JsonValueList *jvl,
312 : JsonValueListIterator *it);
313 : static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
314 : JsonValueListIterator *it);
315 : static int JsonbType(JsonbValue *jb);
316 : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
317 : static int JsonbType(JsonbValue *jb);
318 : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
319 : static JsonbValue *wrapItemsInArray(const JsonValueList *items);
320 : static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
321 : bool useTz, bool *have_error);
322 :
323 :
324 : static JsonTableJoinState *JsonTableInitPlanState(JsonTableContext *cxt,
325 : Node *plan, JsonTableScanState *parent);
326 : static bool JsonTableNextRow(JsonTableScanState *scan);
327 :
328 :
329 : /****************** User interface to JsonPath executor ********************/
330 :
331 : /*
332 : * jsonb_path_exists
333 : * Returns true if jsonpath returns at least one item for the specified
334 : * jsonb value. This function and jsonb_path_match() are used to
335 : * implement @? and @@ operators, which in turn are intended to have an
336 : * index support. Thus, it's desirable to make it easier to achieve
337 : * consistency between index scan results and sequential scan results.
338 : * So, we throw as few errors as possible. Regarding this function,
339 : * such behavior also matches behavior of JSON_EXISTS() clause of
340 : * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
341 : * an analogy in SQL/JSON, so we define its behavior on our own.
342 : */
343 : static Datum
344 85998 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
345 : {
346 85998 : Jsonb *jb = PG_GETARG_JSONB_P(0);
347 85998 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
348 : JsonPathExecResult res;
349 85998 : Jsonb *vars = NULL;
350 85998 : bool silent = true;
351 :
352 85998 : if (PG_NARGS() == 4)
353 : {
354 42 : vars = PG_GETARG_JSONB_P(2);
355 42 : silent = PG_GETARG_BOOL(3);
356 : }
357 :
358 85998 : res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
359 85998 : jb, !silent, NULL, tz);
360 :
361 85992 : PG_FREE_IF_COPY(jb, 0);
362 85992 : PG_FREE_IF_COPY(jp, 1);
363 :
364 85992 : if (jperIsError(res))
365 54 : PG_RETURN_NULL();
366 :
367 85938 : PG_RETURN_BOOL(res == jperOk);
368 : }
369 :
370 : Datum
371 42 : jsonb_path_exists(PG_FUNCTION_ARGS)
372 : {
373 42 : return jsonb_path_exists_internal(fcinfo, false);
374 : }
375 :
376 : Datum
377 0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
378 : {
379 0 : return jsonb_path_exists_internal(fcinfo, true);
380 : }
381 :
382 : /*
383 : * jsonb_path_exists_opr
384 : * Implementation of operator "jsonb @? jsonpath" (2-argument version of
385 : * jsonb_path_exists()).
386 : */
387 : Datum
388 85956 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
389 : {
390 : /* just call the other one -- it can handle both cases */
391 85956 : return jsonb_path_exists_internal(fcinfo, false);
392 : }
393 :
394 : /*
395 : * jsonb_path_match
396 : * Returns jsonpath predicate result item for the specified jsonb value.
397 : * See jsonb_path_exists() comment for details regarding error handling.
398 : */
399 : static Datum
400 97902 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
401 : {
402 97902 : Jsonb *jb = PG_GETARG_JSONB_P(0);
403 97902 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
404 97902 : JsonValueList found = {0};
405 97902 : Jsonb *vars = NULL;
406 97902 : bool silent = true;
407 :
408 97902 : if (PG_NARGS() == 4)
409 : {
410 114 : vars = PG_GETARG_JSONB_P(2);
411 114 : silent = PG_GETARG_BOOL(3);
412 : }
413 :
414 97902 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
415 97902 : jb, !silent, &found, tz);
416 :
417 97896 : PG_FREE_IF_COPY(jb, 0);
418 97896 : PG_FREE_IF_COPY(jp, 1);
419 :
420 97896 : if (JsonValueListLength(&found) == 1)
421 : {
422 97866 : JsonbValue *jbv = JsonValueListHead(&found);
423 :
424 97866 : if (jbv->type == jbvBool)
425 97794 : PG_RETURN_BOOL(jbv->val.boolean);
426 :
427 72 : if (jbv->type == jbvNull)
428 24 : PG_RETURN_NULL();
429 : }
430 :
431 78 : if (!silent)
432 36 : ereport(ERROR,
433 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
434 : errmsg("single boolean result is expected")));
435 :
436 42 : PG_RETURN_NULL();
437 : }
438 :
439 : Datum
440 114 : jsonb_path_match(PG_FUNCTION_ARGS)
441 : {
442 114 : return jsonb_path_match_internal(fcinfo, false);
443 : }
444 :
445 : Datum
446 0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
447 : {
448 0 : return jsonb_path_match_internal(fcinfo, true);
449 : }
450 :
451 : /*
452 : * jsonb_path_match_opr
453 : * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
454 : * jsonb_path_match()).
455 : */
456 : Datum
457 97788 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
458 : {
459 : /* just call the other one -- it can handle both cases */
460 97788 : return jsonb_path_match_internal(fcinfo, false);
461 : }
462 :
463 : /*
464 : * jsonb_path_query
465 : * Executes jsonpath for given jsonb document and returns result as
466 : * rowset.
467 : */
468 : static Datum
469 3600 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
470 : {
471 : FuncCallContext *funcctx;
472 : List *found;
473 : JsonbValue *v;
474 : ListCell *c;
475 :
476 3600 : if (SRF_IS_FIRSTCALL())
477 : {
478 : JsonPath *jp;
479 : Jsonb *jb;
480 : MemoryContext oldcontext;
481 : Jsonb *vars;
482 : bool silent;
483 1860 : JsonValueList found = {0};
484 :
485 1860 : funcctx = SRF_FIRSTCALL_INIT();
486 1860 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
487 :
488 1860 : jb = PG_GETARG_JSONB_P_COPY(0);
489 1860 : jp = PG_GETARG_JSONPATH_P_COPY(1);
490 1860 : vars = PG_GETARG_JSONB_P_COPY(2);
491 1860 : silent = PG_GETARG_BOOL(3);
492 :
493 1860 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
494 1860 : jb, !silent, &found, tz);
495 :
496 1410 : funcctx->user_fctx = JsonValueListGetList(&found);
497 :
498 1410 : MemoryContextSwitchTo(oldcontext);
499 : }
500 :
501 3150 : funcctx = SRF_PERCALL_SETUP();
502 3150 : found = funcctx->user_fctx;
503 :
504 3150 : c = list_head(found);
505 :
506 3150 : if (c == NULL)
507 1410 : SRF_RETURN_DONE(funcctx);
508 :
509 1740 : v = lfirst(c);
510 1740 : funcctx->user_fctx = list_delete_first(found);
511 :
512 1740 : SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
513 : }
514 :
515 : Datum
516 3216 : jsonb_path_query(PG_FUNCTION_ARGS)
517 : {
518 3216 : return jsonb_path_query_internal(fcinfo, false);
519 : }
520 :
521 : Datum
522 384 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
523 : {
524 384 : return jsonb_path_query_internal(fcinfo, true);
525 : }
526 :
527 : /*
528 : * jsonb_path_query_array
529 : * Executes jsonpath for given jsonb document and returns result as
530 : * jsonb array.
531 : */
532 : static Datum
533 48 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
534 : {
535 48 : Jsonb *jb = PG_GETARG_JSONB_P(0);
536 48 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
537 48 : JsonValueList found = {0};
538 48 : Jsonb *vars = PG_GETARG_JSONB_P(2);
539 48 : bool silent = PG_GETARG_BOOL(3);
540 :
541 48 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
542 48 : jb, !silent, &found, tz);
543 :
544 42 : PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
545 : }
546 :
547 : Datum
548 48 : jsonb_path_query_array(PG_FUNCTION_ARGS)
549 : {
550 48 : return jsonb_path_query_array_internal(fcinfo, false);
551 : }
552 :
553 : Datum
554 0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
555 : {
556 0 : return jsonb_path_query_array_internal(fcinfo, true);
557 : }
558 :
559 : /*
560 : * jsonb_path_query_first
561 : * Executes jsonpath for given jsonb document and returns first result
562 : * item. If there are no items, NULL returned.
563 : */
564 : static Datum
565 4362 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
566 : {
567 4362 : Jsonb *jb = PG_GETARG_JSONB_P(0);
568 4362 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
569 4362 : JsonValueList found = {0};
570 4362 : Jsonb *vars = PG_GETARG_JSONB_P(2);
571 4362 : bool silent = PG_GETARG_BOOL(3);
572 :
573 4362 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
574 4362 : jb, !silent, &found, tz);
575 :
576 4356 : if (JsonValueListLength(&found) >= 1)
577 4344 : PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
578 : else
579 12 : PG_RETURN_NULL();
580 : }
581 :
582 : Datum
583 4362 : jsonb_path_query_first(PG_FUNCTION_ARGS)
584 : {
585 4362 : return jsonb_path_query_first_internal(fcinfo, false);
586 : }
587 :
588 : Datum
589 0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
590 : {
591 0 : return jsonb_path_query_first_internal(fcinfo, true);
592 : }
593 :
594 : /********************Execute functions for JsonPath**************************/
595 :
596 : /*
597 : * Interface to jsonpath executor
598 : *
599 : * 'path' - jsonpath to be executed
600 : * 'vars' - variables to be substituted to jsonpath
601 : * 'json' - target document for jsonpath evaluation
602 : * 'throwErrors' - whether we should throw suppressible errors
603 : * 'result' - list to store result items into
604 : *
605 : * Returns an error if a recoverable error happens during processing, or NULL
606 : * on no error.
607 : *
608 : * Note, jsonb and jsonpath values should be available and untoasted during
609 : * work because JsonPathItem, JsonbValue and result item could have pointers
610 : * into input values. If caller needs to just check if document matches
611 : * jsonpath, then it doesn't provide a result arg. In this case executor
612 : * works till first positive result and does not check the rest if possible.
613 : * In other case it tries to find all the satisfied result items.
614 : */
615 : static JsonPathExecResult
616 797304 : executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
617 : Jsonb *json, bool throwErrors, JsonValueList *result,
618 : bool useTz)
619 : {
620 : JsonPathExecContext cxt;
621 : JsonPathExecResult res;
622 : JsonPathItem jsp;
623 : JsonbValue jbv;
624 :
625 797304 : jspInit(&jsp, path);
626 :
627 797304 : if (!JsonbExtractScalar(&json->root, &jbv))
628 192684 : JsonbInitBinary(&jbv, json);
629 :
630 797304 : cxt.vars = vars;
631 797304 : cxt.getVar = getVar;
632 797304 : cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
633 797304 : cxt.ignoreStructuralErrors = cxt.laxMode;
634 797304 : cxt.root = &jbv;
635 797304 : cxt.current = &jbv;
636 797304 : cxt.baseObject.jbc = NULL;
637 797304 : cxt.baseObject.id = 0;
638 : /* 1 + number of base objects in vars */
639 797304 : cxt.lastGeneratedObjectId = 1 + getVar(vars, NULL, 0, NULL, NULL);
640 797292 : cxt.innermostArraySize = -1;
641 797292 : cxt.throwErrors = throwErrors;
642 797292 : cxt.useTz = useTz;
643 :
644 797292 : if (jspStrictAbsenseOfErrors(&cxt) && !result)
645 : {
646 : /*
647 : * In strict mode we must get a complete list of values to check that
648 : * there are no errors at all.
649 : */
650 234 : JsonValueList vals = {0};
651 :
652 234 : res = executeItem(&cxt, &jsp, &jbv, &vals);
653 :
654 222 : if (jperIsError(res))
655 204 : return res;
656 :
657 18 : return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
658 : }
659 :
660 797058 : res = executeItem(&cxt, &jsp, &jbv, result);
661 :
662 : Assert(!throwErrors || !jperIsError(res));
663 :
664 796584 : return res;
665 : }
666 :
667 : /*
668 : * Execute jsonpath with automatic unwrapping of current item in lax mode.
669 : */
670 : static JsonPathExecResult
671 1181268 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
672 : JsonbValue *jb, JsonValueList *found)
673 : {
674 1181268 : return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
675 : }
676 :
677 : /*
678 : * Main jsonpath executor function: walks on jsonpath structure, finds
679 : * relevant parts of jsonb and evaluates expressions over them.
680 : * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
681 : */
682 : static JsonPathExecResult
683 1186860 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
684 : JsonbValue *jb, JsonValueList *found, bool unwrap)
685 : {
686 : JsonPathItem elem;
687 1186860 : JsonPathExecResult res = jperNotFound;
688 : JsonBaseObjectInfo baseObject;
689 :
690 1186860 : check_stack_depth();
691 1186860 : CHECK_FOR_INTERRUPTS();
692 :
693 1186860 : switch (jsp->type)
694 : {
695 : /* all boolean item types: */
696 102174 : case jpiAnd:
697 : case jpiOr:
698 : case jpiNot:
699 : case jpiIsUnknown:
700 : case jpiEqual:
701 : case jpiNotEqual:
702 : case jpiLess:
703 : case jpiGreater:
704 : case jpiLessOrEqual:
705 : case jpiGreaterOrEqual:
706 : case jpiExists:
707 : case jpiStartsWith:
708 : case jpiLikeRegex:
709 : {
710 102174 : JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
711 :
712 102174 : res = appendBoolResult(cxt, jsp, found, st);
713 102174 : break;
714 : }
715 :
716 167286 : case jpiKey:
717 167286 : if (JsonbType(jb) == jbvObject)
718 : {
719 : JsonbValue *v;
720 : JsonbValue key;
721 :
722 166422 : key.type = jbvString;
723 166422 : key.val.string.val = jspGetString(jsp, &key.val.string.len);
724 :
725 166422 : v = findJsonbValueFromContainer(jb->val.binary.data,
726 : JB_FOBJECT, &key);
727 :
728 166422 : if (v != NULL)
729 : {
730 27444 : res = executeNextItem(cxt, jsp, NULL,
731 : v, found, false);
732 :
733 : /* free value if it was not added to found list */
734 27444 : if (jspHasNext(jsp) || !found)
735 16650 : pfree(v);
736 : }
737 138978 : else if (!jspIgnoreStructuralErrors(cxt))
738 : {
739 : Assert(found);
740 :
741 84 : if (!jspThrowErrors(cxt))
742 60 : return jperError;
743 :
744 24 : ereport(ERROR,
745 : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
746 : errmsg("JSON object does not contain key \"%s\"",
747 : pnstrdup(key.val.string.val,
748 : key.val.string.len))));
749 : }
750 : }
751 864 : else if (unwrap && JsonbType(jb) == jbvArray)
752 24 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
753 840 : else if (!jspIgnoreStructuralErrors(cxt))
754 : {
755 : Assert(found);
756 246 : RETURN_ERROR(ereport(ERROR,
757 : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
758 : errmsg("jsonpath member accessor can only be applied to an object"))));
759 : }
760 166932 : break;
761 :
762 809484 : case jpiRoot:
763 809484 : jb = cxt->root;
764 809484 : baseObject = setBaseObject(cxt, jb, 0);
765 809484 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
766 809046 : cxt->baseObject = baseObject;
767 809046 : break;
768 :
769 21540 : case jpiCurrent:
770 21540 : res = executeNextItem(cxt, jsp, NULL, cxt->current,
771 : found, true);
772 21540 : break;
773 :
774 3072 : case jpiAnyArray:
775 3072 : if (JsonbType(jb) == jbvArray)
776 : {
777 2634 : bool hasNext = jspGetNext(jsp, &elem);
778 :
779 2634 : res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
780 2634 : jb, found, jspAutoUnwrap(cxt));
781 : }
782 438 : else if (jspAutoWrap(cxt))
783 186 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
784 252 : else if (!jspIgnoreStructuralErrors(cxt))
785 252 : RETURN_ERROR(ereport(ERROR,
786 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
787 : errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
788 2694 : break;
789 :
790 336 : case jpiIndexArray:
791 336 : if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
792 198 : {
793 318 : int innermostArraySize = cxt->innermostArraySize;
794 : int i;
795 318 : int size = JsonbArraySize(jb);
796 318 : bool singleton = size < 0;
797 318 : bool hasNext = jspGetNext(jsp, &elem);
798 :
799 318 : if (singleton)
800 42 : size = 1;
801 :
802 318 : cxt->innermostArraySize = size; /* for LAST evaluation */
803 :
804 498 : for (i = 0; i < jsp->content.array.nelems; i++)
805 : {
806 : JsonPathItem from;
807 : JsonPathItem to;
808 : int32 index;
809 : int32 index_from;
810 : int32 index_to;
811 330 : bool range = jspGetArraySubscript(jsp, &from,
812 : &to, i);
813 :
814 330 : res = getArrayIndex(cxt, &from, jb, &index_from);
815 :
816 306 : if (jperIsError(res))
817 30 : break;
818 :
819 282 : if (range)
820 : {
821 30 : res = getArrayIndex(cxt, &to, jb, &index_to);
822 :
823 24 : if (jperIsError(res))
824 0 : break;
825 : }
826 : else
827 252 : index_to = index_from;
828 :
829 276 : if (!jspIgnoreStructuralErrors(cxt) &&
830 84 : (index_from < 0 ||
831 72 : index_from > index_to ||
832 72 : index_to >= size))
833 90 : RETURN_ERROR(ereport(ERROR,
834 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
835 : errmsg("jsonpath array subscript is out of bounds"))));
836 :
837 228 : if (index_from < 0)
838 12 : index_from = 0;
839 :
840 228 : if (index_to >= size)
841 36 : index_to = size - 1;
842 :
843 228 : res = jperNotFound;
844 :
845 408 : for (index = index_from; index <= index_to; index++)
846 : {
847 : JsonbValue *v;
848 : bool copy;
849 :
850 228 : if (singleton)
851 : {
852 24 : v = jb;
853 24 : copy = true;
854 : }
855 : else
856 : {
857 204 : v = getIthJsonbValueFromContainer(jb->val.binary.data,
858 : (uint32) index);
859 :
860 204 : if (v == NULL)
861 0 : continue;
862 :
863 204 : copy = false;
864 : }
865 :
866 228 : if (!hasNext && !found)
867 42 : return jperOk;
868 :
869 186 : res = executeNextItem(cxt, jsp, &elem, v, found,
870 : copy);
871 :
872 186 : if (jperIsError(res))
873 0 : break;
874 :
875 186 : if (res == jperOk && !found)
876 6 : break;
877 : }
878 :
879 186 : if (jperIsError(res))
880 0 : break;
881 :
882 186 : if (res == jperOk && !found)
883 6 : break;
884 : }
885 :
886 198 : cxt->innermostArraySize = innermostArraySize;
887 : }
888 18 : else if (!jspIgnoreStructuralErrors(cxt))
889 : {
890 18 : RETURN_ERROR(ereport(ERROR,
891 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
892 : errmsg("jsonpath array accessor can only be applied to an array"))));
893 : }
894 198 : break;
895 :
896 66 : case jpiLast:
897 : {
898 : JsonbValue tmpjbv;
899 : JsonbValue *lastjbv;
900 : int last;
901 66 : bool hasNext = jspGetNext(jsp, &elem);
902 :
903 66 : if (cxt->innermostArraySize < 0)
904 0 : elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
905 :
906 66 : if (!hasNext && !found)
907 : {
908 6 : res = jperOk;
909 6 : break;
910 : }
911 :
912 60 : last = cxt->innermostArraySize - 1;
913 :
914 60 : lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
915 :
916 60 : lastjbv->type = jbvNumeric;
917 60 : lastjbv->val.numeric = int64_to_numeric(last);
918 :
919 60 : res = executeNextItem(cxt, jsp, &elem,
920 : lastjbv, found, hasNext);
921 : }
922 60 : break;
923 :
924 126 : case jpiAnyKey:
925 126 : if (JsonbType(jb) == jbvObject)
926 : {
927 108 : bool hasNext = jspGetNext(jsp, &elem);
928 :
929 108 : if (jb->type != jbvBinary)
930 0 : elog(ERROR, "invalid jsonb object type: %d", jb->type);
931 :
932 108 : return executeAnyItem
933 : (cxt, hasNext ? &elem : NULL,
934 : jb->val.binary.data, found, 1, 1, 1,
935 108 : false, jspAutoUnwrap(cxt));
936 : }
937 18 : else if (unwrap && JsonbType(jb) == jbvArray)
938 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
939 18 : else if (!jspIgnoreStructuralErrors(cxt))
940 : {
941 : Assert(found);
942 12 : RETURN_ERROR(ereport(ERROR,
943 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
944 : errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
945 : }
946 6 : break;
947 :
948 168 : case jpiAdd:
949 168 : return executeBinaryArithmExpr(cxt, jsp, jb,
950 : numeric_add_opt_error, found);
951 :
952 60 : case jpiSub:
953 60 : return executeBinaryArithmExpr(cxt, jsp, jb,
954 : numeric_sub_opt_error, found);
955 :
956 36 : case jpiMul:
957 36 : return executeBinaryArithmExpr(cxt, jsp, jb,
958 : numeric_mul_opt_error, found);
959 :
960 66 : case jpiDiv:
961 66 : return executeBinaryArithmExpr(cxt, jsp, jb,
962 : numeric_div_opt_error, found);
963 :
964 12 : case jpiMod:
965 12 : return executeBinaryArithmExpr(cxt, jsp, jb,
966 : numeric_mod_opt_error, found);
967 :
968 60 : case jpiPlus:
969 60 : return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
970 :
971 126 : case jpiMinus:
972 126 : return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
973 : found);
974 :
975 20178 : case jpiFilter:
976 : {
977 : JsonPathBool st;
978 :
979 20178 : if (unwrap && JsonbType(jb) == jbvArray)
980 126 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
981 : false);
982 :
983 20052 : jspGetArg(jsp, &elem);
984 20052 : st = executeNestedBoolItem(cxt, &elem, jb);
985 19950 : if (st != jpbTrue)
986 17424 : res = jperNotFound;
987 : else
988 2526 : res = executeNextItem(cxt, jsp, NULL,
989 : jb, found, true);
990 19950 : break;
991 : }
992 :
993 306 : case jpiAny:
994 : {
995 306 : bool hasNext = jspGetNext(jsp, &elem);
996 :
997 : /* first try without any intermediate steps */
998 306 : if (jsp->content.anybounds.first == 0)
999 : {
1000 : bool savedIgnoreStructuralErrors;
1001 :
1002 168 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1003 168 : cxt->ignoreStructuralErrors = true;
1004 168 : res = executeNextItem(cxt, jsp, &elem,
1005 : jb, found, true);
1006 168 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1007 :
1008 168 : if (res == jperOk && !found)
1009 6 : break;
1010 : }
1011 :
1012 300 : if (jb->type == jbvBinary)
1013 300 : res = executeAnyItem
1014 : (cxt, hasNext ? &elem : NULL,
1015 : jb->val.binary.data, found,
1016 : 1,
1017 : jsp->content.anybounds.first,
1018 : jsp->content.anybounds.last,
1019 300 : true, jspAutoUnwrap(cxt));
1020 300 : break;
1021 : }
1022 :
1023 57798 : case jpiNull:
1024 : case jpiBool:
1025 : case jpiNumeric:
1026 : case jpiString:
1027 : case jpiVariable:
1028 : {
1029 : JsonbValue vbuf;
1030 : JsonbValue *v;
1031 57798 : bool hasNext = jspGetNext(jsp, &elem);
1032 :
1033 57798 : if (!hasNext && !found)
1034 : {
1035 6 : res = jperOk; /* skip evaluation */
1036 6 : break;
1037 : }
1038 :
1039 57792 : v = hasNext ? &vbuf : palloc(sizeof(*v));
1040 :
1041 57792 : baseObject = cxt->baseObject;
1042 57792 : getJsonPathItem(cxt, jsp, v);
1043 :
1044 57780 : res = executeNextItem(cxt, jsp, &elem,
1045 : v, found, hasNext);
1046 57780 : cxt->baseObject = baseObject;
1047 : }
1048 57780 : break;
1049 :
1050 210 : case jpiType:
1051 : {
1052 210 : JsonbValue *jbv = palloc(sizeof(*jbv));
1053 :
1054 210 : jbv->type = jbvString;
1055 210 : jbv->val.string.val = pstrdup(JsonbTypeName(jb));
1056 210 : jbv->val.string.len = strlen(jbv->val.string.val);
1057 :
1058 210 : res = executeNextItem(cxt, jsp, NULL, jbv,
1059 : found, false);
1060 : }
1061 210 : break;
1062 :
1063 72 : case jpiSize:
1064 : {
1065 72 : int size = JsonbArraySize(jb);
1066 :
1067 72 : if (size < 0)
1068 : {
1069 48 : if (!jspAutoWrap(cxt))
1070 : {
1071 12 : if (!jspIgnoreStructuralErrors(cxt))
1072 12 : RETURN_ERROR(ereport(ERROR,
1073 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1074 : errmsg("jsonpath item method .%s() can only be applied to an array",
1075 : jspOperationName(jsp->type)))));
1076 0 : break;
1077 : }
1078 :
1079 36 : size = 1;
1080 : }
1081 :
1082 60 : jb = palloc(sizeof(*jb));
1083 :
1084 60 : jb->type = jbvNumeric;
1085 60 : jb->val.numeric = int64_to_numeric(size);
1086 :
1087 60 : res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1088 : }
1089 60 : break;
1090 :
1091 108 : case jpiAbs:
1092 108 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1093 : found);
1094 :
1095 48 : case jpiFloor:
1096 48 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1097 : found);
1098 :
1099 102 : case jpiCeiling:
1100 102 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1101 : found);
1102 :
1103 114 : case jpiDouble:
1104 : {
1105 : JsonbValue jbv;
1106 :
1107 114 : if (unwrap && JsonbType(jb) == jbvArray)
1108 42 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1109 : false);
1110 :
1111 108 : if (jb->type == jbvNumeric)
1112 : {
1113 12 : char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1114 : NumericGetDatum(jb->val.numeric)));
1115 : double val;
1116 12 : bool have_error = false;
1117 :
1118 12 : val = float8in_internal_opt_error(tmp,
1119 : NULL,
1120 : "double precision",
1121 : tmp,
1122 : &have_error);
1123 :
1124 12 : if (have_error || isinf(val) || isnan(val))
1125 6 : RETURN_ERROR(ereport(ERROR,
1126 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1127 : errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
1128 : jspOperationName(jsp->type)))));
1129 6 : res = jperOk;
1130 : }
1131 96 : else if (jb->type == jbvString)
1132 : {
1133 : /* cast string as double */
1134 : double val;
1135 48 : char *tmp = pnstrdup(jb->val.string.val,
1136 48 : jb->val.string.len);
1137 48 : bool have_error = false;
1138 :
1139 48 : val = float8in_internal_opt_error(tmp,
1140 : NULL,
1141 : "double precision",
1142 : tmp,
1143 : &have_error);
1144 :
1145 48 : if (have_error || isinf(val) || isnan(val))
1146 42 : RETURN_ERROR(ereport(ERROR,
1147 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1148 : errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
1149 : jspOperationName(jsp->type)))));
1150 :
1151 6 : jb = &jbv;
1152 6 : jb->type = jbvNumeric;
1153 6 : jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1154 : Float8GetDatum(val)));
1155 6 : res = jperOk;
1156 : }
1157 :
1158 60 : if (res == jperNotFound)
1159 48 : RETURN_ERROR(ereport(ERROR,
1160 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1161 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1162 : jspOperationName(jsp->type)))));
1163 :
1164 12 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1165 : }
1166 12 : break;
1167 :
1168 3222 : case jpiDatetime:
1169 3222 : if (unwrap && JsonbType(jb) == jbvArray)
1170 6 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1171 :
1172 3216 : return executeDateTimeMethod(cxt, jsp, jb, found);
1173 :
1174 90 : case jpiKeyValue:
1175 90 : if (unwrap && JsonbType(jb) == jbvArray)
1176 6 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1177 :
1178 84 : return executeKeyValueMethod(cxt, jsp, jb, found);
1179 :
1180 0 : default:
1181 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1182 : }
1183 :
1184 1180980 : return res;
1185 : }
1186 :
1187 : /*
1188 : * Unwrap current array item and execute jsonpath for each of its elements.
1189 : */
1190 : static JsonPathExecResult
1191 2856 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1192 : JsonbValue *jb, JsonValueList *found,
1193 : bool unwrapElements)
1194 : {
1195 2856 : if (jb->type != jbvBinary)
1196 : {
1197 : Assert(jb->type != jbvArray);
1198 0 : elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1199 : }
1200 :
1201 2856 : return executeAnyItem
1202 : (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1203 : false, unwrapElements);
1204 : }
1205 :
1206 : /*
1207 : * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1208 : * list if provided.
1209 : */
1210 : static JsonPathExecResult
1211 1025508 : executeNextItem(JsonPathExecContext *cxt,
1212 : JsonPathItem *cur, JsonPathItem *next,
1213 : JsonbValue *v, JsonValueList *found, bool copy)
1214 : {
1215 : JsonPathItem elem;
1216 : bool hasNext;
1217 :
1218 1025508 : if (!cur)
1219 0 : hasNext = next != NULL;
1220 1025508 : else if (next)
1221 164046 : hasNext = jspHasNext(cur);
1222 : else
1223 : {
1224 861462 : next = &elem;
1225 861462 : hasNext = jspGetNext(cur, next);
1226 : }
1227 :
1228 1025508 : if (hasNext)
1229 189678 : return executeItem(cxt, next, v, found);
1230 :
1231 835830 : if (found)
1232 785694 : JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1233 :
1234 835830 : return jperOk;
1235 : }
1236 :
1237 : /*
1238 : * Same as executeItem(), but when "unwrap == true" automatically unwraps
1239 : * each array item from the resulting sequence in lax mode.
1240 : */
1241 : static JsonPathExecResult
1242 193938 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1243 : JsonbValue *jb, bool unwrap,
1244 : JsonValueList *found)
1245 : {
1246 193938 : if (unwrap && jspAutoUnwrap(cxt))
1247 : {
1248 113364 : JsonValueList seq = {0};
1249 : JsonValueListIterator it;
1250 113364 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1251 : JsonbValue *item;
1252 :
1253 113334 : if (jperIsError(res))
1254 66 : return res;
1255 :
1256 113268 : JsonValueListInitIterator(&seq, &it);
1257 188688 : while ((item = JsonValueListNext(&seq, &it)))
1258 : {
1259 : Assert(item->type != jbvArray);
1260 :
1261 75420 : if (JsonbType(item) == jbvArray)
1262 54 : executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1263 : else
1264 75366 : JsonValueListAppend(found, item);
1265 : }
1266 :
1267 113268 : return jperOk;
1268 : }
1269 :
1270 80574 : return executeItem(cxt, jsp, jb, found);
1271 : }
1272 :
1273 : /*
1274 : * Same as executeItemOptUnwrapResult(), but with error suppression.
1275 : */
1276 : static JsonPathExecResult
1277 193074 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1278 : JsonPathItem *jsp,
1279 : JsonbValue *jb, bool unwrap,
1280 : JsonValueList *found)
1281 : {
1282 : JsonPathExecResult res;
1283 193074 : bool throwErrors = cxt->throwErrors;
1284 :
1285 193074 : cxt->throwErrors = false;
1286 193074 : res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1287 193062 : cxt->throwErrors = throwErrors;
1288 :
1289 193062 : return res;
1290 : }
1291 :
1292 : /* Execute boolean-valued jsonpath expression. */
1293 : static JsonPathBool
1294 176058 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1295 : JsonbValue *jb, bool canHaveNext)
1296 : {
1297 : JsonPathItem larg;
1298 : JsonPathItem rarg;
1299 : JsonPathBool res;
1300 : JsonPathBool res2;
1301 :
1302 176058 : if (!canHaveNext && jspHasNext(jsp))
1303 0 : elog(ERROR, "boolean jsonpath item cannot have next item");
1304 :
1305 176058 : switch (jsp->type)
1306 : {
1307 26448 : case jpiAnd:
1308 26448 : jspGetLeftArg(jsp, &larg);
1309 26448 : res = executeBoolItem(cxt, &larg, jb, false);
1310 :
1311 26448 : if (res == jpbFalse)
1312 22800 : return jpbFalse;
1313 :
1314 : /*
1315 : * SQL/JSON says that we should check second arg in case of
1316 : * jperError
1317 : */
1318 :
1319 3648 : jspGetRightArg(jsp, &rarg);
1320 3648 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1321 :
1322 3648 : return res2 == jpbTrue ? res : res2;
1323 :
1324 12954 : case jpiOr:
1325 12954 : jspGetLeftArg(jsp, &larg);
1326 12954 : res = executeBoolItem(cxt, &larg, jb, false);
1327 :
1328 12954 : if (res == jpbTrue)
1329 2490 : return jpbTrue;
1330 :
1331 10464 : jspGetRightArg(jsp, &rarg);
1332 10464 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1333 :
1334 10464 : return res2 == jpbFalse ? res : res2;
1335 :
1336 108 : case jpiNot:
1337 108 : jspGetArg(jsp, &larg);
1338 :
1339 108 : res = executeBoolItem(cxt, &larg, jb, false);
1340 :
1341 108 : if (res == jpbUnknown)
1342 36 : return jpbUnknown;
1343 :
1344 72 : return res == jpbTrue ? jpbFalse : jpbTrue;
1345 :
1346 210 : case jpiIsUnknown:
1347 210 : jspGetArg(jsp, &larg);
1348 210 : res = executeBoolItem(cxt, &larg, jb, false);
1349 210 : return res == jpbUnknown ? jpbTrue : jpbFalse;
1350 :
1351 56670 : case jpiEqual:
1352 : case jpiNotEqual:
1353 : case jpiLess:
1354 : case jpiGreater:
1355 : case jpiLessOrEqual:
1356 : case jpiGreaterOrEqual:
1357 56670 : jspGetLeftArg(jsp, &larg);
1358 56670 : jspGetRightArg(jsp, &rarg);
1359 56670 : return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1360 : executeComparison, cxt);
1361 :
1362 84 : case jpiStartsWith: /* 'whole STARTS WITH initial' */
1363 84 : jspGetLeftArg(jsp, &larg); /* 'whole' */
1364 84 : jspGetRightArg(jsp, &rarg); /* 'initial' */
1365 84 : return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1366 : executeStartsWith, NULL);
1367 :
1368 396 : case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1369 : {
1370 : /*
1371 : * 'expr' is a sequence-returning expression. 'pattern' is a
1372 : * regex string literal. SQL/JSON standard requires XQuery
1373 : * regexes, but we use Postgres regexes here. 'flags' is a
1374 : * string literal converted to integer flags at compile-time.
1375 : */
1376 396 : JsonLikeRegexContext lrcxt = {0};
1377 :
1378 396 : jspInitByBuffer(&larg, jsp->base,
1379 : jsp->content.like_regex.expr);
1380 :
1381 396 : return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1382 : executeLikeRegex, &lrcxt);
1383 : }
1384 :
1385 79188 : case jpiExists:
1386 79188 : jspGetArg(jsp, &larg);
1387 :
1388 79188 : if (jspStrictAbsenseOfErrors(cxt))
1389 : {
1390 : /*
1391 : * In strict mode we must get a complete list of values to
1392 : * check that there are no errors at all.
1393 : */
1394 48 : JsonValueList vals = {0};
1395 : JsonPathExecResult res =
1396 48 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1397 : false, &vals);
1398 :
1399 48 : if (jperIsError(res))
1400 36 : return jpbUnknown;
1401 :
1402 12 : return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1403 : }
1404 : else
1405 : {
1406 : JsonPathExecResult res =
1407 79140 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1408 : false, NULL);
1409 :
1410 79140 : if (jperIsError(res))
1411 24 : return jpbUnknown;
1412 :
1413 79116 : return res == jperOk ? jpbTrue : jpbFalse;
1414 : }
1415 :
1416 0 : default:
1417 0 : elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1418 : return jpbUnknown;
1419 : }
1420 : }
1421 :
1422 : /*
1423 : * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1424 : * item onto the stack.
1425 : */
1426 : static JsonPathBool
1427 20052 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1428 : JsonbValue *jb)
1429 : {
1430 : JsonbValue *prev;
1431 : JsonPathBool res;
1432 :
1433 20052 : prev = cxt->current;
1434 20052 : cxt->current = jb;
1435 20052 : res = executeBoolItem(cxt, jsp, jb, false);
1436 19950 : cxt->current = prev;
1437 :
1438 19950 : return res;
1439 : }
1440 :
1441 : /*
1442 : * Implementation of several jsonpath nodes:
1443 : * - jpiAny (.** accessor),
1444 : * - jpiAnyKey (.* accessor),
1445 : * - jpiAnyArray ([*] accessor)
1446 : */
1447 : static JsonPathExecResult
1448 3450 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1449 : JsonValueList *found, uint32 level, uint32 first, uint32 last,
1450 : bool ignoreStructuralErrors, bool unwrapNext)
1451 : {
1452 3450 : JsonPathExecResult res = jperNotFound;
1453 : JsonbIterator *it;
1454 : int32 r;
1455 : JsonbValue v;
1456 :
1457 3450 : check_stack_depth();
1458 :
1459 3450 : if (level > last)
1460 30 : return res;
1461 :
1462 3420 : it = JsonbIteratorInit(jbc);
1463 :
1464 : /*
1465 : * Recursively iterate over jsonb objects/arrays
1466 : */
1467 18210 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1468 : {
1469 15384 : if (r == WJB_KEY)
1470 : {
1471 630 : r = JsonbIteratorNext(&it, &v, true);
1472 : Assert(r == WJB_VALUE);
1473 : }
1474 :
1475 15384 : if (r == WJB_VALUE || r == WJB_ELEM)
1476 : {
1477 :
1478 9138 : if (level >= first ||
1479 12 : (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1480 12 : v.type != jbvBinary)) /* leaves only requested */
1481 : {
1482 : /* check expression */
1483 9078 : if (jsp)
1484 : {
1485 5592 : if (ignoreStructuralErrors)
1486 : {
1487 : bool savedIgnoreStructuralErrors;
1488 :
1489 348 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1490 348 : cxt->ignoreStructuralErrors = true;
1491 348 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1492 348 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1493 : }
1494 : else
1495 5244 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1496 :
1497 5466 : if (jperIsError(res))
1498 66 : break;
1499 :
1500 5400 : if (res == jperOk && !found)
1501 342 : break;
1502 : }
1503 3486 : else if (found)
1504 3462 : JsonValueListAppend(found, copyJsonbValue(&v));
1505 : else
1506 24 : return jperOk;
1507 : }
1508 :
1509 8580 : if (level < last && v.type == jbvBinary)
1510 : {
1511 186 : res = executeAnyItem
1512 : (cxt, jsp, v.val.binary.data, found,
1513 : level + 1, first, last,
1514 : ignoreStructuralErrors, unwrapNext);
1515 :
1516 186 : if (jperIsError(res))
1517 0 : break;
1518 :
1519 186 : if (res == jperOk && found == NULL)
1520 36 : break;
1521 : }
1522 : }
1523 : }
1524 :
1525 3270 : return res;
1526 : }
1527 :
1528 : /*
1529 : * Execute unary or binary predicate.
1530 : *
1531 : * Predicates have existence semantics, because their operands are item
1532 : * sequences. Pairs of items from the left and right operand's sequences are
1533 : * checked. TRUE returned only if any pair satisfying the condition is found.
1534 : * In strict mode, even if the desired pair has already been found, all pairs
1535 : * still need to be examined to check the absence of errors. If any error
1536 : * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1537 : */
1538 : static JsonPathBool
1539 57150 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1540 : JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1541 : bool unwrapRightArg, JsonPathPredicateCallback exec,
1542 : void *param)
1543 : {
1544 : JsonPathExecResult res;
1545 : JsonValueListIterator lseqit;
1546 57150 : JsonValueList lseq = {0};
1547 57150 : JsonValueList rseq = {0};
1548 : JsonbValue *lval;
1549 57150 : bool error = false;
1550 57150 : bool found = false;
1551 :
1552 : /* Left argument is always auto-unwrapped. */
1553 57150 : res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1554 57150 : if (jperIsError(res))
1555 18 : return jpbUnknown;
1556 :
1557 57132 : if (rarg)
1558 : {
1559 : /* Right argument is conditionally auto-unwrapped. */
1560 56736 : res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1561 : unwrapRightArg, &rseq);
1562 56724 : if (jperIsError(res))
1563 54 : return jpbUnknown;
1564 : }
1565 :
1566 57066 : JsonValueListInitIterator(&lseq, &lseqit);
1567 76176 : while ((lval = JsonValueListNext(&lseq, &lseqit)))
1568 : {
1569 : JsonValueListIterator rseqit;
1570 : JsonbValue *rval;
1571 24834 : bool first = true;
1572 :
1573 24834 : JsonValueListInitIterator(&rseq, &rseqit);
1574 24834 : if (rarg)
1575 24438 : rval = JsonValueListNext(&rseq, &rseqit);
1576 : else
1577 396 : rval = NULL;
1578 :
1579 : /* Loop over right arg sequence or do single pass otherwise */
1580 39000 : while (rarg ? (rval != NULL) : first)
1581 : {
1582 19890 : JsonPathBool res = exec(pred, lval, rval, param);
1583 :
1584 19800 : if (res == jpbUnknown)
1585 : {
1586 690 : if (jspStrictAbsenseOfErrors(cxt))
1587 5634 : return jpbUnknown;
1588 :
1589 666 : error = true;
1590 : }
1591 19110 : else if (res == jpbTrue)
1592 : {
1593 6150 : if (!jspStrictAbsenseOfErrors(cxt))
1594 5610 : return jpbTrue;
1595 :
1596 540 : found = true;
1597 : }
1598 :
1599 14166 : first = false;
1600 14166 : if (rarg)
1601 13872 : rval = JsonValueListNext(&rseq, &rseqit);
1602 : }
1603 : }
1604 :
1605 51342 : if (found) /* possible only in strict mode */
1606 438 : return jpbTrue;
1607 :
1608 50904 : if (error) /* possible only in lax mode */
1609 636 : return jpbUnknown;
1610 :
1611 50268 : return jpbFalse;
1612 : }
1613 :
1614 : /*
1615 : * Execute binary arithmetic expression on singleton numeric operands.
1616 : * Array operands are automatically unwrapped in lax mode.
1617 : */
1618 : static JsonPathExecResult
1619 342 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1620 : JsonbValue *jb, BinaryArithmFunc func,
1621 : JsonValueList *found)
1622 : {
1623 : JsonPathExecResult jper;
1624 : JsonPathItem elem;
1625 342 : JsonValueList lseq = {0};
1626 342 : JsonValueList rseq = {0};
1627 : JsonbValue *lval;
1628 : JsonbValue *rval;
1629 : Numeric res;
1630 :
1631 342 : jspGetLeftArg(jsp, &elem);
1632 :
1633 : /*
1634 : * XXX: By standard only operands of multiplicative expressions are
1635 : * unwrapped. We extend it to other binary arithmetic expressions too.
1636 : */
1637 342 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1638 336 : if (jperIsError(jper))
1639 0 : return jper;
1640 :
1641 336 : jspGetRightArg(jsp, &elem);
1642 :
1643 336 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1644 330 : if (jperIsError(jper))
1645 0 : return jper;
1646 :
1647 594 : if (JsonValueListLength(&lseq) != 1 ||
1648 264 : !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1649 66 : RETURN_ERROR(ereport(ERROR,
1650 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1651 : errmsg("left operand of jsonpath operator %s is not a single numeric value",
1652 : jspOperationName(jsp->type)))));
1653 :
1654 498 : if (JsonValueListLength(&rseq) != 1 ||
1655 234 : !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1656 54 : RETURN_ERROR(ereport(ERROR,
1657 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1658 : errmsg("right operand of jsonpath operator %s is not a single numeric value",
1659 : jspOperationName(jsp->type)))));
1660 :
1661 210 : if (jspThrowErrors(cxt))
1662 : {
1663 78 : res = func(lval->val.numeric, rval->val.numeric, NULL);
1664 : }
1665 : else
1666 : {
1667 132 : bool error = false;
1668 :
1669 132 : res = func(lval->val.numeric, rval->val.numeric, &error);
1670 :
1671 132 : if (error)
1672 12 : return jperError;
1673 : }
1674 :
1675 174 : if (!jspGetNext(jsp, &elem) && !found)
1676 6 : return jperOk;
1677 :
1678 168 : lval = palloc(sizeof(*lval));
1679 168 : lval->type = jbvNumeric;
1680 168 : lval->val.numeric = res;
1681 :
1682 168 : return executeNextItem(cxt, jsp, &elem, lval, found, false);
1683 : }
1684 :
1685 : /*
1686 : * Execute unary arithmetic expression for each numeric item in its operand's
1687 : * sequence. Array operand is automatically unwrapped in lax mode.
1688 : */
1689 : static JsonPathExecResult
1690 186 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1691 : JsonbValue *jb, PGFunction func, JsonValueList *found)
1692 : {
1693 : JsonPathExecResult jper;
1694 : JsonPathExecResult jper2;
1695 : JsonPathItem elem;
1696 186 : JsonValueList seq = {0};
1697 : JsonValueListIterator it;
1698 : JsonbValue *val;
1699 : bool hasNext;
1700 :
1701 186 : jspGetArg(jsp, &elem);
1702 186 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1703 :
1704 180 : if (jperIsError(jper))
1705 0 : return jper;
1706 :
1707 180 : jper = jperNotFound;
1708 :
1709 180 : hasNext = jspGetNext(jsp, &elem);
1710 :
1711 180 : JsonValueListInitIterator(&seq, &it);
1712 324 : while ((val = JsonValueListNext(&seq, &it)))
1713 : {
1714 192 : if ((val = getScalar(val, jbvNumeric)))
1715 : {
1716 150 : if (!found && !hasNext)
1717 12 : return jperOk;
1718 : }
1719 : else
1720 : {
1721 42 : if (!found && !hasNext)
1722 6 : continue; /* skip non-numerics processing */
1723 :
1724 36 : RETURN_ERROR(ereport(ERROR,
1725 : (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1726 : errmsg("operand of unary jsonpath operator %s is not a numeric value",
1727 : jspOperationName(jsp->type)))));
1728 : }
1729 :
1730 138 : if (func)
1731 84 : val->val.numeric =
1732 84 : DatumGetNumeric(DirectFunctionCall1(func,
1733 : NumericGetDatum(val->val.numeric)));
1734 :
1735 138 : jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1736 :
1737 138 : if (jperIsError(jper2))
1738 0 : return jper2;
1739 :
1740 138 : if (jper2 == jperOk)
1741 : {
1742 138 : if (!found)
1743 0 : return jperOk;
1744 138 : jper = jperOk;
1745 : }
1746 : }
1747 :
1748 132 : return jper;
1749 : }
1750 :
1751 : /*
1752 : * STARTS_WITH predicate callback.
1753 : *
1754 : * Check if the 'whole' string starts from 'initial' string.
1755 : */
1756 : static JsonPathBool
1757 174 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1758 : void *param)
1759 : {
1760 174 : if (!(whole = getScalar(whole, jbvString)))
1761 48 : return jpbUnknown; /* error */
1762 :
1763 126 : if (!(initial = getScalar(initial, jbvString)))
1764 0 : return jpbUnknown; /* error */
1765 :
1766 126 : if (whole->val.string.len >= initial->val.string.len &&
1767 90 : !memcmp(whole->val.string.val,
1768 90 : initial->val.string.val,
1769 90 : initial->val.string.len))
1770 54 : return jpbTrue;
1771 :
1772 72 : return jpbFalse;
1773 : }
1774 :
1775 : /*
1776 : * LIKE_REGEX predicate callback.
1777 : *
1778 : * Check if the string matches regex pattern.
1779 : */
1780 : static JsonPathBool
1781 396 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1782 : void *param)
1783 : {
1784 396 : JsonLikeRegexContext *cxt = param;
1785 :
1786 396 : if (!(str = getScalar(str, jbvString)))
1787 120 : return jpbUnknown;
1788 :
1789 : /* Cache regex text and converted flags. */
1790 276 : if (!cxt->regex)
1791 : {
1792 276 : cxt->regex =
1793 276 : cstring_to_text_with_len(jsp->content.like_regex.pattern,
1794 : jsp->content.like_regex.patternlen);
1795 276 : cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
1796 : }
1797 :
1798 276 : if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1799 : str->val.string.len,
1800 : cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1801 102 : return jpbTrue;
1802 :
1803 174 : return jpbFalse;
1804 : }
1805 :
1806 : /*
1807 : * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1808 : * user function 'func'.
1809 : */
1810 : static JsonPathExecResult
1811 258 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1812 : JsonbValue *jb, bool unwrap, PGFunction func,
1813 : JsonValueList *found)
1814 : {
1815 : JsonPathItem next;
1816 : Datum datum;
1817 :
1818 258 : if (unwrap && JsonbType(jb) == jbvArray)
1819 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1820 :
1821 258 : if (!(jb = getScalar(jb, jbvNumeric)))
1822 36 : RETURN_ERROR(ereport(ERROR,
1823 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1824 : errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1825 : jspOperationName(jsp->type)))));
1826 :
1827 222 : datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1828 :
1829 222 : if (!jspGetNext(jsp, &next) && !found)
1830 0 : return jperOk;
1831 :
1832 222 : jb = palloc(sizeof(*jb));
1833 222 : jb->type = jbvNumeric;
1834 222 : jb->val.numeric = DatumGetNumeric(datum);
1835 :
1836 222 : return executeNextItem(cxt, jsp, &next, jb, found, false);
1837 : }
1838 :
1839 : /*
1840 : * Implementation of the .datetime() method.
1841 : *
1842 : * Converts a string into a date/time value. The actual type is determined at run time.
1843 : * If an argument is provided, this argument is used as a template string.
1844 : * Otherwise, the first fitting ISO format is selected.
1845 : */
1846 : static JsonPathExecResult
1847 3216 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1848 : JsonbValue *jb, JsonValueList *found)
1849 : {
1850 : JsonbValue jbvbuf;
1851 : Datum value;
1852 : text *datetime;
1853 : Oid collid;
1854 : Oid typid;
1855 3216 : int32 typmod = -1;
1856 3216 : int tz = 0;
1857 : bool hasNext;
1858 3216 : JsonPathExecResult res = jperNotFound;
1859 : JsonPathItem elem;
1860 :
1861 3216 : if (!(jb = getScalar(jb, jbvString)))
1862 30 : RETURN_ERROR(ereport(ERROR,
1863 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1864 : errmsg("jsonpath item method .%s() can only be applied to a string",
1865 : jspOperationName(jsp->type)))));
1866 :
1867 3186 : datetime = cstring_to_text_with_len(jb->val.string.val,
1868 : jb->val.string.len);
1869 :
1870 : /*
1871 : * At some point we might wish to have callers supply the collation to
1872 : * use, but right now it's unclear that they'd be able to do better than
1873 : * DEFAULT_COLLATION_OID anyway.
1874 : */
1875 3186 : collid = DEFAULT_COLLATION_OID;
1876 :
1877 3186 : if (jsp->content.arg)
1878 : {
1879 : text *template;
1880 : char *template_str;
1881 : int template_len;
1882 1650 : bool have_error = false;
1883 :
1884 1650 : jspGetArg(jsp, &elem);
1885 :
1886 1650 : if (elem.type != jpiString)
1887 0 : elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1888 :
1889 1650 : template_str = jspGetString(&elem, &template_len);
1890 :
1891 1650 : template = cstring_to_text_with_len(template_str,
1892 : template_len);
1893 :
1894 1650 : value = parse_datetime(datetime, template, collid, true,
1895 : &typid, &typmod, &tz,
1896 1650 : jspThrowErrors(cxt) ? NULL : &have_error);
1897 :
1898 1590 : if (have_error)
1899 0 : res = jperError;
1900 : else
1901 1590 : res = jperOk;
1902 : }
1903 : else
1904 : {
1905 : /*
1906 : * According to SQL/JSON standard enumerate ISO formats for: date,
1907 : * timetz, time, timestamptz, timestamp.
1908 : *
1909 : * We also support ISO 8601 for timestamps, because to_json[b]()
1910 : * functions use this format.
1911 : */
1912 : static const char *fmt_str[] =
1913 : {
1914 : "yyyy-mm-dd",
1915 : "HH24:MI:SSTZH:TZM",
1916 : "HH24:MI:SSTZH",
1917 : "HH24:MI:SS",
1918 : "yyyy-mm-dd HH24:MI:SSTZH:TZM",
1919 : "yyyy-mm-dd HH24:MI:SSTZH",
1920 : "yyyy-mm-dd HH24:MI:SS",
1921 : "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
1922 : "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
1923 : "yyyy-mm-dd\"T\"HH24:MI:SS"
1924 : };
1925 :
1926 : /* cache for format texts */
1927 : static text *fmt_txt[lengthof(fmt_str)] = {0};
1928 : int i;
1929 :
1930 : /* loop until datetime format fits */
1931 6828 : for (i = 0; i < lengthof(fmt_str); i++)
1932 : {
1933 6816 : bool have_error = false;
1934 :
1935 6816 : if (!fmt_txt[i])
1936 : {
1937 : MemoryContext oldcxt =
1938 60 : MemoryContextSwitchTo(TopMemoryContext);
1939 :
1940 60 : fmt_txt[i] = cstring_to_text(fmt_str[i]);
1941 60 : MemoryContextSwitchTo(oldcxt);
1942 : }
1943 :
1944 6816 : value = parse_datetime(datetime, fmt_txt[i], collid, true,
1945 : &typid, &typmod, &tz,
1946 : &have_error);
1947 :
1948 6816 : if (!have_error)
1949 : {
1950 1524 : res = jperOk;
1951 1524 : break;
1952 : }
1953 : }
1954 :
1955 1536 : if (res == jperNotFound)
1956 12 : RETURN_ERROR(ereport(ERROR,
1957 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1958 : errmsg("datetime format is not recognized: \"%s\"",
1959 : text_to_cstring(datetime)),
1960 : errhint("Use a datetime template argument to specify the input data format."))));
1961 : }
1962 :
1963 3114 : pfree(datetime);
1964 :
1965 3114 : if (jperIsError(res))
1966 0 : return res;
1967 :
1968 3114 : hasNext = jspGetNext(jsp, &elem);
1969 :
1970 3114 : if (!hasNext && !found)
1971 6 : return res;
1972 :
1973 3108 : jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1974 :
1975 3108 : jb->type = jbvDatetime;
1976 3108 : jb->val.datetime.value = value;
1977 3108 : jb->val.datetime.typid = typid;
1978 3108 : jb->val.datetime.typmod = typmod;
1979 3108 : jb->val.datetime.tz = tz;
1980 :
1981 3108 : return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1982 : }
1983 :
1984 : /*
1985 : * Implementation of .keyvalue() method.
1986 : *
1987 : * .keyvalue() method returns a sequence of object's key-value pairs in the
1988 : * following format: '{ "key": key, "value": value, "id": id }'.
1989 : *
1990 : * "id" field is an object identifier which is constructed from the two parts:
1991 : * base object id and its binary offset in base object's jsonb:
1992 : * id = 10000000000 * base_object_id + obj_offset_in_base_object
1993 : *
1994 : * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1995 : * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1996 : * readability of identifiers.
1997 : *
1998 : * Base object is usually a root object of the path: context item '$' or path
1999 : * variable '$var', literals can't produce objects for now. But if the path
2000 : * contains generated objects (.keyvalue() itself, for example), then they
2001 : * become base object for the subsequent .keyvalue().
2002 : *
2003 : * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
2004 : * of variables (see getJsonPathVariable()). Ids for generated objects
2005 : * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
2006 : */
2007 : static JsonPathExecResult
2008 84 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2009 : JsonbValue *jb, JsonValueList *found)
2010 : {
2011 84 : JsonPathExecResult res = jperNotFound;
2012 : JsonPathItem next;
2013 : JsonbContainer *jbc;
2014 : JsonbValue key;
2015 : JsonbValue val;
2016 : JsonbValue idval;
2017 : JsonbValue keystr;
2018 : JsonbValue valstr;
2019 : JsonbValue idstr;
2020 : JsonbIterator *it;
2021 : JsonbIteratorToken tok;
2022 : int64 id;
2023 : bool hasNext;
2024 :
2025 84 : if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
2026 24 : RETURN_ERROR(ereport(ERROR,
2027 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
2028 : errmsg("jsonpath item method .%s() can only be applied to an object",
2029 : jspOperationName(jsp->type)))));
2030 :
2031 60 : jbc = jb->val.binary.data;
2032 :
2033 60 : if (!JsonContainerSize(jbc))
2034 18 : return jperNotFound; /* no key-value pairs */
2035 :
2036 42 : hasNext = jspGetNext(jsp, &next);
2037 :
2038 42 : keystr.type = jbvString;
2039 42 : keystr.val.string.val = "key";
2040 42 : keystr.val.string.len = 3;
2041 :
2042 42 : valstr.type = jbvString;
2043 42 : valstr.val.string.val = "value";
2044 42 : valstr.val.string.len = 5;
2045 :
2046 42 : idstr.type = jbvString;
2047 42 : idstr.val.string.val = "id";
2048 42 : idstr.val.string.len = 2;
2049 :
2050 : /* construct object id from its base object and offset inside that */
2051 42 : id = jb->type != jbvBinary ? 0 :
2052 42 : (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
2053 42 : id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
2054 :
2055 42 : idval.type = jbvNumeric;
2056 42 : idval.val.numeric = int64_to_numeric(id);
2057 :
2058 42 : it = JsonbIteratorInit(jbc);
2059 :
2060 168 : while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
2061 : {
2062 : JsonBaseObjectInfo baseObject;
2063 : JsonbValue obj;
2064 : JsonbParseState *ps;
2065 : JsonbValue *keyval;
2066 : Jsonb *jsonb;
2067 :
2068 138 : if (tok != WJB_KEY)
2069 72 : continue;
2070 :
2071 66 : res = jperOk;
2072 :
2073 66 : if (!hasNext && !found)
2074 12 : break;
2075 :
2076 60 : tok = JsonbIteratorNext(&it, &val, true);
2077 : Assert(tok == WJB_VALUE);
2078 :
2079 60 : ps = NULL;
2080 60 : pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2081 :
2082 60 : pushJsonbValue(&ps, WJB_KEY, &keystr);
2083 60 : pushJsonbValue(&ps, WJB_VALUE, &key);
2084 :
2085 60 : pushJsonbValue(&ps, WJB_KEY, &valstr);
2086 60 : pushJsonbValue(&ps, WJB_VALUE, &val);
2087 :
2088 60 : pushJsonbValue(&ps, WJB_KEY, &idstr);
2089 60 : pushJsonbValue(&ps, WJB_VALUE, &idval);
2090 :
2091 60 : keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2092 :
2093 60 : jsonb = JsonbValueToJsonb(keyval);
2094 :
2095 60 : JsonbInitBinary(&obj, jsonb);
2096 :
2097 60 : baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2098 :
2099 60 : res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2100 :
2101 60 : cxt->baseObject = baseObject;
2102 :
2103 60 : if (jperIsError(res))
2104 0 : return res;
2105 :
2106 60 : if (res == jperOk && !found)
2107 6 : break;
2108 : }
2109 :
2110 42 : return res;
2111 : }
2112 :
2113 : /*
2114 : * Convert boolean execution status 'res' to a boolean JSON item and execute
2115 : * next jsonpath.
2116 : */
2117 : static JsonPathExecResult
2118 102174 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2119 : JsonValueList *found, JsonPathBool res)
2120 : {
2121 : JsonPathItem next;
2122 : JsonbValue jbv;
2123 :
2124 102174 : if (!jspGetNext(jsp, &next) && !found)
2125 18 : return jperOk; /* found singleton boolean value */
2126 :
2127 102156 : if (res == jpbUnknown)
2128 : {
2129 30 : jbv.type = jbvNull;
2130 : }
2131 : else
2132 : {
2133 102126 : jbv.type = jbvBool;
2134 102126 : jbv.val.boolean = res == jpbTrue;
2135 : }
2136 :
2137 102156 : return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2138 : }
2139 :
2140 : /*
2141 : * Convert jsonpath's scalar or variable node to actual jsonb value.
2142 : *
2143 : * If node is a variable then its id returned, otherwise 0 returned.
2144 : */
2145 : static void
2146 57792 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2147 : JsonbValue *value)
2148 : {
2149 57792 : switch (item->type)
2150 : {
2151 7554 : case jpiNull:
2152 7554 : value->type = jbvNull;
2153 7554 : break;
2154 1404 : case jpiBool:
2155 1404 : value->type = jbvBool;
2156 1404 : value->val.boolean = jspGetBool(item);
2157 1404 : break;
2158 19410 : case jpiNumeric:
2159 19410 : value->type = jbvNumeric;
2160 19410 : value->val.numeric = jspGetNumeric(item);
2161 19410 : break;
2162 21954 : case jpiString:
2163 21954 : value->type = jbvString;
2164 43908 : value->val.string.val = jspGetString(item,
2165 21954 : &value->val.string.len);
2166 21954 : break;
2167 7470 : case jpiVariable:
2168 7470 : getJsonPathVariable(cxt, item, value);
2169 7458 : return;
2170 0 : default:
2171 0 : elog(ERROR, "unexpected jsonpath item type");
2172 : }
2173 : }
2174 :
2175 : /*
2176 : * Get the value of variable passed to jsonpath executor
2177 : */
2178 : static void
2179 7470 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2180 : JsonbValue *value)
2181 : {
2182 : char *varName;
2183 : int varNameLength;
2184 : JsonbValue baseObject;
2185 : int baseObjectId;
2186 :
2187 : Assert(variable->type == jpiVariable);
2188 7470 : varName = jspGetString(variable, &varNameLength);
2189 :
2190 14934 : if (!cxt->vars ||
2191 7464 : (baseObjectId = cxt->getVar(cxt->vars, varName, varNameLength, value,
2192 : &baseObject)) < 0)
2193 12 : ereport(ERROR,
2194 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2195 : errmsg("could not find jsonpath variable \"%s\"",
2196 : pnstrdup(varName, varNameLength))));
2197 :
2198 7458 : if (baseObjectId > 0)
2199 7458 : setBaseObject(cxt, &baseObject, baseObjectId);
2200 7458 : }
2201 :
2202 : static int
2203 195396 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
2204 : JsonbValue *value, JsonbValue *baseObject)
2205 : {
2206 195396 : Jsonb *vars = varsJsonb;
2207 : JsonbValue tmp;
2208 : JsonbValue *v;
2209 :
2210 195396 : if (!varName)
2211 : {
2212 190170 : if (vars && !JsonContainerIsObject(&vars->root))
2213 : {
2214 12 : ereport(ERROR,
2215 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
2216 : errmsg("\"vars\" argument is not an object"),
2217 : errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
2218 : }
2219 :
2220 190158 : return vars ? 1 : 0; /* count of base objects */
2221 : }
2222 :
2223 5226 : tmp.type = jbvString;
2224 5226 : tmp.val.string.val = varName;
2225 5226 : tmp.val.string.len = varNameLength;
2226 :
2227 5226 : v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2228 :
2229 5226 : if (!v)
2230 6 : return -1;
2231 :
2232 5220 : *value = *v;
2233 5220 : pfree(v);
2234 :
2235 5220 : JsonbInitBinary(baseObject, vars);
2236 5220 : return 1;
2237 : }
2238 :
2239 : /**************** Support functions for JsonPath execution *****************/
2240 :
2241 : /*
2242 : * Returns the size of an array item, or -1 if item is not an array.
2243 : */
2244 : static int
2245 390 : JsonbArraySize(JsonbValue *jb)
2246 : {
2247 : Assert(jb->type != jbvArray);
2248 :
2249 390 : if (jb->type == jbvBinary)
2250 : {
2251 312 : JsonbContainer *jbc = jb->val.binary.data;
2252 :
2253 312 : if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2254 300 : return JsonContainerSize(jbc);
2255 : }
2256 :
2257 90 : return -1;
2258 : }
2259 :
2260 : /* Comparison predicate callback. */
2261 : static JsonPathBool
2262 19320 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2263 : {
2264 19320 : JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2265 :
2266 19320 : return compareItems(cmp->type, lv, rv, cxt->useTz);
2267 : }
2268 :
2269 : /*
2270 : * Perform per-byte comparison of two strings.
2271 : */
2272 : static int
2273 3456 : binaryCompareStrings(const char *s1, int len1,
2274 : const char *s2, int len2)
2275 : {
2276 : int cmp;
2277 :
2278 3456 : cmp = memcmp(s1, s2, Min(len1, len2));
2279 :
2280 3456 : if (cmp != 0)
2281 1968 : return cmp;
2282 :
2283 1488 : if (len1 == len2)
2284 288 : return 0;
2285 :
2286 1200 : return len1 < len2 ? -1 : 1;
2287 : }
2288 :
2289 : /*
2290 : * Compare two strings in the current server encoding using Unicode codepoint
2291 : * collation.
2292 : */
2293 : static int
2294 3456 : compareStrings(const char *mbstr1, int mblen1,
2295 : const char *mbstr2, int mblen2)
2296 : {
2297 6912 : if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2298 3456 : GetDatabaseEncoding() == PG_UTF8)
2299 : {
2300 : /*
2301 : * It's known property of UTF-8 strings that their per-byte comparison
2302 : * result matches codepoints comparison result. ASCII can be
2303 : * considered as special case of UTF-8.
2304 : */
2305 3456 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2306 : }
2307 : else
2308 : {
2309 : char *utf8str1,
2310 : *utf8str2;
2311 : int cmp,
2312 : utf8len1,
2313 : utf8len2;
2314 :
2315 : /*
2316 : * We have to convert other encodings to UTF-8 first, then compare.
2317 : * Input strings may be not null-terminated and pg_server_to_any() may
2318 : * return them "as is". So, use strlen() only if there is real
2319 : * conversion.
2320 : */
2321 0 : utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2322 0 : utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2323 0 : utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2324 0 : utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2325 :
2326 0 : cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2327 :
2328 : /*
2329 : * If pg_server_to_any() did no real conversion, then we actually
2330 : * compared original strings. So, we already done.
2331 : */
2332 0 : if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2333 0 : return cmp;
2334 :
2335 : /* Free memory if needed */
2336 0 : if (mbstr1 != utf8str1)
2337 0 : pfree(utf8str1);
2338 0 : if (mbstr2 != utf8str2)
2339 0 : pfree(utf8str2);
2340 :
2341 : /*
2342 : * When all Unicode codepoints are equal, return result of binary
2343 : * comparison. In some edge cases, same characters may have different
2344 : * representations in encoding. Then our behavior could diverge from
2345 : * standard. However, that allow us to do simple binary comparison
2346 : * for "==" operator, which is performance critical in typical cases.
2347 : * In future to implement strict standard conformance, we can do
2348 : * normalization of input JSON strings.
2349 : */
2350 0 : if (cmp == 0)
2351 0 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2352 : else
2353 0 : return cmp;
2354 : }
2355 : }
2356 :
2357 : /*
2358 : * Compare two SQL/JSON items using comparison operation 'op'.
2359 : */
2360 : static JsonPathBool
2361 19320 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2362 : {
2363 : int cmp;
2364 : bool res;
2365 :
2366 19320 : if (jb1->type != jb2->type)
2367 : {
2368 3132 : if (jb1->type == jbvNull || jb2->type == jbvNull)
2369 :
2370 : /*
2371 : * Equality and order comparison of nulls to non-nulls returns
2372 : * always false, but inequality comparison returns true.
2373 : */
2374 2874 : return op == jpiNotEqual ? jpbTrue : jpbFalse;
2375 :
2376 : /* Non-null items of different types are not comparable. */
2377 258 : return jpbUnknown;
2378 : }
2379 :
2380 16188 : switch (jb1->type)
2381 : {
2382 186 : case jbvNull:
2383 186 : cmp = 0;
2384 186 : break;
2385 870 : case jbvBool:
2386 1266 : cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2387 396 : jb1->val.boolean ? 1 : -1;
2388 870 : break;
2389 3762 : case jbvNumeric:
2390 3762 : cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2391 3762 : break;
2392 9930 : case jbvString:
2393 9930 : if (op == jpiEqual)
2394 6474 : return jb1->val.string.len != jb2->val.string.len ||
2395 3582 : memcmp(jb1->val.string.val,
2396 3582 : jb2->val.string.val,
2397 6474 : jb1->val.string.len) ? jpbFalse : jpbTrue;
2398 :
2399 3456 : cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2400 3456 : jb2->val.string.val, jb2->val.string.len);
2401 3456 : break;
2402 1428 : case jbvDatetime:
2403 : {
2404 : bool cast_error;
2405 :
2406 1428 : cmp = compareDatetime(jb1->val.datetime.value,
2407 : jb1->val.datetime.typid,
2408 : jb2->val.datetime.value,
2409 : jb2->val.datetime.typid,
2410 : useTz,
2411 : &cast_error);
2412 :
2413 1338 : if (cast_error)
2414 252 : return jpbUnknown;
2415 : }
2416 1086 : break;
2417 :
2418 12 : case jbvBinary:
2419 : case jbvArray:
2420 : case jbvObject:
2421 12 : return jpbUnknown; /* non-scalars are not comparable */
2422 :
2423 0 : default:
2424 0 : elog(ERROR, "invalid jsonb value type %d", jb1->type);
2425 : }
2426 :
2427 9360 : switch (op)
2428 : {
2429 1686 : case jpiEqual:
2430 1686 : res = (cmp == 0);
2431 1686 : break;
2432 6 : case jpiNotEqual:
2433 6 : res = (cmp != 0);
2434 6 : break;
2435 2244 : case jpiLess:
2436 2244 : res = (cmp < 0);
2437 2244 : break;
2438 1506 : case jpiGreater:
2439 1506 : res = (cmp > 0);
2440 1506 : break;
2441 1446 : case jpiLessOrEqual:
2442 1446 : res = (cmp <= 0);
2443 1446 : break;
2444 2472 : case jpiGreaterOrEqual:
2445 2472 : res = (cmp >= 0);
2446 2472 : break;
2447 0 : default:
2448 0 : elog(ERROR, "unrecognized jsonpath operation: %d", op);
2449 : return jpbUnknown;
2450 : }
2451 :
2452 9360 : return res ? jpbTrue : jpbFalse;
2453 : }
2454 :
2455 : /* Compare two numerics */
2456 : static int
2457 3762 : compareNumeric(Numeric a, Numeric b)
2458 : {
2459 3762 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2460 : NumericGetDatum(a),
2461 : NumericGetDatum(b)));
2462 : }
2463 :
2464 : static JsonbValue *
2465 719610 : copyJsonbValue(JsonbValue *src)
2466 : {
2467 719610 : JsonbValue *dst = palloc(sizeof(*dst));
2468 :
2469 719610 : *dst = *src;
2470 :
2471 719610 : return dst;
2472 : }
2473 :
2474 : /*
2475 : * Execute array subscript expression and convert resulting numeric item to
2476 : * the integer type with truncation.
2477 : */
2478 : static JsonPathExecResult
2479 360 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2480 : int32 *index)
2481 : {
2482 : JsonbValue *jbv;
2483 360 : JsonValueList found = {0};
2484 360 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2485 : Datum numeric_index;
2486 354 : bool have_error = false;
2487 :
2488 354 : if (jperIsError(res))
2489 0 : return res;
2490 :
2491 696 : if (JsonValueListLength(&found) != 1 ||
2492 342 : !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2493 24 : RETURN_ERROR(ereport(ERROR,
2494 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2495 : errmsg("jsonpath array subscript is not a single numeric value"))));
2496 :
2497 330 : numeric_index = DirectFunctionCall2(numeric_trunc,
2498 : NumericGetDatum(jbv->val.numeric),
2499 : Int32GetDatum(0));
2500 :
2501 330 : *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2502 : &have_error);
2503 :
2504 330 : if (have_error)
2505 24 : RETURN_ERROR(ereport(ERROR,
2506 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2507 : errmsg("jsonpath array subscript is out of integer range"))));
2508 :
2509 306 : return jperOk;
2510 : }
2511 :
2512 : /* Save base object and its id needed for the execution of .keyvalue(). */
2513 : static JsonBaseObjectInfo
2514 817002 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2515 : {
2516 817002 : JsonBaseObjectInfo baseObject = cxt->baseObject;
2517 :
2518 817002 : cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2519 : (JsonbContainer *) jbv->val.binary.data;
2520 817002 : cxt->baseObject.id = id;
2521 :
2522 817002 : return baseObject;
2523 : }
2524 :
2525 : static void
2526 1224 : JsonValueListClear(JsonValueList *jvl)
2527 : {
2528 1224 : jvl->singleton = NULL;
2529 1224 : jvl->list = NULL;
2530 1224 : }
2531 :
2532 : static void
2533 864522 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2534 : {
2535 864522 : if (jvl->singleton)
2536 : {
2537 1866 : jvl->list = list_make2(jvl->singleton, jbv);
2538 1866 : jvl->singleton = NULL;
2539 : }
2540 862656 : else if (!jvl->list)
2541 860814 : jvl->singleton = jbv;
2542 : else
2543 1842 : jvl->list = lappend(jvl->list, jbv);
2544 864522 : }
2545 :
2546 : static int
2547 708708 : JsonValueListLength(const JsonValueList *jvl)
2548 : {
2549 708708 : return jvl->singleton ? 1 : list_length(jvl->list);
2550 : }
2551 :
2552 : static bool
2553 30 : JsonValueListIsEmpty(JsonValueList *jvl)
2554 : {
2555 30 : return !jvl->singleton && list_length(jvl->list) <= 0;
2556 : }
2557 :
2558 : static JsonbValue *
2559 708030 : JsonValueListHead(JsonValueList *jvl)
2560 : {
2561 708030 : return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2562 : }
2563 :
2564 : static List *
2565 1410 : JsonValueListGetList(JsonValueList *jvl)
2566 : {
2567 1410 : if (jvl->singleton)
2568 828 : return list_make1(jvl->singleton);
2569 :
2570 582 : return jvl->list;
2571 : }
2572 :
2573 : static void
2574 197082 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2575 : {
2576 197082 : if (jvl->singleton)
2577 : {
2578 118164 : it->value = jvl->singleton;
2579 118164 : it->list = NIL;
2580 118164 : it->next = NULL;
2581 : }
2582 78918 : else if (jvl->list != NIL)
2583 : {
2584 1536 : it->value = (JsonbValue *) linitial(jvl->list);
2585 1536 : it->list = jvl->list;
2586 1536 : it->next = list_second_cell(jvl->list);
2587 : }
2588 : else
2589 : {
2590 77382 : it->value = NULL;
2591 77382 : it->list = NIL;
2592 77382 : it->next = NULL;
2593 : }
2594 197082 : }
2595 :
2596 : /*
2597 : * Get the next item from the sequence advancing iterator.
2598 : */
2599 : static JsonbValue *
2600 307572 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2601 : {
2602 307572 : JsonbValue *result = it->value;
2603 :
2604 307572 : if (it->next)
2605 : {
2606 2952 : it->value = lfirst(it->next);
2607 2952 : it->next = lnext(it->list, it->next);
2608 : }
2609 : else
2610 : {
2611 304620 : it->value = NULL;
2612 : }
2613 :
2614 307572 : return result;
2615 : }
2616 :
2617 : /*
2618 : * Initialize a binary JsonbValue with the given jsonb container.
2619 : */
2620 : static JsonbValue *
2621 197964 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2622 : {
2623 197964 : jbv->type = jbvBinary;
2624 197964 : jbv->val.binary.data = &jb->root;
2625 197964 : jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2626 :
2627 197964 : return jbv;
2628 : }
2629 :
2630 : /*
2631 : * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
2632 : */
2633 : static int
2634 269850 : JsonbType(JsonbValue *jb)
2635 : {
2636 269850 : int type = jb->type;
2637 :
2638 269850 : if (jb->type == jbvBinary)
2639 : {
2640 182484 : JsonbContainer *jbc = (void *) jb->val.binary.data;
2641 :
2642 : /* Scalars should be always extracted during jsonpath execution. */
2643 : Assert(!JsonContainerIsScalar(jbc));
2644 :
2645 182484 : if (JsonContainerIsObject(jbc))
2646 179298 : type = jbvObject;
2647 3186 : else if (JsonContainerIsArray(jbc))
2648 3186 : type = jbvArray;
2649 : else
2650 0 : elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2651 : }
2652 :
2653 269850 : return type;
2654 : }
2655 :
2656 : /* Get scalar of given type or NULL on type mismatch */
2657 : static JsonbValue *
2658 5202 : getScalar(JsonbValue *scalar, enum jbvType type)
2659 : {
2660 : /* Scalars should be always extracted during jsonpath execution. */
2661 : Assert(scalar->type != jbvBinary ||
2662 : !JsonContainerIsScalar(scalar->val.binary.data));
2663 :
2664 5202 : return scalar->type == type ? scalar : NULL;
2665 : }
2666 :
2667 : /* Construct a JSON array from the item list */
2668 : static JsonbValue *
2669 480 : wrapItemsInArray(const JsonValueList *items)
2670 : {
2671 480 : JsonbParseState *ps = NULL;
2672 : JsonValueListIterator it;
2673 : JsonbValue *jbv;
2674 :
2675 480 : pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2676 :
2677 480 : JsonValueListInitIterator(items, &it);
2678 1170 : while ((jbv = JsonValueListNext(items, &it)))
2679 690 : pushJsonbValue(&ps, WJB_ELEM, jbv);
2680 :
2681 480 : return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2682 : }
2683 :
2684 : /* Check if the timezone required for casting from type1 to type2 is used */
2685 : static void
2686 378 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
2687 : {
2688 378 : if (!useTz)
2689 90 : ereport(ERROR,
2690 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2691 : errmsg("cannot convert value from %s to %s without time zone usage",
2692 : type1, type2),
2693 : errhint("Use *_tz() function for time zone support.")));
2694 288 : }
2695 :
2696 : /* Convert time datum to timetz datum */
2697 : static Datum
2698 144 : castTimeToTimeTz(Datum time, bool useTz)
2699 : {
2700 144 : checkTimezoneIsUsedForCast(useTz, "time", "timetz");
2701 :
2702 108 : return DirectFunctionCall1(time_timetz, time);
2703 : }
2704 :
2705 : /*
2706 : * Compare date to timestamp.
2707 : * Note that this doesn't involve any timezone considerations.
2708 : */
2709 : static int
2710 114 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
2711 : {
2712 114 : return date_cmp_timestamp_internal(date1, ts2);
2713 : }
2714 :
2715 : /*
2716 : * Compare date to timestamptz.
2717 : */
2718 : static int
2719 90 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
2720 : {
2721 90 : checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
2722 :
2723 72 : return date_cmp_timestamptz_internal(date1, tstz2);
2724 : }
2725 :
2726 : /*
2727 : * Compare timestamp to timestamptz.
2728 : */
2729 : static int
2730 144 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
2731 : {
2732 144 : checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
2733 :
2734 108 : return timestamp_cmp_timestamptz_internal(ts1, tstz2);
2735 : }
2736 :
2737 : /*
2738 : * Cross-type comparison of two datetime SQL/JSON items. If items are
2739 : * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
2740 : * If the cast requires timezone and it is not used, then explicit error is thrown.
2741 : */
2742 : static int
2743 1428 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2744 : bool useTz, bool *cast_error)
2745 : {
2746 : PGFunction cmpfunc;
2747 :
2748 1428 : *cast_error = false;
2749 :
2750 1428 : switch (typid1)
2751 : {
2752 222 : case DATEOID:
2753 : switch (typid2)
2754 : {
2755 108 : case DATEOID:
2756 108 : cmpfunc = date_cmp;
2757 :
2758 108 : break;
2759 :
2760 42 : case TIMESTAMPOID:
2761 42 : return cmpDateToTimestamp(DatumGetDateADT(val1),
2762 : DatumGetTimestamp(val2),
2763 : useTz);
2764 :
2765 36 : case TIMESTAMPTZOID:
2766 36 : return cmpDateToTimestampTz(DatumGetDateADT(val1),
2767 : DatumGetTimestampTz(val2),
2768 : useTz);
2769 :
2770 36 : case TIMEOID:
2771 : case TIMETZOID:
2772 36 : *cast_error = true; /* uncomparable types */
2773 36 : return 0;
2774 :
2775 0 : default:
2776 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2777 : typid2);
2778 : }
2779 108 : break;
2780 :
2781 252 : case TIMEOID:
2782 : switch (typid2)
2783 : {
2784 108 : case TIMEOID:
2785 108 : cmpfunc = time_cmp;
2786 :
2787 108 : break;
2788 :
2789 72 : case TIMETZOID:
2790 72 : val1 = castTimeToTimeTz(val1, useTz);
2791 54 : cmpfunc = timetz_cmp;
2792 :
2793 54 : break;
2794 :
2795 72 : case DATEOID:
2796 : case TIMESTAMPOID:
2797 : case TIMESTAMPTZOID:
2798 72 : *cast_error = true; /* uncomparable types */
2799 72 : return 0;
2800 :
2801 0 : default:
2802 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2803 : typid2);
2804 : }
2805 162 : break;
2806 :
2807 324 : case TIMETZOID:
2808 : switch (typid2)
2809 : {
2810 72 : case TIMEOID:
2811 72 : val2 = castTimeToTimeTz(val2, useTz);
2812 54 : cmpfunc = timetz_cmp;
2813 :
2814 54 : break;
2815 :
2816 180 : case TIMETZOID:
2817 180 : cmpfunc = timetz_cmp;
2818 :
2819 180 : break;
2820 :
2821 72 : case DATEOID:
2822 : case TIMESTAMPOID:
2823 : case TIMESTAMPTZOID:
2824 72 : *cast_error = true; /* uncomparable types */
2825 72 : return 0;
2826 :
2827 0 : default:
2828 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2829 : typid2);
2830 : }
2831 234 : break;
2832 :
2833 288 : case TIMESTAMPOID:
2834 : switch (typid2)
2835 : {
2836 72 : case DATEOID:
2837 72 : return -cmpDateToTimestamp(DatumGetDateADT(val2),
2838 : DatumGetTimestamp(val1),
2839 : useTz);
2840 :
2841 108 : case TIMESTAMPOID:
2842 108 : cmpfunc = timestamp_cmp;
2843 :
2844 108 : break;
2845 :
2846 72 : case TIMESTAMPTZOID:
2847 72 : return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
2848 : DatumGetTimestampTz(val2),
2849 : useTz);
2850 :
2851 36 : case TIMEOID:
2852 : case TIMETZOID:
2853 36 : *cast_error = true; /* uncomparable types */
2854 36 : return 0;
2855 :
2856 0 : default:
2857 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2858 : typid2);
2859 : }
2860 108 : break;
2861 :
2862 342 : case TIMESTAMPTZOID:
2863 : switch (typid2)
2864 : {
2865 54 : case DATEOID:
2866 54 : return -cmpDateToTimestampTz(DatumGetDateADT(val2),
2867 : DatumGetTimestampTz(val1),
2868 : useTz);
2869 :
2870 72 : case TIMESTAMPOID:
2871 72 : return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
2872 : DatumGetTimestampTz(val1),
2873 : useTz);
2874 :
2875 180 : case TIMESTAMPTZOID:
2876 180 : cmpfunc = timestamp_cmp;
2877 :
2878 180 : break;
2879 :
2880 36 : case TIMEOID:
2881 : case TIMETZOID:
2882 36 : *cast_error = true; /* uncomparable types */
2883 36 : return 0;
2884 :
2885 0 : default:
2886 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2887 : typid2);
2888 : }
2889 180 : break;
2890 :
2891 0 : default:
2892 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
2893 : }
2894 :
2895 792 : if (*cast_error)
2896 0 : return 0; /* cast error */
2897 :
2898 792 : return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
2899 : }
2900 :
2901 : /********************Interface to pgsql's executor***************************/
2902 :
2903 : bool
2904 546 : JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool *error)
2905 : {
2906 546 : JsonPathExecResult res = executeJsonPath(jp, vars, EvalJsonPathVar,
2907 546 : DatumGetJsonbP(jb), !error, NULL,
2908 : true);
2909 :
2910 : Assert(error || !jperIsError(res));
2911 :
2912 540 : if (error && jperIsError(res))
2913 162 : *error = true;
2914 :
2915 540 : return res == jperOk;
2916 : }
2917 :
2918 : Datum
2919 2508 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
2920 : bool *error, List *vars)
2921 : {
2922 : JsonbValue *first;
2923 : bool wrap;
2924 2508 : JsonValueList found = {0};
2925 : JsonPathExecResult res PG_USED_FOR_ASSERTS_ONLY;
2926 : int count;
2927 :
2928 2508 : res = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
2929 : &found, true);
2930 :
2931 : Assert(error || !jperIsError(res));
2932 :
2933 2502 : if (error && jperIsError(res))
2934 : {
2935 30 : *error = true;
2936 30 : *empty = false;
2937 30 : return (Datum) 0;
2938 : }
2939 :
2940 2472 : count = JsonValueListLength(&found);
2941 :
2942 2472 : first = count ? JsonValueListHead(&found) : NULL;
2943 :
2944 2472 : if (!first)
2945 174 : wrap = false;
2946 2298 : else if (wrapper == JSW_NONE)
2947 1836 : wrap = false;
2948 462 : else if (wrapper == JSW_UNCONDITIONAL)
2949 288 : wrap = true;
2950 174 : else if (wrapper == JSW_CONDITIONAL)
2951 174 : wrap = count > 1 ||
2952 198 : IsAJsonbScalar(first) ||
2953 24 : (first->type == jbvBinary &&
2954 24 : JsonContainerIsScalar(first->val.binary.data));
2955 : else
2956 : {
2957 0 : elog(ERROR, "unrecognized json wrapper %d", wrapper);
2958 : wrap = false;
2959 : }
2960 :
2961 2472 : if (wrap)
2962 438 : return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
2963 :
2964 2034 : if (count > 1)
2965 : {
2966 48 : if (error)
2967 : {
2968 42 : *error = true;
2969 42 : return (Datum) 0;
2970 : }
2971 :
2972 6 : ereport(ERROR,
2973 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
2974 : errmsg("JSON path expression in JSON_QUERY should return "
2975 : "singleton item without wrapper"),
2976 : errhint("use WITH WRAPPER clause to wrap SQL/JSON item "
2977 : "sequence into array")));
2978 : }
2979 :
2980 1986 : if (first)
2981 1812 : return JsonbPGetDatum(JsonbValueToJsonb(first));
2982 :
2983 174 : *empty = true;
2984 174 : return PointerGetDatum(NULL);
2985 : }
2986 :
2987 : JsonbValue *
2988 603066 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars)
2989 : {
2990 : JsonbValue *res;
2991 603066 : JsonValueList found = {0};
2992 : JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
2993 : int count;
2994 :
2995 603066 : jper = executeJsonPath(jp, vars, EvalJsonPathVar, DatumGetJsonbP(jb), !error,
2996 : &found, true);
2997 :
2998 : Assert(error || !jperIsError(jper));
2999 :
3000 603054 : if (error && jperIsError(jper))
3001 : {
3002 18 : *error = true;
3003 18 : *empty = false;
3004 18 : return NULL;
3005 : }
3006 :
3007 603036 : count = JsonValueListLength(&found);
3008 :
3009 603036 : *empty = !count;
3010 :
3011 603036 : if (*empty)
3012 336 : return NULL;
3013 :
3014 602700 : if (count > 1)
3015 : {
3016 18 : if (error)
3017 : {
3018 12 : *error = true;
3019 12 : return NULL;
3020 : }
3021 :
3022 6 : ereport(ERROR,
3023 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3024 : errmsg("JSON path expression in JSON_VALUE should return "
3025 : "singleton scalar item")));
3026 : }
3027 :
3028 602682 : res = JsonValueListHead(&found);
3029 :
3030 602682 : if (res->type == jbvBinary &&
3031 96 : JsonContainerIsScalar(res->val.binary.data))
3032 0 : JsonbExtractScalar(res->val.binary.data, res);
3033 :
3034 602682 : if (!IsAJsonbScalar(res))
3035 : {
3036 96 : if (error)
3037 : {
3038 84 : *error = true;
3039 84 : return NULL;
3040 : }
3041 :
3042 12 : ereport(ERROR,
3043 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
3044 : errmsg("JSON path expression in JSON_VALUE should return "
3045 : "singleton scalar item")));
3046 : }
3047 :
3048 602586 : if (res->type == jbvNull)
3049 156 : return NULL;
3050 :
3051 602430 : return res;
3052 : }
3053 :
3054 : static void
3055 2166 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
3056 : {
3057 2166 : jbv->type = jbvNumeric;
3058 2166 : jbv->val.numeric = DatumGetNumeric(num);
3059 2166 : }
3060 :
3061 : void
3062 2238 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
3063 : {
3064 2238 : switch (typid)
3065 : {
3066 0 : case BOOLOID:
3067 0 : res->type = jbvBool;
3068 0 : res->val.boolean = DatumGetBool(val);
3069 0 : break;
3070 0 : case NUMERICOID:
3071 0 : JsonbValueInitNumericDatum(res, val);
3072 0 : break;
3073 0 : case INT2OID:
3074 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
3075 0 : break;
3076 2166 : case INT4OID:
3077 2166 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
3078 2166 : break;
3079 0 : case INT8OID:
3080 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
3081 0 : break;
3082 0 : case FLOAT4OID:
3083 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
3084 0 : break;
3085 0 : case FLOAT8OID:
3086 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
3087 0 : break;
3088 12 : case TEXTOID:
3089 : case VARCHAROID:
3090 12 : res->type = jbvString;
3091 12 : res->val.string.val = VARDATA_ANY(val);
3092 12 : res->val.string.len = VARSIZE_ANY_EXHDR(val);
3093 12 : break;
3094 48 : case DATEOID:
3095 : case TIMEOID:
3096 : case TIMETZOID:
3097 : case TIMESTAMPOID:
3098 : case TIMESTAMPTZOID:
3099 48 : res->type = jbvDatetime;
3100 48 : res->val.datetime.value = val;
3101 48 : res->val.datetime.typid = typid;
3102 48 : res->val.datetime.typmod = typmod;
3103 48 : res->val.datetime.tz = 0;
3104 48 : break;
3105 12 : case JSONBOID:
3106 : {
3107 12 : JsonbValue *jbv = res;
3108 12 : Jsonb *jb = DatumGetJsonbP(val);
3109 :
3110 12 : if (JsonContainerIsScalar(&jb->root))
3111 : {
3112 : bool res PG_USED_FOR_ASSERTS_ONLY;
3113 :
3114 12 : res = JsonbExtractScalar(&jb->root, jbv);
3115 : Assert(res);
3116 : }
3117 : else
3118 0 : JsonbInitBinary(jbv, jb);
3119 12 : break;
3120 : }
3121 0 : case JSONOID:
3122 : {
3123 0 : text *txt = DatumGetTextP(val);
3124 0 : char *str = text_to_cstring(txt);
3125 : Jsonb *jb =
3126 0 : DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
3127 : CStringGetDatum(str)));
3128 :
3129 0 : pfree(str);
3130 :
3131 0 : JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
3132 0 : break;
3133 : }
3134 0 : default:
3135 0 : ereport(ERROR,
3136 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3137 : errmsg("only bool, numeric, and text types could be "
3138 : "casted to supported jsonpath types.")));
3139 : }
3140 2238 : }
3141 :
3142 : /************************ JSON_TABLE functions ***************************/
3143 :
3144 : /*
3145 : * Returns private data from executor state. Ensure validity by check with
3146 : * MAGIC number.
3147 : */
3148 : static inline JsonTableContext *
3149 8106 : GetJsonTableContext(TableFuncScanState *state, const char *fname)
3150 : {
3151 : JsonTableContext *result;
3152 :
3153 8106 : if (!IsA(state, TableFuncScanState))
3154 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
3155 8106 : result = (JsonTableContext *) state->opaque;
3156 8106 : if (result->magic != JSON_TABLE_CONTEXT_MAGIC)
3157 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
3158 :
3159 8106 : return result;
3160 : }
3161 :
3162 : /* Recursively initialize JSON_TABLE scan state */
3163 : static void
3164 516 : JsonTableInitScanState(JsonTableContext *cxt, JsonTableScanState *scan,
3165 : JsonTableParent *node, JsonTableScanState *parent,
3166 : List *args, MemoryContext mcxt)
3167 : {
3168 : int i;
3169 :
3170 516 : scan->parent = parent;
3171 516 : scan->outerJoin = node->outerJoin;
3172 516 : scan->errorOnError = node->errorOnError;
3173 516 : scan->path = DatumGetJsonPathP(node->path->constvalue);
3174 516 : scan->args = args;
3175 516 : scan->mcxt = AllocSetContextCreate(mcxt, "JsonTableContext",
3176 : ALLOCSET_DEFAULT_SIZES);
3177 1032 : scan->nested = node->child ?
3178 516 : JsonTableInitPlanState(cxt, node->child, scan) : NULL;
3179 516 : scan->current = PointerGetDatum(NULL);
3180 516 : scan->currentIsNull = true;
3181 :
3182 1710 : for (i = node->colMin; i <= node->colMax; i++)
3183 1194 : cxt->colexprs[i].scan = scan;
3184 516 : }
3185 :
3186 : /* Recursively initialize JSON_TABLE scan state */
3187 : static JsonTableJoinState *
3188 324 : JsonTableInitPlanState(JsonTableContext *cxt, Node *plan,
3189 : JsonTableScanState *parent)
3190 : {
3191 324 : JsonTableJoinState *state = palloc0(sizeof(*state));
3192 :
3193 324 : if (IsA(plan, JsonTableSibling))
3194 : {
3195 78 : JsonTableSibling *join = castNode(JsonTableSibling, plan);
3196 :
3197 78 : state->is_join = true;
3198 78 : state->u.join.cross = join->cross;
3199 78 : state->u.join.left = JsonTableInitPlanState(cxt, join->larg, parent);
3200 78 : state->u.join.right = JsonTableInitPlanState(cxt, join->rarg, parent);
3201 : }
3202 : else
3203 : {
3204 246 : JsonTableParent *node = castNode(JsonTableParent, plan);
3205 :
3206 246 : state->is_join = false;
3207 :
3208 246 : JsonTableInitScanState(cxt, &state->u.scan, node, parent,
3209 : parent->args, parent->mcxt);
3210 : }
3211 :
3212 324 : return state;
3213 : }
3214 :
3215 : /*
3216 : * JsonTableInitOpaque
3217 : * Fill in TableFuncScanState->opaque for JsonTable processor
3218 : */
3219 : static void
3220 270 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
3221 : {
3222 : JsonTableContext *cxt;
3223 270 : PlanState *ps = &state->ss.ps;
3224 270 : TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
3225 270 : TableFunc *tf = tfs->tablefunc;
3226 270 : JsonExpr *ci = castNode(JsonExpr, tf->docexpr);
3227 270 : JsonTableParent *root = castNode(JsonTableParent, tf->plan);
3228 270 : List *args = NIL;
3229 : ListCell *lc;
3230 : int i;
3231 :
3232 270 : cxt = palloc0(sizeof(JsonTableContext));
3233 270 : cxt->magic = JSON_TABLE_CONTEXT_MAGIC;
3234 :
3235 270 : if (ci->passing_values)
3236 : {
3237 : ListCell *exprlc;
3238 : ListCell *namelc;
3239 :
3240 228 : forboth(exprlc, ci->passing_values,
3241 : namelc, ci->passing_names)
3242 : {
3243 150 : Expr *expr = (Expr *) lfirst(exprlc);
3244 150 : String *name = lfirst_node(String, namelc);
3245 150 : JsonPathVariableEvalContext *var = palloc(sizeof(*var));
3246 :
3247 150 : var->name = pstrdup(name->sval);
3248 150 : var->typid = exprType((Node *) expr);
3249 150 : var->typmod = exprTypmod((Node *) expr);
3250 150 : var->estate = ExecInitExpr(expr, ps);
3251 150 : var->econtext = ps->ps_ExprContext;
3252 150 : var->mcxt = CurrentMemoryContext;
3253 150 : var->evaluated = false;
3254 150 : var->value = (Datum) 0;
3255 150 : var->isnull = true;
3256 :
3257 150 : args = lappend(args, var);
3258 : }
3259 : }
3260 :
3261 540 : cxt->colexprs = palloc(sizeof(*cxt->colexprs) *
3262 270 : list_length(tf->colvalexprs));
3263 :
3264 270 : JsonTableInitScanState(cxt, &cxt->root, root, NULL, args,
3265 : CurrentMemoryContext);
3266 :
3267 270 : i = 0;
3268 :
3269 1464 : foreach(lc, tf->colvalexprs)
3270 : {
3271 1194 : Expr *expr = lfirst(lc);
3272 :
3273 2388 : cxt->colexprs[i].expr =
3274 1194 : ExecInitExprWithCaseValue(expr, ps,
3275 1194 : &cxt->colexprs[i].scan->current,
3276 1194 : &cxt->colexprs[i].scan->currentIsNull);
3277 :
3278 1194 : i++;
3279 : }
3280 :
3281 270 : state->opaque = cxt;
3282 270 : }
3283 :
3284 : /* Reset scan iterator to the beginning of the item list */
3285 : static void
3286 1254 : JsonTableRescan(JsonTableScanState *scan)
3287 : {
3288 1254 : JsonValueListInitIterator(&scan->found, &scan->iter);
3289 1254 : scan->current = PointerGetDatum(NULL);
3290 1254 : scan->currentIsNull = true;
3291 1254 : scan->advanceNested = false;
3292 1254 : scan->ordinal = 0;
3293 1254 : }
3294 :
3295 : /* Reset context item of a scan, execute JSON path and reset a scan */
3296 : static void
3297 1014 : JsonTableResetContextItem(JsonTableScanState *scan, Datum item)
3298 : {
3299 : MemoryContext oldcxt;
3300 : JsonPathExecResult res;
3301 1014 : Jsonb *js = (Jsonb *) DatumGetJsonbP(item);
3302 :
3303 1014 : JsonValueListClear(&scan->found);
3304 :
3305 1014 : MemoryContextResetOnly(scan->mcxt);
3306 :
3307 1014 : oldcxt = MemoryContextSwitchTo(scan->mcxt);
3308 :
3309 1014 : res = executeJsonPath(scan->path, scan->args, EvalJsonPathVar, js,
3310 1014 : scan->errorOnError, &scan->found, false /* FIXME */ );
3311 :
3312 1014 : MemoryContextSwitchTo(oldcxt);
3313 :
3314 1014 : if (jperIsError(res))
3315 : {
3316 : Assert(!scan->errorOnError);
3317 210 : JsonValueListClear(&scan->found); /* EMPTY ON ERROR case */
3318 : }
3319 :
3320 1014 : JsonTableRescan(scan);
3321 1014 : }
3322 :
3323 : /*
3324 : * JsonTableSetDocument
3325 : * Install the input document
3326 : */
3327 : static void
3328 264 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
3329 : {
3330 264 : JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableSetDocument");
3331 :
3332 264 : JsonTableResetContextItem(&cxt->root, value);
3333 264 : }
3334 :
3335 : /* Recursively reset scan and its child nodes */
3336 : static void
3337 240 : JsonTableRescanRecursive(JsonTableJoinState *state)
3338 : {
3339 240 : if (state->is_join)
3340 : {
3341 0 : JsonTableRescanRecursive(state->u.join.left);
3342 0 : JsonTableRescanRecursive(state->u.join.right);
3343 0 : state->u.join.advanceRight = false;
3344 : }
3345 : else
3346 : {
3347 240 : JsonTableRescan(&state->u.scan);
3348 240 : if (state->u.scan.nested)
3349 36 : JsonTableRescanRecursive(state->u.scan.nested);
3350 : }
3351 240 : }
3352 :
3353 : /*
3354 : * Fetch next row from a cross/union joined scan.
3355 : *
3356 : * Returns false at the end of a scan, true otherwise.
3357 : */
3358 : static bool
3359 3054 : JsonTableNextJoinRow(JsonTableJoinState *state)
3360 : {
3361 3054 : if (!state->is_join)
3362 2112 : return JsonTableNextRow(&state->u.scan);
3363 :
3364 942 : if (state->u.join.advanceRight)
3365 : {
3366 : /* fetch next inner row */
3367 456 : if (JsonTableNextJoinRow(state->u.join.right))
3368 300 : return true;
3369 :
3370 : /* inner rows are exhausted */
3371 156 : if (state->u.join.cross)
3372 108 : state->u.join.advanceRight = false; /* next outer row */
3373 : else
3374 48 : return false; /* end of scan */
3375 : }
3376 :
3377 690 : while (!state->u.join.advanceRight)
3378 : {
3379 : /* fetch next outer row */
3380 690 : bool left = JsonTableNextJoinRow(state->u.join.left);
3381 :
3382 690 : if (state->u.join.cross)
3383 : {
3384 318 : if (!left)
3385 114 : return false; /* end of scan */
3386 :
3387 204 : JsonTableRescanRecursive(state->u.join.right);
3388 :
3389 204 : if (!JsonTableNextJoinRow(state->u.join.right))
3390 96 : continue; /* next outer row */
3391 :
3392 108 : state->u.join.advanceRight = true; /* next inner row */
3393 : }
3394 372 : else if (!left)
3395 : {
3396 144 : if (!JsonTableNextJoinRow(state->u.join.right))
3397 96 : return false; /* end of scan */
3398 :
3399 48 : state->u.join.advanceRight = true; /* next inner row */
3400 : }
3401 :
3402 384 : break;
3403 : }
3404 :
3405 384 : return true;
3406 : }
3407 :
3408 : /* Recursively set 'reset' flag of scan and its child nodes */
3409 : static void
3410 1080 : JsonTableJoinReset(JsonTableJoinState *state)
3411 : {
3412 1080 : if (state->is_join)
3413 : {
3414 258 : JsonTableJoinReset(state->u.join.left);
3415 258 : JsonTableJoinReset(state->u.join.right);
3416 258 : state->u.join.advanceRight = false;
3417 : }
3418 : else
3419 : {
3420 822 : state->u.scan.reset = true;
3421 822 : state->u.scan.advanceNested = false;
3422 :
3423 822 : if (state->u.scan.nested)
3424 36 : JsonTableJoinReset(state->u.scan.nested);
3425 : }
3426 1080 : }
3427 :
3428 : /*
3429 : * Fetch next row from a simple scan with outer/inner joined nested subscans.
3430 : *
3431 : * Returns false at the end of a scan, true otherwise.
3432 : */
3433 : static bool
3434 3588 : JsonTableNextRow(JsonTableScanState *scan)
3435 : {
3436 : /* reset context item if requested */
3437 3588 : if (scan->reset)
3438 : {
3439 : Assert(!scan->parent->currentIsNull);
3440 750 : JsonTableResetContextItem(scan, scan->parent->current);
3441 750 : scan->reset = false;
3442 : }
3443 :
3444 3588 : if (scan->advanceNested)
3445 : {
3446 : /* fetch next nested row */
3447 1032 : scan->advanceNested = JsonTableNextJoinRow(scan->nested);
3448 :
3449 1032 : if (scan->advanceNested)
3450 768 : return true;
3451 : }
3452 :
3453 : for (;;)
3454 84 : {
3455 : /* fetch next row */
3456 2904 : JsonbValue *jbv = JsonValueListNext(&scan->found, &scan->iter);
3457 : MemoryContext oldcxt;
3458 :
3459 2904 : if (!jbv)
3460 : {
3461 1110 : scan->current = PointerGetDatum(NULL);
3462 1110 : scan->currentIsNull = true;
3463 1110 : return false; /* end of scan */
3464 : }
3465 :
3466 : /* set current row item */
3467 1794 : oldcxt = MemoryContextSwitchTo(scan->mcxt);
3468 1794 : scan->current = JsonbPGetDatum(JsonbValueToJsonb(jbv));
3469 1794 : scan->currentIsNull = false;
3470 1794 : MemoryContextSwitchTo(oldcxt);
3471 :
3472 1794 : scan->ordinal++;
3473 :
3474 1794 : if (!scan->nested)
3475 1266 : break;
3476 :
3477 528 : JsonTableJoinReset(scan->nested);
3478 :
3479 528 : scan->advanceNested = JsonTableNextJoinRow(scan->nested);
3480 :
3481 528 : if (scan->advanceNested || scan->outerJoin)
3482 : break;
3483 : }
3484 :
3485 1710 : return true;
3486 : }
3487 :
3488 : /*
3489 : * JsonTableFetchRow
3490 : * Prepare the next "current" tuple for upcoming GetValue calls.
3491 : * Returns FALSE if the row-filter expression returned no more rows.
3492 : */
3493 : static bool
3494 1476 : JsonTableFetchRow(TableFuncScanState *state)
3495 : {
3496 1476 : JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableFetchRow");
3497 :
3498 1476 : if (cxt->empty)
3499 0 : return false;
3500 :
3501 1476 : return JsonTableNextRow(&cxt->root);
3502 : }
3503 :
3504 : /*
3505 : * JsonTableGetValue
3506 : * Return the value for column number 'colnum' for the current row.
3507 : *
3508 : * This leaks memory, so be sure to reset often the context in which it's
3509 : * called.
3510 : */
3511 : static Datum
3512 6096 : JsonTableGetValue(TableFuncScanState *state, int colnum,
3513 : Oid typid, int32 typmod, bool *isnull)
3514 : {
3515 6096 : JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableGetValue");
3516 6096 : ExprContext *econtext = state->ss.ps.ps_ExprContext;
3517 6096 : ExprState *estate = cxt->colexprs[colnum].expr;
3518 6096 : JsonTableScanState *scan = cxt->colexprs[colnum].scan;
3519 : Datum result;
3520 :
3521 6096 : if (scan->currentIsNull) /* NULL from outer/union join */
3522 : {
3523 636 : result = (Datum) 0;
3524 636 : *isnull = true;
3525 : }
3526 5460 : else if (estate) /* regular column */
3527 : {
3528 4548 : result = ExecEvalExpr(estate, econtext, isnull);
3529 : }
3530 : else
3531 : {
3532 912 : result = Int32GetDatum(scan->ordinal); /* ordinality column */
3533 912 : *isnull = false;
3534 : }
3535 :
3536 6066 : return result;
3537 : }
3538 :
3539 : /*
3540 : * JsonTableDestroyOpaque
3541 : */
3542 : static void
3543 270 : JsonTableDestroyOpaque(TableFuncScanState *state)
3544 : {
3545 270 : JsonTableContext *cxt = GetJsonTableContext(state, "JsonTableDestroyOpaque");
3546 :
3547 : /* not valid anymore */
3548 270 : cxt->magic = 0;
3549 :
3550 270 : state->opaque = NULL;
3551 270 : }
3552 :
3553 : const TableFuncRoutine JsonbTableRoutine =
3554 : {
3555 : JsonTableInitOpaque,
3556 : JsonTableSetDocument,
3557 : NULL,
3558 : NULL,
3559 : NULL,
3560 : JsonTableFetchRow,
3561 : JsonTableGetValue,
3562 : JsonTableDestroyOpaque
3563 : };
|