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-2026, 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 "funcapi.h"
65 : #include "miscadmin.h"
66 : #include "nodes/miscnodes.h"
67 : #include "nodes/nodeFuncs.h"
68 : #include "regex/regex.h"
69 : #include "utils/builtins.h"
70 : #include "utils/date.h"
71 : #include "utils/datetime.h"
72 : #include "utils/float.h"
73 : #include "utils/formatting.h"
74 : #include "utils/json.h"
75 : #include "utils/jsonpath.h"
76 : #include "utils/memutils.h"
77 : #include "utils/timestamp.h"
78 :
79 : /*
80 : * Represents "base object" and its "id" for .keyvalue() evaluation.
81 : */
82 : typedef struct JsonBaseObjectInfo
83 : {
84 : JsonbContainer *jbc;
85 : int id;
86 : } JsonBaseObjectInfo;
87 :
88 : /* Callbacks for executeJsonPath() */
89 : typedef JsonbValue *(*JsonPathGetVarCallback) (void *vars, char *varName, int varNameLen,
90 : JsonbValue *baseObject, int *baseObjectId);
91 : typedef int (*JsonPathCountVarsCallback) (void *vars);
92 :
93 : /*
94 : * Context of jsonpath execution.
95 : */
96 : typedef struct JsonPathExecContext
97 : {
98 : void *vars; /* variables to substitute into jsonpath */
99 : JsonPathGetVarCallback getVar; /* callback to extract a given variable
100 : * from 'vars' */
101 : JsonbValue *root; /* for $ evaluation */
102 : JsonbValue *current; /* for @ evaluation */
103 : JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
104 : * evaluation */
105 : int lastGeneratedObjectId; /* "id" counter for .keyvalue()
106 : * evaluation */
107 : int innermostArraySize; /* for LAST array index evaluation */
108 : bool laxMode; /* true for "lax" mode, false for "strict"
109 : * mode */
110 : bool ignoreStructuralErrors; /* with "true" structural errors such
111 : * as absence of required json item or
112 : * unexpected json item type are
113 : * ignored */
114 : bool throwErrors; /* with "false" all suppressible errors are
115 : * suppressed */
116 : bool useTz;
117 : } JsonPathExecContext;
118 :
119 : /* Context for LIKE_REGEX execution. */
120 : typedef struct JsonLikeRegexContext
121 : {
122 : text *regex;
123 : int cflags;
124 : } JsonLikeRegexContext;
125 :
126 : /* Result of jsonpath predicate evaluation */
127 : typedef enum JsonPathBool
128 : {
129 : jpbFalse = 0,
130 : jpbTrue = 1,
131 : jpbUnknown = 2
132 : } JsonPathBool;
133 :
134 : /* Result of jsonpath expression evaluation */
135 : typedef enum JsonPathExecResult
136 : {
137 : jperOk = 0,
138 : jperNotFound = 1,
139 : jperError = 2
140 : } JsonPathExecResult;
141 :
142 : #define jperIsError(jper) ((jper) == jperError)
143 :
144 : /*
145 : * List (or really array) of JsonbValues. This is the output representation
146 : * of jsonpath evaluation.
147 : *
148 : * The initial or "base" chunk of a list is typically a local variable in
149 : * a calling function. If we need more entries than will fit in the base
150 : * chunk, we palloc more chunks. For notational simplicity, those are also
151 : * treated as being of type JsonValueList, although they will have items[]
152 : * arrays that are larger than BASE_JVL_ITEMS.
153 : *
154 : * Callers *must* initialize the base chunk with JsonValueListInit().
155 : * Typically they should free any extra chunks when done, using
156 : * JsonValueListClear(), although some top-level functions skip that
157 : * on the assumption that the caller's context will be reset soon.
158 : *
159 : * Note that most types of JsonbValue include pointers to external data, which
160 : * will not be managed by the JsonValueList functions. We expect that such
161 : * data is part of the input to the jsonpath operation, and the caller will
162 : * see to it that it holds still for the duration of the operation.
163 : *
164 : * Most lists are short, though some can be quite long. So we set
165 : * BASE_JVL_ITEMS small to conserve stack space, but grow the extra
166 : * chunks aggressively.
167 : */
168 : #define BASE_JVL_ITEMS 2 /* number of items a base chunk holds */
169 : #define MIN_EXTRA_JVL_ITEMS 16 /* min number of items an extra chunk holds */
170 :
171 : typedef struct JsonValueList
172 : {
173 : int nitems; /* number of items stored in this chunk */
174 : int maxitems; /* allocated length of items[] */
175 : struct JsonValueList *next; /* => next chunk, if any */
176 : struct JsonValueList *last; /* => last chunk (only valid in base chunk) */
177 : JsonbValue items[BASE_JVL_ITEMS];
178 : } JsonValueList;
179 :
180 : /* State data for iterating through a JsonValueList */
181 : typedef struct JsonValueListIterator
182 : {
183 : JsonValueList *chunk; /* current chunk of list */
184 : int nextitem; /* index of next value to return in chunk */
185 : } JsonValueListIterator;
186 :
187 : /* Structures for JSON_TABLE execution */
188 :
189 : /*
190 : * Struct holding the result of jsonpath evaluation, to be used as source row
191 : * for JsonTableGetValue() which in turn computes the values of individual
192 : * JSON_TABLE columns.
193 : */
194 : typedef struct JsonTablePlanRowSource
195 : {
196 : Datum value;
197 : bool isnull;
198 : } JsonTablePlanRowSource;
199 :
200 : /*
201 : * State of evaluation of row pattern derived by applying jsonpath given in
202 : * a JsonTablePlan to an input document given in the parent TableFunc.
203 : */
204 : typedef struct JsonTablePlanState
205 : {
206 : /* Original plan */
207 : JsonTablePlan *plan;
208 :
209 : /* The following fields are only valid for JsonTablePathScan plans */
210 :
211 : /* jsonpath to evaluate against the input doc to get the row pattern */
212 : JsonPath *path;
213 :
214 : /*
215 : * Memory context to use when evaluating the row pattern from the jsonpath
216 : */
217 : MemoryContext mcxt;
218 :
219 : /* PASSING arguments passed to jsonpath executor */
220 : List *args;
221 :
222 : /* List and iterator of jsonpath result values */
223 : JsonValueList found;
224 : JsonValueListIterator iter;
225 :
226 : /* Currently selected row for JsonTableGetValue() to use */
227 : JsonTablePlanRowSource current;
228 :
229 : /* Counter for ORDINAL columns */
230 : int ordinal;
231 :
232 : /* Nested plan, if any */
233 : struct JsonTablePlanState *nested;
234 :
235 : /* Left sibling, if any */
236 : struct JsonTablePlanState *left;
237 :
238 : /* Right sibling, if any */
239 : struct JsonTablePlanState *right;
240 :
241 : /* Parent plan, if this is a nested plan */
242 : struct JsonTablePlanState *parent;
243 : } JsonTablePlanState;
244 :
245 : /* Random number to identify JsonTableExecContext for sanity checking */
246 : #define JSON_TABLE_EXEC_CONTEXT_MAGIC 418352867
247 :
248 : typedef struct JsonTableExecContext
249 : {
250 : int magic;
251 :
252 : /* State of the plan providing a row evaluated from "root" jsonpath */
253 : JsonTablePlanState *rootplanstate;
254 :
255 : /*
256 : * Per-column JsonTablePlanStates for all columns including the nested
257 : * ones.
258 : */
259 : JsonTablePlanState **colplanstates;
260 : } JsonTableExecContext;
261 :
262 : /* strict/lax flags is decomposed into four [un]wrap/error flags */
263 : #define jspStrictAbsenceOfErrors(cxt) (!(cxt)->laxMode)
264 : #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
265 : #define jspAutoWrap(cxt) ((cxt)->laxMode)
266 : #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
267 : #define jspThrowErrors(cxt) ((cxt)->throwErrors)
268 :
269 : /* Convenience macro: return or throw error depending on context */
270 : #define RETURN_ERROR(throw_error) \
271 : do { \
272 : if (jspThrowErrors(cxt)) \
273 : throw_error; \
274 : else \
275 : return jperError; \
276 : } while (0)
277 :
278 : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
279 : JsonbValue *larg,
280 : JsonbValue *rarg,
281 : void *param);
282 : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2,
283 : Node *escontext);
284 :
285 : static JsonPathExecResult executeJsonPath(JsonPath *path, void *vars,
286 : JsonPathGetVarCallback getVar,
287 : JsonPathCountVarsCallback countVars,
288 : Jsonb *json, bool throwErrors,
289 : JsonValueList *result, bool useTz);
290 : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
291 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
292 : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
293 : JsonPathItem *jsp, JsonbValue *jb,
294 : JsonValueList *found, bool unwrap);
295 : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
296 : JsonPathItem *jsp, JsonbValue *jb,
297 : JsonValueList *found, bool unwrapElements);
298 : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
299 : JsonPathItem *cur, JsonPathItem *next,
300 : JsonbValue *v, JsonValueList *found);
301 : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
302 : bool unwrap, JsonValueList *found);
303 : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
304 : JsonbValue *jb, bool unwrap, JsonValueList *found);
305 : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
306 : JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
307 : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
308 : JsonPathItem *jsp, JsonbValue *jb);
309 : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
310 : JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
311 : uint32 level, uint32 first, uint32 last,
312 : bool ignoreStructuralErrors, bool unwrapNext);
313 : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
314 : JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
315 : JsonbValue *jb, bool unwrapRightArg,
316 : JsonPathPredicateCallback exec, void *param);
317 : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
318 : JsonPathItem *jsp, JsonbValue *jb,
319 : BinaryArithmFunc func, JsonValueList *found);
320 : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
321 : JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
322 : JsonValueList *found);
323 : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
324 : JsonbValue *whole, JsonbValue *initial, void *param);
325 : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
326 : JsonbValue *rarg, void *param);
327 : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
328 : JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
329 : JsonValueList *found);
330 : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
331 : JsonbValue *jb, JsonValueList *found);
332 : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
333 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
334 : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
335 : JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
336 : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
337 : JsonbValue *value);
338 : static JsonbValue *GetJsonPathVar(void *cxt, char *varName, int varNameLen,
339 : JsonbValue *baseObject, int *baseObjectId);
340 : static int CountJsonPathVars(void *cxt);
341 : static void JsonItemFromDatum(Datum val, Oid typid, int32 typmod,
342 : JsonbValue *res);
343 : static void JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num);
344 : static void getJsonPathVariable(JsonPathExecContext *cxt,
345 : JsonPathItem *variable, JsonbValue *value);
346 : static int countVariablesFromJsonb(void *varsJsonb);
347 : static JsonbValue *getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
348 : int varNameLength,
349 : JsonbValue *baseObject,
350 : int *baseObjectId);
351 : static int JsonbArraySize(JsonbValue *jb);
352 : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
353 : JsonbValue *rv, void *p);
354 : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
355 : bool useTz);
356 : static int compareNumeric(Numeric a, Numeric b);
357 : static JsonbValue *copyJsonbValue(JsonbValue *src);
358 : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
359 : JsonPathItem *jsp, JsonbValue *jb, int32 *index);
360 : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
361 : JsonbValue *jbv, int32 id);
362 : static void JsonValueListInit(JsonValueList *jvl);
363 : static void JsonValueListClear(JsonValueList *jvl);
364 : static void JsonValueListAppend(JsonValueList *jvl, const JsonbValue *jbv);
365 : static bool JsonValueListIsEmpty(const JsonValueList *jvl);
366 : static bool JsonValueListIsSingleton(const JsonValueList *jvl);
367 : static bool JsonValueListHasMultipleItems(const JsonValueList *jvl);
368 : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
369 : static void JsonValueListInitIterator(JsonValueList *jvl,
370 : JsonValueListIterator *it);
371 : static JsonbValue *JsonValueListNext(JsonValueListIterator *it);
372 : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
373 : static int JsonbType(JsonbValue *jb);
374 : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
375 : static JsonbValue *wrapItemsInArray(JsonValueList *items);
376 : static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
377 : bool useTz, bool *cast_error);
378 : static void checkTimezoneIsUsedForCast(bool useTz, const char *type1,
379 : const char *type2);
380 :
381 : static void JsonTableInitOpaque(TableFuncScanState *state, int natts);
382 : static JsonTablePlanState *JsonTableInitPlan(JsonTableExecContext *cxt,
383 : JsonTablePlan *plan,
384 : JsonTablePlanState *parentstate,
385 : List *args,
386 : MemoryContext mcxt);
387 : static void JsonTableSetDocument(TableFuncScanState *state, Datum value);
388 : static void JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item);
389 : static bool JsonTableFetchRow(TableFuncScanState *state);
390 : static Datum JsonTableGetValue(TableFuncScanState *state, int colnum,
391 : Oid typid, int32 typmod, bool *isnull);
392 : static void JsonTableDestroyOpaque(TableFuncScanState *state);
393 : static bool JsonTablePlanScanNextRow(JsonTablePlanState *planstate);
394 : static void JsonTableResetNestedPlan(JsonTablePlanState *planstate);
395 : static bool JsonTablePlanJoinNextRow(JsonTablePlanState *planstate);
396 : static bool JsonTablePlanNextRow(JsonTablePlanState *planstate);
397 :
398 : const TableFuncRoutine JsonbTableRoutine =
399 : {
400 : .InitOpaque = JsonTableInitOpaque,
401 : .SetDocument = JsonTableSetDocument,
402 : .SetNamespace = NULL,
403 : .SetRowFilter = NULL,
404 : .SetColumnFilter = NULL,
405 : .FetchRow = JsonTableFetchRow,
406 : .GetValue = JsonTableGetValue,
407 : .DestroyOpaque = JsonTableDestroyOpaque
408 : };
409 :
410 : /****************** User interface to JsonPath executor ********************/
411 :
412 : /*
413 : * jsonb_path_exists
414 : * Returns true if jsonpath returns at least one item for the specified
415 : * jsonb value. This function and jsonb_path_match() are used to
416 : * implement @? and @@ operators, which in turn are intended to have an
417 : * index support. Thus, it's desirable to make it easier to achieve
418 : * consistency between index scan results and sequential scan results.
419 : * So, we throw as few errors as possible. Regarding this function,
420 : * such behavior also matches behavior of JSON_EXISTS() clause of
421 : * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
422 : * an analogy in SQL/JSON, so we define its behavior on our own.
423 : */
424 : static Datum
425 57470 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
426 : {
427 57470 : Jsonb *jb = PG_GETARG_JSONB_P(0);
428 57470 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
429 : JsonPathExecResult res;
430 57470 : Jsonb *vars = NULL;
431 57470 : bool silent = true;
432 :
433 57470 : if (PG_NARGS() == 4)
434 : {
435 43 : vars = PG_GETARG_JSONB_P(2);
436 43 : silent = PG_GETARG_BOOL(3);
437 : }
438 :
439 57470 : res = executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
440 : countVariablesFromJsonb,
441 57470 : jb, !silent, NULL, tz);
442 :
443 57462 : PG_FREE_IF_COPY(jb, 0);
444 57462 : PG_FREE_IF_COPY(jp, 1);
445 :
446 57462 : if (jperIsError(res))
447 50 : PG_RETURN_NULL();
448 :
449 57412 : PG_RETURN_BOOL(res == jperOk);
450 : }
451 :
452 : Datum
453 43 : jsonb_path_exists(PG_FUNCTION_ARGS)
454 : {
455 43 : return jsonb_path_exists_internal(fcinfo, false);
456 : }
457 :
458 : Datum
459 0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
460 : {
461 0 : return jsonb_path_exists_internal(fcinfo, true);
462 : }
463 :
464 : /*
465 : * jsonb_path_exists_opr
466 : * Implementation of operator "jsonb @? jsonpath" (2-argument version of
467 : * jsonb_path_exists()).
468 : */
469 : Datum
470 57427 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
471 : {
472 : /* just call the other one -- it can handle both cases */
473 57427 : return jsonb_path_exists_internal(fcinfo, false);
474 : }
475 :
476 : /*
477 : * jsonb_path_match
478 : * Returns jsonpath predicate result item for the specified jsonb value.
479 : * See jsonb_path_exists() comment for details regarding error handling.
480 : */
481 : static Datum
482 65299 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
483 : {
484 65299 : Jsonb *jb = PG_GETARG_JSONB_P(0);
485 65299 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
486 65299 : Jsonb *vars = NULL;
487 65299 : bool silent = true;
488 : JsonValueList found;
489 :
490 65299 : if (PG_NARGS() == 4)
491 : {
492 97 : vars = PG_GETARG_JSONB_P(2);
493 97 : silent = PG_GETARG_BOOL(3);
494 : }
495 :
496 65299 : JsonValueListInit(&found);
497 :
498 65299 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
499 : countVariablesFromJsonb,
500 65299 : jb, !silent, &found, tz);
501 :
502 65291 : PG_FREE_IF_COPY(jb, 0);
503 65291 : PG_FREE_IF_COPY(jp, 1);
504 :
505 65291 : if (JsonValueListIsSingleton(&found))
506 : {
507 65268 : JsonbValue *jbv = JsonValueListHead(&found);
508 :
509 65268 : if (jbv->type == jbvBool)
510 65212 : PG_RETURN_BOOL(jbv->val.boolean);
511 :
512 56 : if (jbv->type == jbvNull)
513 20 : PG_RETURN_NULL();
514 : }
515 :
516 59 : if (!silent)
517 24 : ereport(ERROR,
518 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
519 : errmsg("single boolean result is expected")));
520 :
521 35 : PG_RETURN_NULL();
522 : }
523 :
524 : Datum
525 97 : jsonb_path_match(PG_FUNCTION_ARGS)
526 : {
527 97 : return jsonb_path_match_internal(fcinfo, false);
528 : }
529 :
530 : Datum
531 0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
532 : {
533 0 : return jsonb_path_match_internal(fcinfo, true);
534 : }
535 :
536 : /*
537 : * jsonb_path_match_opr
538 : * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
539 : * jsonb_path_match()).
540 : */
541 : Datum
542 65202 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
543 : {
544 : /* just call the other one -- it can handle both cases */
545 65202 : return jsonb_path_match_internal(fcinfo, false);
546 : }
547 :
548 : /*
549 : * jsonb_path_query
550 : * Executes jsonpath for given jsonb document and returns result as
551 : * rowset.
552 : */
553 : static Datum
554 4944 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
555 : {
556 : FuncCallContext *funcctx;
557 : JsonValueListIterator *iter;
558 : JsonbValue *v;
559 :
560 4944 : if (SRF_IS_FIRSTCALL())
561 : {
562 : JsonPath *jp;
563 : Jsonb *jb;
564 : Jsonb *vars;
565 : bool silent;
566 : MemoryContext oldcontext;
567 : JsonValueList *found;
568 :
569 2712 : funcctx = SRF_FIRSTCALL_INIT();
570 2712 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
571 :
572 2712 : jb = PG_GETARG_JSONB_P_COPY(0);
573 2712 : jp = PG_GETARG_JSONPATH_P_COPY(1);
574 2712 : vars = PG_GETARG_JSONB_P_COPY(2);
575 2712 : silent = PG_GETARG_BOOL(3);
576 :
577 2712 : found = palloc_object(JsonValueList);
578 2712 : JsonValueListInit(found);
579 :
580 2712 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
581 : countVariablesFromJsonb,
582 2712 : jb, !silent, found, tz);
583 :
584 1824 : iter = palloc_object(JsonValueListIterator);
585 1824 : JsonValueListInitIterator(found, iter);
586 :
587 1824 : funcctx->user_fctx = iter;
588 :
589 1824 : MemoryContextSwitchTo(oldcontext);
590 : }
591 :
592 4056 : funcctx = SRF_PERCALL_SETUP();
593 4056 : iter = funcctx->user_fctx;
594 :
595 4056 : v = JsonValueListNext(iter);
596 :
597 4056 : if (v == NULL)
598 1824 : SRF_RETURN_DONE(funcctx);
599 :
600 2232 : SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
601 : }
602 :
603 : Datum
604 3940 : jsonb_path_query(PG_FUNCTION_ARGS)
605 : {
606 3940 : return jsonb_path_query_internal(fcinfo, false);
607 : }
608 :
609 : Datum
610 1004 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
611 : {
612 1004 : return jsonb_path_query_internal(fcinfo, true);
613 : }
614 :
615 : /*
616 : * jsonb_path_query_array
617 : * Executes jsonpath for given jsonb document and returns result as
618 : * jsonb array.
619 : */
620 : static Datum
621 52 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
622 : {
623 52 : Jsonb *jb = PG_GETARG_JSONB_P(0);
624 52 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
625 52 : Jsonb *vars = PG_GETARG_JSONB_P(2);
626 52 : bool silent = PG_GETARG_BOOL(3);
627 : JsonValueList found;
628 :
629 52 : JsonValueListInit(&found);
630 :
631 52 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
632 : countVariablesFromJsonb,
633 52 : jb, !silent, &found, tz);
634 :
635 48 : PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
636 : }
637 :
638 : Datum
639 52 : jsonb_path_query_array(PG_FUNCTION_ARGS)
640 : {
641 52 : return jsonb_path_query_array_internal(fcinfo, false);
642 : }
643 :
644 : Datum
645 0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
646 : {
647 0 : return jsonb_path_query_array_internal(fcinfo, true);
648 : }
649 :
650 : /*
651 : * jsonb_path_query_first
652 : * Executes jsonpath for given jsonb document and returns first result
653 : * item. If there are no items, NULL returned.
654 : */
655 : static Datum
656 2923 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
657 : {
658 2923 : Jsonb *jb = PG_GETARG_JSONB_P(0);
659 2923 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
660 2923 : Jsonb *vars = PG_GETARG_JSONB_P(2);
661 2923 : bool silent = PG_GETARG_BOOL(3);
662 : JsonValueList found;
663 :
664 2923 : JsonValueListInit(&found);
665 :
666 2923 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
667 : countVariablesFromJsonb,
668 2923 : jb, !silent, &found, tz);
669 :
670 2915 : if (!JsonValueListIsEmpty(&found))
671 2905 : PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
672 : else
673 10 : PG_RETURN_NULL();
674 : }
675 :
676 : Datum
677 2923 : jsonb_path_query_first(PG_FUNCTION_ARGS)
678 : {
679 2923 : return jsonb_path_query_first_internal(fcinfo, false);
680 : }
681 :
682 : Datum
683 0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
684 : {
685 0 : return jsonb_path_query_first_internal(fcinfo, true);
686 : }
687 :
688 : /********************Execute functions for JsonPath**************************/
689 :
690 : /*
691 : * Interface to jsonpath executor
692 : *
693 : * 'path' - jsonpath to be executed
694 : * 'vars' - variables to be substituted to jsonpath
695 : * 'getVar' - callback used by getJsonPathVariable() to extract variables from
696 : * 'vars'
697 : * 'countVars' - callback to count the number of jsonpath variables in 'vars'
698 : * 'json' - target document for jsonpath evaluation
699 : * 'throwErrors' - whether we should throw suppressible errors
700 : * 'result' - list to store result items into
701 : *
702 : * Returns an error if a recoverable error happens during processing, or NULL
703 : * on no error.
704 : *
705 : * Note, jsonb and jsonpath values should be available and untoasted during
706 : * work because JsonPathItem, JsonbValue and result item could have pointers
707 : * into input values. If caller needs to just check if document matches
708 : * jsonpath, then it doesn't provide a result arg. In this case executor
709 : * works till first positive result and does not check the rest if possible.
710 : * In other case it tries to find all the satisfied result items.
711 : */
712 : static JsonPathExecResult
713 132660 : executeJsonPath(JsonPath *path, void *vars, JsonPathGetVarCallback getVar,
714 : JsonPathCountVarsCallback countVars,
715 : Jsonb *json, bool throwErrors, JsonValueList *result,
716 : bool useTz)
717 : {
718 : JsonPathExecContext cxt;
719 : JsonPathExecResult res;
720 : JsonPathItem jsp;
721 : JsonbValue jbv;
722 :
723 132660 : jspInit(&jsp, path);
724 :
725 132660 : if (!JsonbExtractScalar(&json->root, &jbv))
726 128818 : JsonbInitBinary(&jbv, json);
727 :
728 132660 : cxt.vars = vars;
729 132660 : cxt.getVar = getVar;
730 132660 : cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
731 132660 : cxt.ignoreStructuralErrors = cxt.laxMode;
732 132660 : cxt.root = &jbv;
733 132660 : cxt.current = &jbv;
734 132660 : cxt.baseObject.jbc = NULL;
735 132660 : cxt.baseObject.id = 0;
736 : /* 1 + number of base objects in vars */
737 132660 : cxt.lastGeneratedObjectId = 1 + countVars(vars);
738 132652 : cxt.innermostArraySize = -1;
739 132652 : cxt.throwErrors = throwErrors;
740 132652 : cxt.useTz = useTz;
741 :
742 132652 : if (jspStrictAbsenceOfErrors(&cxt) && !result)
743 : {
744 : /*
745 : * In strict mode we must get a complete list of values to check that
746 : * there are no errors at all.
747 : */
748 : JsonValueList vals;
749 : bool isempty;
750 :
751 165 : JsonValueListInit(&vals);
752 :
753 165 : res = executeItem(&cxt, &jsp, &jbv, &vals);
754 :
755 157 : isempty = JsonValueListIsEmpty(&vals);
756 157 : JsonValueListClear(&vals);
757 :
758 157 : if (jperIsError(res))
759 144 : return res;
760 :
761 13 : return isempty ? jperNotFound : jperOk;
762 : }
763 :
764 132487 : res = executeItem(&cxt, &jsp, &jbv, result);
765 :
766 : Assert(!throwErrors || !jperIsError(res));
767 :
768 131559 : return res;
769 : }
770 :
771 : /*
772 : * Execute jsonpath with automatic unwrapping of current item in lax mode.
773 : */
774 : static JsonPathExecResult
775 397546 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
776 : JsonbValue *jb, JsonValueList *found)
777 : {
778 397546 : return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
779 : }
780 :
781 : /*
782 : * Main jsonpath executor function: walks on jsonpath structure, finds
783 : * relevant parts of jsonb and evaluates expressions over them.
784 : * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
785 : */
786 : static JsonPathExecResult
787 403124 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
788 : JsonbValue *jb, JsonValueList *found, bool unwrap)
789 : {
790 : JsonPathItem elem;
791 403124 : JsonPathExecResult res = jperNotFound;
792 : JsonBaseObjectInfo baseObject;
793 :
794 403124 : check_stack_depth();
795 403124 : CHECK_FOR_INTERRUPTS();
796 :
797 403124 : switch (jsp->type)
798 : {
799 40703 : case jpiNull:
800 : case jpiBool:
801 : case jpiNumeric:
802 : case jpiString:
803 : case jpiVariable:
804 : {
805 : JsonbValue v;
806 40703 : bool hasNext = jspGetNext(jsp, &elem);
807 :
808 40703 : if (!hasNext && !found && jsp->type != jpiVariable)
809 : {
810 : /*
811 : * Skip evaluation, but not for variables. We must
812 : * trigger an error for the missing variable.
813 : */
814 10 : res = jperOk;
815 10 : break;
816 : }
817 :
818 40693 : baseObject = cxt->baseObject;
819 40693 : getJsonPathItem(cxt, jsp, &v);
820 :
821 40661 : res = executeNextItem(cxt, jsp, &elem,
822 : &v, found);
823 40661 : cxt->baseObject = baseObject;
824 : }
825 40661 : break;
826 :
827 : /* all boolean item types: */
828 68127 : case jpiAnd:
829 : case jpiOr:
830 : case jpiNot:
831 : case jpiIsUnknown:
832 : case jpiEqual:
833 : case jpiNotEqual:
834 : case jpiLess:
835 : case jpiGreater:
836 : case jpiLessOrEqual:
837 : case jpiGreaterOrEqual:
838 : case jpiExists:
839 : case jpiStartsWith:
840 : case jpiLikeRegex:
841 : {
842 68127 : JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
843 :
844 68127 : res = appendBoolResult(cxt, jsp, found, st);
845 68127 : break;
846 : }
847 :
848 242 : case jpiAdd:
849 242 : return executeBinaryArithmExpr(cxt, jsp, jb,
850 : numeric_add_safe, found);
851 :
852 110 : case jpiSub:
853 110 : return executeBinaryArithmExpr(cxt, jsp, jb,
854 : numeric_sub_safe, found);
855 :
856 40 : case jpiMul:
857 40 : return executeBinaryArithmExpr(cxt, jsp, jb,
858 : numeric_mul_safe, found);
859 :
860 44 : case jpiDiv:
861 44 : return executeBinaryArithmExpr(cxt, jsp, jb,
862 : numeric_div_safe, found);
863 :
864 8 : case jpiMod:
865 8 : return executeBinaryArithmExpr(cxt, jsp, jb,
866 : numeric_mod_safe, found);
867 :
868 48 : case jpiPlus:
869 48 : return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
870 :
871 100 : case jpiMinus:
872 100 : return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
873 : found);
874 :
875 2049 : case jpiAnyArray:
876 2049 : if (JsonbType(jb) == jbvArray)
877 : {
878 1845 : bool hasNext = jspGetNext(jsp, &elem);
879 :
880 1845 : res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
881 1845 : jb, found, jspAutoUnwrap(cxt));
882 : }
883 204 : else if (jspAutoWrap(cxt))
884 172 : res = executeNextItem(cxt, jsp, NULL, jb, found);
885 32 : else if (!jspIgnoreStructuralErrors(cxt))
886 32 : RETURN_ERROR(ereport(ERROR,
887 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
888 : errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
889 1857 : break;
890 :
891 194 : case jpiAnyKey:
892 194 : if (JsonbType(jb) == jbvObject)
893 : {
894 97 : bool hasNext = jspGetNext(jsp, &elem);
895 :
896 97 : if (jb->type != jbvBinary)
897 0 : elog(ERROR, "invalid jsonb object type: %d", jb->type);
898 :
899 97 : return executeAnyItem
900 : (cxt, hasNext ? &elem : NULL,
901 : jb->val.binary.data, found, 1, 1, 1,
902 97 : false, jspAutoUnwrap(cxt));
903 : }
904 97 : else if (unwrap && JsonbType(jb) == jbvArray)
905 18 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
906 79 : else if (!jspIgnoreStructuralErrors(cxt))
907 : {
908 : Assert(found);
909 21 : RETURN_ERROR(ereport(ERROR,
910 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
911 : errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
912 : }
913 58 : break;
914 :
915 341 : case jpiIndexArray:
916 341 : if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
917 259 : {
918 333 : int innermostArraySize = cxt->innermostArraySize;
919 : int i;
920 333 : int size = JsonbArraySize(jb);
921 333 : bool singleton = size < 0;
922 333 : bool hasNext = jspGetNext(jsp, &elem);
923 :
924 333 : if (singleton)
925 4 : size = 1;
926 :
927 333 : cxt->innermostArraySize = size; /* for LAST evaluation */
928 :
929 577 : for (i = 0; i < jsp->content.array.nelems; i++)
930 : {
931 : JsonPathItem from;
932 : JsonPathItem to;
933 : int32 index;
934 : int32 index_from;
935 : int32 index_to;
936 341 : bool range = jspGetArraySubscript(jsp, &from,
937 : &to, i);
938 :
939 341 : res = getArrayIndex(cxt, &from, jb, &index_from);
940 :
941 325 : if (jperIsError(res))
942 23 : break;
943 :
944 307 : if (range)
945 : {
946 21 : res = getArrayIndex(cxt, &to, jb, &index_to);
947 :
948 17 : if (jperIsError(res))
949 0 : break;
950 : }
951 : else
952 286 : index_to = index_from;
953 :
954 303 : if (!jspIgnoreStructuralErrors(cxt) &&
955 64 : (index_from < 0 ||
956 56 : index_from > index_to ||
957 56 : index_to >= size))
958 54 : RETURN_ERROR(ereport(ERROR,
959 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
960 : errmsg("jsonpath array subscript is out of bounds"))));
961 :
962 269 : if (index_from < 0)
963 8 : index_from = 0;
964 :
965 269 : if (index_to >= size)
966 22 : index_to = size - 1;
967 :
968 269 : res = jperNotFound;
969 :
970 516 : for (index = index_from; index <= index_to; index++)
971 : {
972 : JsonbValue *v;
973 :
974 272 : if (singleton)
975 : {
976 4 : v = jb;
977 : }
978 : else
979 : {
980 268 : v = getIthJsonbValueFromContainer(jb->val.binary.data,
981 : (uint32) index);
982 :
983 268 : if (v == NULL)
984 0 : continue;
985 : }
986 :
987 272 : if (!hasNext && !found)
988 20 : return jperOk;
989 :
990 252 : res = executeNextItem(cxt, jsp, &elem, v, found);
991 :
992 252 : if (jperIsError(res))
993 0 : break;
994 :
995 252 : if (res == jperOk && !found)
996 5 : break;
997 : }
998 :
999 249 : if (jperIsError(res))
1000 0 : break;
1001 :
1002 249 : if (res == jperOk && !found)
1003 5 : break;
1004 : }
1005 :
1006 259 : cxt->innermostArraySize = innermostArraySize;
1007 : }
1008 8 : else if (!jspIgnoreStructuralErrors(cxt))
1009 : {
1010 8 : RETURN_ERROR(ereport(ERROR,
1011 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1012 : errmsg("jsonpath array accessor can only be applied to an array"))));
1013 : }
1014 259 : break;
1015 :
1016 229 : case jpiAny:
1017 : {
1018 229 : bool hasNext = jspGetNext(jsp, &elem);
1019 :
1020 : /* first try without any intermediate steps */
1021 229 : if (jsp->content.anybounds.first == 0)
1022 : {
1023 : bool savedIgnoreStructuralErrors;
1024 :
1025 127 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1026 127 : cxt->ignoreStructuralErrors = true;
1027 127 : res = executeNextItem(cxt, jsp, &elem,
1028 : jb, found);
1029 127 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1030 :
1031 127 : if (res == jperOk && !found)
1032 5 : break;
1033 : }
1034 :
1035 224 : if (jb->type == jbvBinary)
1036 224 : res = executeAnyItem
1037 : (cxt, hasNext ? &elem : NULL,
1038 : jb->val.binary.data, found,
1039 : 1,
1040 : jsp->content.anybounds.first,
1041 : jsp->content.anybounds.last,
1042 224 : true, jspAutoUnwrap(cxt));
1043 224 : break;
1044 : }
1045 :
1046 111725 : case jpiKey:
1047 111725 : if (JsonbType(jb) == jbvObject)
1048 : {
1049 : JsonbValue *v;
1050 : JsonbValue key;
1051 :
1052 111093 : key.type = jbvString;
1053 111093 : key.val.string.val = jspGetString(jsp, &key.val.string.len);
1054 :
1055 111093 : v = findJsonbValueFromContainer(jb->val.binary.data,
1056 : JB_FOBJECT, &key);
1057 :
1058 111093 : if (v != NULL)
1059 : {
1060 18400 : res = executeNextItem(cxt, jsp, NULL,
1061 : v, found);
1062 18400 : pfree(v);
1063 : }
1064 92693 : else if (!jspIgnoreStructuralErrors(cxt))
1065 : {
1066 : Assert(found);
1067 :
1068 59 : if (!jspThrowErrors(cxt))
1069 43 : return jperError;
1070 :
1071 16 : ereport(ERROR,
1072 : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
1073 : errmsg("JSON object does not contain key \"%s\"",
1074 : pnstrdup(key.val.string.val,
1075 : key.val.string.len))));
1076 : }
1077 : }
1078 632 : else if (unwrap && JsonbType(jb) == jbvArray)
1079 24 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1080 608 : else if (!jspIgnoreStructuralErrors(cxt))
1081 : {
1082 : Assert(found);
1083 175 : RETURN_ERROR(ereport(ERROR,
1084 : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
1085 : errmsg("jsonpath member accessor can only be applied to an object"))));
1086 : }
1087 111467 : break;
1088 :
1089 16149 : case jpiCurrent:
1090 16149 : res = executeNextItem(cxt, jsp, NULL, cxt->current, found);
1091 16149 : break;
1092 :
1093 140701 : case jpiRoot:
1094 140701 : jb = cxt->root;
1095 140701 : baseObject = setBaseObject(cxt, jb, 0);
1096 140701 : res = executeNextItem(cxt, jsp, NULL, jb, found);
1097 139825 : cxt->baseObject = baseObject;
1098 139825 : break;
1099 :
1100 15214 : case jpiFilter:
1101 : {
1102 : JsonPathBool st;
1103 :
1104 15214 : if (unwrap && JsonbType(jb) == jbvArray)
1105 88 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1106 : false);
1107 :
1108 15126 : jspGetArg(jsp, &elem);
1109 15126 : st = executeNestedBoolItem(cxt, &elem, jb);
1110 15062 : if (st != jpbTrue)
1111 12660 : res = jperNotFound;
1112 : else
1113 2402 : res = executeNextItem(cxt, jsp, NULL,
1114 : jb, found);
1115 15062 : break;
1116 : }
1117 :
1118 191 : case jpiType:
1119 : {
1120 : JsonbValue jbv;
1121 :
1122 191 : jbv.type = jbvString;
1123 191 : jbv.val.string.val = pstrdup(JsonbTypeName(jb));
1124 191 : jbv.val.string.len = strlen(jbv.val.string.val);
1125 :
1126 191 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1127 : }
1128 191 : break;
1129 :
1130 48 : case jpiSize:
1131 : {
1132 48 : int size = JsonbArraySize(jb);
1133 : JsonbValue jbv;
1134 :
1135 48 : if (size < 0)
1136 : {
1137 32 : if (!jspAutoWrap(cxt))
1138 : {
1139 8 : if (!jspIgnoreStructuralErrors(cxt))
1140 8 : RETURN_ERROR(ereport(ERROR,
1141 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1142 : errmsg("jsonpath item method .%s() can only be applied to an array",
1143 : jspOperationName(jsp->type)))));
1144 0 : break;
1145 : }
1146 :
1147 24 : size = 1;
1148 : }
1149 :
1150 40 : jbv.type = jbvNumeric;
1151 40 : jbv.val.numeric = int64_to_numeric(size);
1152 :
1153 40 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1154 : }
1155 40 : break;
1156 :
1157 72 : case jpiAbs:
1158 72 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1159 : found);
1160 :
1161 32 : case jpiFloor:
1162 32 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1163 : found);
1164 :
1165 68 : case jpiCeiling:
1166 68 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1167 : found);
1168 :
1169 76 : case jpiDouble:
1170 : {
1171 : JsonbValue jbv;
1172 :
1173 76 : if (unwrap && JsonbType(jb) == jbvArray)
1174 28 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1175 : false);
1176 :
1177 72 : if (jb->type == jbvNumeric)
1178 : {
1179 8 : char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1180 : NumericGetDatum(jb->val.numeric)));
1181 : double val;
1182 8 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1183 :
1184 8 : val = float8in_internal(tmp,
1185 : NULL,
1186 : "double precision",
1187 : tmp,
1188 : (Node *) &escontext);
1189 :
1190 8 : if (escontext.error_occurred)
1191 4 : RETURN_ERROR(ereport(ERROR,
1192 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1193 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1194 : tmp, jspOperationName(jsp->type), "double precision"))));
1195 4 : if (isinf(val) || isnan(val))
1196 0 : RETURN_ERROR(ereport(ERROR,
1197 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1198 : errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1199 : jspOperationName(jsp->type)))));
1200 4 : res = jperOk;
1201 : }
1202 64 : else if (jb->type == jbvString)
1203 : {
1204 : /* cast string as double */
1205 : double val;
1206 32 : char *tmp = pnstrdup(jb->val.string.val,
1207 32 : jb->val.string.len);
1208 32 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1209 :
1210 32 : val = float8in_internal(tmp,
1211 : NULL,
1212 : "double precision",
1213 : tmp,
1214 : (Node *) &escontext);
1215 :
1216 32 : if (escontext.error_occurred)
1217 12 : RETURN_ERROR(ereport(ERROR,
1218 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1219 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1220 : tmp, jspOperationName(jsp->type), "double precision"))));
1221 28 : if (isinf(val) || isnan(val))
1222 24 : RETURN_ERROR(ereport(ERROR,
1223 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1224 : errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1225 : jspOperationName(jsp->type)))));
1226 :
1227 4 : jb = &jbv;
1228 4 : jb->type = jbvNumeric;
1229 4 : jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1230 : Float8GetDatum(val)));
1231 4 : res = jperOk;
1232 : }
1233 :
1234 40 : if (res == jperNotFound)
1235 32 : RETURN_ERROR(ereport(ERROR,
1236 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1237 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1238 : jspOperationName(jsp->type)))));
1239 :
1240 8 : res = executeNextItem(cxt, jsp, NULL, jb, found);
1241 : }
1242 8 : break;
1243 :
1244 5686 : case jpiDatetime:
1245 : case jpiDate:
1246 : case jpiTime:
1247 : case jpiTimeTz:
1248 : case jpiTimestamp:
1249 : case jpiTimestampTz:
1250 5686 : if (unwrap && JsonbType(jb) == jbvArray)
1251 24 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1252 :
1253 5662 : return executeDateTimeMethod(cxt, jsp, jb, found);
1254 :
1255 62 : case jpiKeyValue:
1256 62 : if (unwrap && JsonbType(jb) == jbvArray)
1257 4 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1258 :
1259 58 : return executeKeyValueMethod(cxt, jsp, jb, found);
1260 :
1261 44 : case jpiLast:
1262 : {
1263 : JsonbValue jbv;
1264 : int last;
1265 44 : bool hasNext = jspGetNext(jsp, &elem);
1266 :
1267 44 : if (cxt->innermostArraySize < 0)
1268 0 : elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
1269 :
1270 44 : if (!hasNext && !found)
1271 : {
1272 4 : res = jperOk;
1273 4 : break;
1274 : }
1275 :
1276 40 : last = cxt->innermostArraySize - 1;
1277 :
1278 40 : jbv.type = jbvNumeric;
1279 40 : jbv.val.numeric = int64_to_numeric(last);
1280 :
1281 40 : res = executeNextItem(cxt, jsp, &elem,
1282 : &jbv, found);
1283 : }
1284 40 : break;
1285 :
1286 120 : case jpiBigint:
1287 : {
1288 : JsonbValue jbv;
1289 : Datum datum;
1290 :
1291 120 : if (unwrap && JsonbType(jb) == jbvArray)
1292 28 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1293 : false);
1294 :
1295 116 : if (jb->type == jbvNumeric)
1296 : {
1297 32 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1298 : int64 val;
1299 :
1300 32 : val = numeric_int8_safe(jb->val.numeric,
1301 : (Node *) &escontext);
1302 32 : if (escontext.error_occurred)
1303 8 : RETURN_ERROR(ereport(ERROR,
1304 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1305 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1306 : DatumGetCString(DirectFunctionCall1(numeric_out,
1307 : NumericGetDatum(jb->val.numeric))),
1308 : jspOperationName(jsp->type),
1309 : "bigint"))));
1310 :
1311 24 : datum = Int64GetDatum(val);
1312 24 : res = jperOk;
1313 : }
1314 84 : else if (jb->type == jbvString)
1315 : {
1316 : /* cast string as bigint */
1317 52 : char *tmp = pnstrdup(jb->val.string.val,
1318 52 : jb->val.string.len);
1319 52 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1320 : bool noerr;
1321 :
1322 52 : noerr = DirectInputFunctionCallSafe(int8in, tmp,
1323 : InvalidOid, -1,
1324 : (Node *) &escontext,
1325 : &datum);
1326 :
1327 52 : if (!noerr || escontext.error_occurred)
1328 36 : RETURN_ERROR(ereport(ERROR,
1329 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1330 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1331 : tmp, jspOperationName(jsp->type), "bigint"))));
1332 16 : res = jperOk;
1333 : }
1334 :
1335 72 : if (res == jperNotFound)
1336 32 : RETURN_ERROR(ereport(ERROR,
1337 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1338 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1339 : jspOperationName(jsp->type)))));
1340 :
1341 40 : jbv.type = jbvNumeric;
1342 40 : jbv.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1343 : datum));
1344 :
1345 40 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1346 : }
1347 40 : break;
1348 :
1349 171 : case jpiBoolean:
1350 : {
1351 : JsonbValue jbv;
1352 : bool bval;
1353 :
1354 171 : if (unwrap && JsonbType(jb) == jbvArray)
1355 24 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1356 : false);
1357 :
1358 167 : if (jb->type == jbvBool)
1359 : {
1360 17 : bval = jb->val.boolean;
1361 :
1362 17 : res = jperOk;
1363 : }
1364 150 : else if (jb->type == jbvNumeric)
1365 : {
1366 : int ival;
1367 : Datum datum;
1368 : bool noerr;
1369 33 : char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1370 : NumericGetDatum(jb->val.numeric)));
1371 33 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1372 :
1373 33 : noerr = DirectInputFunctionCallSafe(int4in, tmp,
1374 : InvalidOid, -1,
1375 : (Node *) &escontext,
1376 : &datum);
1377 :
1378 33 : if (!noerr || escontext.error_occurred)
1379 8 : RETURN_ERROR(ereport(ERROR,
1380 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1381 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1382 : tmp, jspOperationName(jsp->type), "boolean"))));
1383 :
1384 25 : ival = DatumGetInt32(datum);
1385 25 : if (ival == 0)
1386 4 : bval = false;
1387 : else
1388 21 : bval = true;
1389 :
1390 25 : res = jperOk;
1391 : }
1392 117 : else if (jb->type == jbvString)
1393 : {
1394 : /* cast string as boolean */
1395 93 : char *tmp = pnstrdup(jb->val.string.val,
1396 93 : jb->val.string.len);
1397 :
1398 93 : if (!parse_bool(tmp, &bval))
1399 36 : RETURN_ERROR(ereport(ERROR,
1400 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1401 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1402 : tmp, jspOperationName(jsp->type), "boolean"))));
1403 :
1404 57 : res = jperOk;
1405 : }
1406 :
1407 123 : if (res == jperNotFound)
1408 24 : RETURN_ERROR(ereport(ERROR,
1409 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1410 : errmsg("jsonpath item method .%s() can only be applied to a boolean, string, or numeric value",
1411 : jspOperationName(jsp->type)))));
1412 :
1413 99 : jbv.type = jbvBool;
1414 99 : jbv.val.boolean = bval;
1415 :
1416 99 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1417 : }
1418 99 : break;
1419 :
1420 284 : case jpiDecimal:
1421 : case jpiNumber:
1422 : {
1423 : JsonbValue jbv;
1424 : Numeric num;
1425 284 : char *numstr = NULL;
1426 :
1427 284 : if (unwrap && JsonbType(jb) == jbvArray)
1428 56 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1429 : false);
1430 :
1431 276 : if (jb->type == jbvNumeric)
1432 : {
1433 116 : num = jb->val.numeric;
1434 116 : if (numeric_is_nan(num) || numeric_is_inf(num))
1435 0 : RETURN_ERROR(ereport(ERROR,
1436 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1437 : errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1438 : jspOperationName(jsp->type)))));
1439 :
1440 116 : if (jsp->type == jpiDecimal)
1441 92 : numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
1442 : NumericGetDatum(num)));
1443 116 : res = jperOk;
1444 : }
1445 160 : else if (jb->type == jbvString)
1446 : {
1447 : /* cast string as number */
1448 : Datum datum;
1449 : bool noerr;
1450 96 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1451 :
1452 96 : numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
1453 :
1454 96 : noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1455 : InvalidOid, -1,
1456 : (Node *) &escontext,
1457 : &datum);
1458 :
1459 96 : if (!noerr || escontext.error_occurred)
1460 24 : RETURN_ERROR(ereport(ERROR,
1461 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1462 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1463 : numstr, jspOperationName(jsp->type), "numeric"))));
1464 :
1465 88 : num = DatumGetNumeric(datum);
1466 88 : if (numeric_is_nan(num) || numeric_is_inf(num))
1467 48 : RETURN_ERROR(ereport(ERROR,
1468 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1469 : errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1470 : jspOperationName(jsp->type)))));
1471 :
1472 40 : res = jperOk;
1473 : }
1474 :
1475 220 : if (res == jperNotFound)
1476 64 : RETURN_ERROR(ereport(ERROR,
1477 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1478 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1479 : jspOperationName(jsp->type)))));
1480 :
1481 : /*
1482 : * If we have arguments, then they must be the precision and
1483 : * optional scale used in .decimal(). Convert them to the
1484 : * typmod equivalent and then truncate the numeric value per
1485 : * this typmod details.
1486 : */
1487 156 : if (jsp->type == jpiDecimal && jsp->content.args.left)
1488 : {
1489 : Datum numdatum;
1490 : Datum dtypmod;
1491 : int32 precision;
1492 68 : int32 scale = 0;
1493 : bool noerr;
1494 : ArrayType *arrtypmod;
1495 : Datum datums[2];
1496 : char pstr[12]; /* sign, 10 digits and '\0' */
1497 : char sstr[12]; /* sign, 10 digits and '\0' */
1498 68 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1499 :
1500 68 : jspGetLeftArg(jsp, &elem);
1501 68 : if (elem.type != jpiNumeric)
1502 0 : elog(ERROR, "invalid jsonpath item type for .decimal() precision");
1503 :
1504 68 : precision = numeric_int4_safe(jspGetNumeric(&elem),
1505 : (Node *) &escontext);
1506 68 : if (escontext.error_occurred)
1507 4 : RETURN_ERROR(ereport(ERROR,
1508 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1509 : errmsg("precision of jsonpath item method .%s() is out of range for type integer",
1510 : jspOperationName(jsp->type)))));
1511 :
1512 64 : if (jsp->content.args.right)
1513 : {
1514 64 : jspGetRightArg(jsp, &elem);
1515 64 : if (elem.type != jpiNumeric)
1516 0 : elog(ERROR, "invalid jsonpath item type for .decimal() scale");
1517 :
1518 64 : scale = numeric_int4_safe(jspGetNumeric(&elem),
1519 : (Node *) &escontext);
1520 64 : if (escontext.error_occurred)
1521 4 : RETURN_ERROR(ereport(ERROR,
1522 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1523 : errmsg("scale of jsonpath item method .%s() is out of range for type integer",
1524 : jspOperationName(jsp->type)))));
1525 : }
1526 :
1527 : /*
1528 : * numerictypmodin() takes the precision and scale in the
1529 : * form of CString arrays.
1530 : */
1531 60 : pg_ltoa(precision, pstr);
1532 60 : datums[0] = CStringGetDatum(pstr);
1533 60 : pg_ltoa(scale, sstr);
1534 60 : datums[1] = CStringGetDatum(sstr);
1535 60 : arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
1536 :
1537 60 : dtypmod = DirectFunctionCall1(numerictypmodin,
1538 : PointerGetDatum(arrtypmod));
1539 :
1540 : /* Convert numstr to Numeric with typmod */
1541 : Assert(numstr != NULL);
1542 40 : noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1543 : InvalidOid, DatumGetInt32(dtypmod),
1544 : (Node *) &escontext,
1545 : &numdatum);
1546 :
1547 40 : if (!noerr || escontext.error_occurred)
1548 8 : RETURN_ERROR(ereport(ERROR,
1549 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1550 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1551 : numstr, jspOperationName(jsp->type), "numeric"))));
1552 :
1553 32 : num = DatumGetNumeric(numdatum);
1554 32 : pfree(arrtypmod);
1555 : }
1556 :
1557 120 : jbv.type = jbvNumeric;
1558 120 : jbv.val.numeric = num;
1559 :
1560 120 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1561 : }
1562 120 : break;
1563 :
1564 112 : case jpiInteger:
1565 : {
1566 : JsonbValue jbv;
1567 : Datum datum;
1568 :
1569 112 : if (unwrap && JsonbType(jb) == jbvArray)
1570 28 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1571 : false);
1572 :
1573 108 : if (jb->type == jbvNumeric)
1574 : {
1575 : int32 val;
1576 28 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1577 :
1578 28 : val = numeric_int4_safe(jb->val.numeric,
1579 : (Node *) &escontext);
1580 28 : if (escontext.error_occurred)
1581 8 : RETURN_ERROR(ereport(ERROR,
1582 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1583 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1584 : DatumGetCString(DirectFunctionCall1(numeric_out,
1585 : NumericGetDatum(jb->val.numeric))),
1586 : jspOperationName(jsp->type), "integer"))));
1587 :
1588 20 : datum = Int32GetDatum(val);
1589 20 : res = jperOk;
1590 : }
1591 80 : else if (jb->type == jbvString)
1592 : {
1593 : /* cast string as integer */
1594 48 : char *tmp = pnstrdup(jb->val.string.val,
1595 48 : jb->val.string.len);
1596 48 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1597 : bool noerr;
1598 :
1599 48 : noerr = DirectInputFunctionCallSafe(int4in, tmp,
1600 : InvalidOid, -1,
1601 : (Node *) &escontext,
1602 : &datum);
1603 :
1604 48 : if (!noerr || escontext.error_occurred)
1605 36 : RETURN_ERROR(ereport(ERROR,
1606 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1607 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1608 : tmp, jspOperationName(jsp->type), "integer"))));
1609 12 : res = jperOk;
1610 : }
1611 :
1612 64 : if (res == jperNotFound)
1613 32 : RETURN_ERROR(ereport(ERROR,
1614 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1615 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1616 : jspOperationName(jsp->type)))));
1617 :
1618 32 : jbv.type = jbvNumeric;
1619 32 : jbv.val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1620 : datum));
1621 :
1622 32 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1623 : }
1624 32 : break;
1625 :
1626 134 : case jpiStringFunc:
1627 : {
1628 : JsonbValue jbv;
1629 134 : char *tmp = NULL;
1630 :
1631 134 : if (unwrap && JsonbType(jb) == jbvArray)
1632 20 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1633 :
1634 126 : switch (JsonbType(jb))
1635 : {
1636 18 : case jbvString:
1637 :
1638 : /*
1639 : * Value is not necessarily null-terminated, so we do
1640 : * pnstrdup() here.
1641 : */
1642 18 : tmp = pnstrdup(jb->val.string.val,
1643 18 : jb->val.string.len);
1644 18 : break;
1645 26 : case jbvNumeric:
1646 26 : tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1647 : NumericGetDatum(jb->val.numeric)));
1648 26 : break;
1649 18 : case jbvBool:
1650 18 : tmp = (jb->val.boolean) ? "true" : "false";
1651 18 : break;
1652 40 : case jbvDatetime:
1653 : {
1654 : char buf[MAXDATELEN + 1];
1655 :
1656 40 : JsonEncodeDateTime(buf,
1657 : jb->val.datetime.value,
1658 : jb->val.datetime.typid,
1659 40 : &jb->val.datetime.tz);
1660 40 : tmp = pstrdup(buf);
1661 : }
1662 40 : break;
1663 24 : case jbvNull:
1664 : case jbvArray:
1665 : case jbvObject:
1666 : case jbvBinary:
1667 24 : RETURN_ERROR(ereport(ERROR,
1668 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1669 : errmsg("jsonpath item method .%s() can only be applied to a boolean, string, numeric, or datetime value",
1670 : jspOperationName(jsp->type)))));
1671 : break;
1672 : }
1673 :
1674 : Assert(tmp != NULL); /* We must have set tmp above */
1675 102 : jbv.val.string.val = tmp;
1676 102 : jbv.val.string.len = strlen(jbv.val.string.val);
1677 102 : jbv.type = jbvString;
1678 :
1679 102 : res = executeNextItem(cxt, jsp, NULL, &jbv, found);
1680 : }
1681 102 : break;
1682 :
1683 0 : default:
1684 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1685 : }
1686 :
1687 394380 : return res;
1688 : }
1689 :
1690 : /*
1691 : * Unwrap current array item and execute jsonpath for each of its elements.
1692 : */
1693 : static JsonPathExecResult
1694 2071 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1695 : JsonbValue *jb, JsonValueList *found,
1696 : bool unwrapElements)
1697 : {
1698 2071 : if (jb->type != jbvBinary)
1699 : {
1700 : Assert(jb->type != jbvArray);
1701 0 : elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1702 : }
1703 :
1704 2071 : return executeAnyItem
1705 : (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1706 : false, unwrapElements);
1707 : }
1708 :
1709 : /*
1710 : * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1711 : * list if provided.
1712 : */
1713 : static JsonPathExecResult
1714 293527 : executeNextItem(JsonPathExecContext *cxt,
1715 : JsonPathItem *cur, JsonPathItem *next,
1716 : JsonbValue *v, JsonValueList *found)
1717 : {
1718 : JsonPathItem elem;
1719 : bool hasNext;
1720 :
1721 293527 : if (!cur)
1722 0 : hasNext = next != NULL;
1723 293527 : else if (next)
1724 115071 : hasNext = jspHasNext(cur);
1725 : else
1726 : {
1727 178456 : next = &elem;
1728 178456 : hasNext = jspGetNext(cur, next);
1729 : }
1730 :
1731 293527 : if (hasNext)
1732 131230 : return executeItem(cxt, next, v, found);
1733 :
1734 162297 : if (found)
1735 128826 : JsonValueListAppend(found, v);
1736 :
1737 162297 : return jperOk;
1738 : }
1739 :
1740 : /*
1741 : * Same as executeItem(), but when "unwrap == true" automatically unwraps
1742 : * each array item from the resulting sequence in lax mode.
1743 : */
1744 : static JsonPathExecResult
1745 133302 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1746 : JsonbValue *jb, bool unwrap,
1747 : JsonValueList *found)
1748 : {
1749 133302 : if (unwrap && jspAutoUnwrap(cxt))
1750 : {
1751 : JsonValueList seq;
1752 : JsonValueListIterator it;
1753 : JsonPathExecResult res;
1754 : JsonbValue *item;
1755 :
1756 79642 : JsonValueListInit(&seq);
1757 :
1758 79642 : res = executeItem(cxt, jsp, jb, &seq);
1759 :
1760 79626 : if (jperIsError(res))
1761 : {
1762 47 : JsonValueListClear(&seq);
1763 47 : return res;
1764 : }
1765 :
1766 79579 : JsonValueListInitIterator(&seq, &it);
1767 133962 : while ((item = JsonValueListNext(&it)))
1768 : {
1769 : Assert(item->type != jbvArray);
1770 :
1771 54383 : if (JsonbType(item) == jbvArray)
1772 36 : executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1773 : else
1774 54347 : JsonValueListAppend(found, item);
1775 : }
1776 :
1777 79579 : JsonValueListClear(&seq);
1778 :
1779 79579 : return jperOk;
1780 : }
1781 :
1782 53660 : return executeItem(cxt, jsp, jb, found);
1783 : }
1784 :
1785 : /*
1786 : * Same as executeItemOptUnwrapResult(), but with error suppression.
1787 : */
1788 : static JsonPathExecResult
1789 132270 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1790 : JsonPathItem *jsp,
1791 : JsonbValue *jb, bool unwrap,
1792 : JsonValueList *found)
1793 : {
1794 : JsonPathExecResult res;
1795 132270 : bool throwErrors = cxt->throwErrors;
1796 :
1797 132270 : cxt->throwErrors = false;
1798 132270 : res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1799 132266 : cxt->throwErrors = throwErrors;
1800 :
1801 132266 : return res;
1802 : }
1803 :
1804 : /* Execute boolean-valued jsonpath expression. */
1805 : static JsonPathBool
1806 119175 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1807 : JsonbValue *jb, bool canHaveNext)
1808 : {
1809 : JsonPathItem larg;
1810 : JsonPathItem rarg;
1811 : JsonPathBool res;
1812 : JsonPathBool res2;
1813 :
1814 : /* since this function recurses, it could be driven to stack overflow */
1815 119175 : check_stack_depth();
1816 :
1817 119175 : if (!canHaveNext && jspHasNext(jsp))
1818 0 : elog(ERROR, "boolean jsonpath item cannot have next item");
1819 :
1820 119175 : switch (jsp->type)
1821 : {
1822 17654 : case jpiAnd:
1823 17654 : jspGetLeftArg(jsp, &larg);
1824 17654 : res = executeBoolItem(cxt, &larg, jb, false);
1825 :
1826 17654 : if (res == jpbFalse)
1827 15212 : return jpbFalse;
1828 :
1829 : /*
1830 : * SQL/JSON says that we should check second arg in case of
1831 : * jperError
1832 : */
1833 :
1834 2442 : jspGetRightArg(jsp, &rarg);
1835 2442 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1836 :
1837 2442 : return res2 == jpbTrue ? res : res2;
1838 :
1839 8636 : case jpiOr:
1840 8636 : jspGetLeftArg(jsp, &larg);
1841 8636 : res = executeBoolItem(cxt, &larg, jb, false);
1842 :
1843 8636 : if (res == jpbTrue)
1844 1660 : return jpbTrue;
1845 :
1846 6976 : jspGetRightArg(jsp, &rarg);
1847 6976 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1848 :
1849 6976 : return res2 == jpbFalse ? res : res2;
1850 :
1851 72 : case jpiNot:
1852 72 : jspGetArg(jsp, &larg);
1853 :
1854 72 : res = executeBoolItem(cxt, &larg, jb, false);
1855 :
1856 72 : if (res == jpbUnknown)
1857 24 : return jpbUnknown;
1858 :
1859 48 : return res == jpbTrue ? jpbFalse : jpbTrue;
1860 :
1861 142 : case jpiIsUnknown:
1862 142 : jspGetArg(jsp, &larg);
1863 142 : res = executeBoolItem(cxt, &larg, jb, false);
1864 142 : return res == jpbUnknown ? jpbTrue : jpbFalse;
1865 :
1866 39555 : case jpiEqual:
1867 : case jpiNotEqual:
1868 : case jpiLess:
1869 : case jpiGreater:
1870 : case jpiLessOrEqual:
1871 : case jpiGreaterOrEqual:
1872 39555 : jspGetLeftArg(jsp, &larg);
1873 39555 : jspGetRightArg(jsp, &rarg);
1874 39555 : return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1875 : executeComparison, cxt);
1876 :
1877 56 : case jpiStartsWith: /* 'whole STARTS WITH initial' */
1878 56 : jspGetLeftArg(jsp, &larg); /* 'whole' */
1879 56 : jspGetRightArg(jsp, &rarg); /* 'initial' */
1880 56 : return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1881 : executeStartsWith, NULL);
1882 :
1883 264 : case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1884 : {
1885 : /*
1886 : * 'expr' is a sequence-returning expression. 'pattern' is a
1887 : * regex string literal. SQL/JSON standard requires XQuery
1888 : * regexes, but we use Postgres regexes here. 'flags' is a
1889 : * string literal converted to integer flags at compile-time.
1890 : */
1891 264 : JsonLikeRegexContext lrcxt = {0};
1892 :
1893 264 : jspInitByBuffer(&larg, jsp->base,
1894 : jsp->content.like_regex.expr);
1895 :
1896 264 : return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1897 : executeLikeRegex, &lrcxt);
1898 : }
1899 :
1900 52796 : case jpiExists:
1901 52796 : jspGetArg(jsp, &larg);
1902 :
1903 52796 : if (jspStrictAbsenceOfErrors(cxt))
1904 : {
1905 : /*
1906 : * In strict mode we must get a complete list of values to
1907 : * check that there are no errors at all.
1908 : */
1909 : JsonValueList vals;
1910 : JsonPathExecResult res;
1911 : bool isempty;
1912 :
1913 34 : JsonValueListInit(&vals);
1914 :
1915 34 : res = executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1916 : false, &vals);
1917 :
1918 34 : isempty = JsonValueListIsEmpty(&vals);
1919 34 : JsonValueListClear(&vals);
1920 :
1921 34 : if (jperIsError(res))
1922 26 : return jpbUnknown;
1923 :
1924 8 : return isempty ? jpbFalse : jpbTrue;
1925 : }
1926 : else
1927 : {
1928 : JsonPathExecResult res =
1929 52762 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1930 : false, NULL);
1931 :
1932 52762 : if (jperIsError(res))
1933 16 : return jpbUnknown;
1934 :
1935 52746 : return res == jperOk ? jpbTrue : jpbFalse;
1936 : }
1937 :
1938 0 : default:
1939 0 : elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1940 : return jpbUnknown;
1941 : }
1942 : }
1943 :
1944 : /*
1945 : * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1946 : * item onto the stack.
1947 : */
1948 : static JsonPathBool
1949 15126 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1950 : JsonbValue *jb)
1951 : {
1952 : JsonbValue *prev;
1953 : JsonPathBool res;
1954 :
1955 15126 : prev = cxt->current;
1956 15126 : cxt->current = jb;
1957 15126 : res = executeBoolItem(cxt, jsp, jb, false);
1958 15062 : cxt->current = prev;
1959 :
1960 15062 : return res;
1961 : }
1962 :
1963 : /*
1964 : * Implementation of several jsonpath nodes:
1965 : * - jpiAny (.** accessor),
1966 : * - jpiAnyKey (.* accessor),
1967 : * - jpiAnyArray ([*] accessor)
1968 : */
1969 : static JsonPathExecResult
1970 2523 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1971 : JsonValueList *found, uint32 level, uint32 first, uint32 last,
1972 : bool ignoreStructuralErrors, bool unwrapNext)
1973 : {
1974 2523 : JsonPathExecResult res = jperNotFound;
1975 : JsonbIterator *it;
1976 : int32 r;
1977 : JsonbValue v;
1978 :
1979 2523 : check_stack_depth();
1980 :
1981 2523 : if (level > last)
1982 22 : return res;
1983 :
1984 2501 : it = JsonbIteratorInit(jbc);
1985 :
1986 : /*
1987 : * Recursively iterate over jsonb objects/arrays
1988 : */
1989 14104 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1990 : {
1991 12135 : if (r == WJB_KEY)
1992 : {
1993 477 : r = JsonbIteratorNext(&it, &v, true);
1994 : Assert(r == WJB_VALUE);
1995 : }
1996 :
1997 12135 : if (r == WJB_VALUE || r == WJB_ELEM)
1998 : {
1999 :
2000 7665 : if (level >= first ||
2001 8 : (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
2002 8 : v.type != jbvBinary)) /* leaves only requested */
2003 : {
2004 : /* check expression */
2005 7621 : if (jsp)
2006 : {
2007 5578 : if (ignoreStructuralErrors)
2008 : {
2009 : bool savedIgnoreStructuralErrors;
2010 :
2011 255 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
2012 255 : cxt->ignoreStructuralErrors = true;
2013 255 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
2014 255 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
2015 : }
2016 : else
2017 5323 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
2018 :
2019 5418 : if (jperIsError(res))
2020 49 : break;
2021 :
2022 5369 : if (res == jperOk && !found)
2023 263 : break;
2024 : }
2025 2043 : else if (found)
2026 2013 : JsonValueListAppend(found, &v);
2027 : else
2028 30 : return jperOk;
2029 : }
2030 :
2031 7163 : if (level < last && v.type == jbvBinary)
2032 : {
2033 131 : res = executeAnyItem
2034 : (cxt, jsp, v.val.binary.data, found,
2035 : level + 1, first, last,
2036 : ignoreStructuralErrors, unwrapNext);
2037 :
2038 131 : if (jperIsError(res))
2039 0 : break;
2040 :
2041 131 : if (res == jperOk && found == NULL)
2042 30 : break;
2043 : }
2044 : }
2045 : }
2046 :
2047 2311 : return res;
2048 : }
2049 :
2050 : /*
2051 : * Execute unary or binary predicate.
2052 : *
2053 : * Predicates have existence semantics, because their operands are item
2054 : * sequences. Pairs of items from the left and right operand's sequences are
2055 : * checked. TRUE returned only if any pair satisfying the condition is found.
2056 : * In strict mode, even if the desired pair has already been found, all pairs
2057 : * still need to be examined to check the absence of errors. If any error
2058 : * occurs, UNKNOWN (analogous to SQL NULL) is returned.
2059 : */
2060 : static JsonPathBool
2061 39875 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
2062 : JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
2063 : bool unwrapRightArg, JsonPathPredicateCallback exec,
2064 : void *param)
2065 : {
2066 : JsonPathExecResult res;
2067 : JsonValueListIterator lseqit;
2068 : JsonValueList lseq;
2069 : JsonValueList rseq;
2070 : JsonbValue *lval;
2071 39875 : bool error = false;
2072 39875 : bool found = false;
2073 :
2074 39875 : JsonValueListInit(&lseq);
2075 39875 : JsonValueListInit(&rseq);
2076 :
2077 : /* Left argument is always auto-unwrapped. */
2078 39875 : res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
2079 39875 : if (jperIsError(res))
2080 : {
2081 12 : error = true;
2082 12 : goto exit;
2083 : }
2084 :
2085 39863 : if (rarg)
2086 : {
2087 : /* Right argument is conditionally auto-unwrapped. */
2088 39599 : res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
2089 : unwrapRightArg, &rseq);
2090 39595 : if (jperIsError(res))
2091 : {
2092 39 : error = true;
2093 39 : goto exit;
2094 : }
2095 : }
2096 :
2097 39820 : JsonValueListInitIterator(&lseq, &lseqit);
2098 53371 : while ((lval = JsonValueListNext(&lseqit)))
2099 : {
2100 : JsonValueListIterator rseqit;
2101 : JsonbValue *rval;
2102 18249 : bool first = true;
2103 :
2104 18249 : JsonValueListInitIterator(&rseq, &rseqit);
2105 18249 : if (rarg)
2106 17985 : rval = JsonValueListNext(&rseqit);
2107 : else
2108 264 : rval = NULL;
2109 :
2110 : /* Loop over right arg sequence or do single pass otherwise */
2111 28524 : while (rarg ? (rval != NULL) : first)
2112 : {
2113 14973 : JsonPathBool res = exec(pred, lval, rval, param);
2114 :
2115 14913 : if (res == jpbUnknown)
2116 : {
2117 502 : error = true;
2118 502 : if (jspStrictAbsenceOfErrors(cxt))
2119 : {
2120 17 : found = false; /* return unknown, not success */
2121 4638 : goto exit;
2122 : }
2123 : }
2124 14411 : else if (res == jpbTrue)
2125 : {
2126 4831 : found = true;
2127 4831 : if (!jspStrictAbsenceOfErrors(cxt))
2128 4621 : goto exit;
2129 : }
2130 :
2131 10275 : first = false;
2132 10275 : if (rarg)
2133 10079 : rval = JsonValueListNext(&rseqit);
2134 : }
2135 : }
2136 :
2137 35122 : exit:
2138 39811 : JsonValueListClear(&lseq);
2139 39811 : JsonValueListClear(&rseq);
2140 :
2141 39811 : if (found) /* possible only in strict mode */
2142 4763 : return jpbTrue;
2143 :
2144 35048 : if (error) /* possible only in lax mode */
2145 531 : return jpbUnknown;
2146 :
2147 34517 : return jpbFalse;
2148 : }
2149 :
2150 : /*
2151 : * Execute binary arithmetic expression on singleton numeric operands.
2152 : * Array operands are automatically unwrapped in lax mode.
2153 : */
2154 : static JsonPathExecResult
2155 444 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
2156 : JsonbValue *jb, BinaryArithmFunc func,
2157 : JsonValueList *found)
2158 : {
2159 : JsonPathExecResult jper;
2160 : JsonPathItem elem;
2161 : JsonValueList lseq;
2162 : JsonValueList rseq;
2163 : JsonbValue *lval;
2164 : JsonbValue *rval;
2165 : JsonbValue resval;
2166 : Numeric res;
2167 :
2168 444 : JsonValueListInit(&lseq);
2169 444 : JsonValueListInit(&rseq);
2170 :
2171 444 : jspGetLeftArg(jsp, &elem);
2172 :
2173 : /*
2174 : * XXX: By standard only operands of multiplicative expressions are
2175 : * unwrapped. We extend it to other binary arithmetic expressions too.
2176 : */
2177 444 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
2178 440 : if (jperIsError(jper))
2179 : {
2180 0 : JsonValueListClear(&lseq);
2181 0 : JsonValueListClear(&rseq);
2182 0 : return jper;
2183 : }
2184 :
2185 440 : jspGetRightArg(jsp, &elem);
2186 :
2187 440 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
2188 436 : if (jperIsError(jper))
2189 : {
2190 0 : JsonValueListClear(&lseq);
2191 0 : JsonValueListClear(&rseq);
2192 0 : return jper;
2193 : }
2194 :
2195 827 : if (!JsonValueListIsSingleton(&lseq) ||
2196 391 : !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
2197 : {
2198 45 : JsonValueListClear(&lseq);
2199 45 : JsonValueListClear(&rseq);
2200 45 : RETURN_ERROR(ereport(ERROR,
2201 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2202 : errmsg("left operand of jsonpath operator %s is not a single numeric value",
2203 : jspOperationName(jsp->type)))));
2204 : }
2205 :
2206 759 : if (!JsonValueListIsSingleton(&rseq) ||
2207 368 : !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
2208 : {
2209 39 : JsonValueListClear(&lseq);
2210 39 : JsonValueListClear(&rseq);
2211 39 : RETURN_ERROR(ereport(ERROR,
2212 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2213 : errmsg("right operand of jsonpath operator %s is not a single numeric value",
2214 : jspOperationName(jsp->type)))));
2215 : }
2216 :
2217 352 : if (jspThrowErrors(cxt))
2218 : {
2219 68 : res = func(lval->val.numeric, rval->val.numeric, NULL);
2220 : }
2221 : else
2222 : {
2223 284 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2224 :
2225 284 : res = func(lval->val.numeric, rval->val.numeric, (Node *) &escontext);
2226 :
2227 284 : if (escontext.error_occurred)
2228 : {
2229 8 : JsonValueListClear(&lseq);
2230 8 : JsonValueListClear(&rseq);
2231 8 : return jperError;
2232 : }
2233 : }
2234 :
2235 328 : JsonValueListClear(&lseq);
2236 328 : JsonValueListClear(&rseq);
2237 :
2238 328 : if (!jspGetNext(jsp, &elem) && !found)
2239 5 : return jperOk;
2240 :
2241 323 : resval.type = jbvNumeric;
2242 323 : resval.val.numeric = res;
2243 :
2244 323 : return executeNextItem(cxt, jsp, &elem, &resval, found);
2245 : }
2246 :
2247 : /*
2248 : * Execute unary arithmetic expression for each numeric item in its operand's
2249 : * sequence. Array operand is automatically unwrapped in lax mode.
2250 : */
2251 : static JsonPathExecResult
2252 148 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
2253 : JsonbValue *jb, PGFunction func, JsonValueList *found)
2254 : {
2255 : JsonPathExecResult jper;
2256 : JsonPathExecResult jper2;
2257 : JsonPathItem elem;
2258 : JsonValueList seq;
2259 : JsonValueListIterator it;
2260 : JsonbValue *val;
2261 : bool hasNext;
2262 :
2263 148 : JsonValueListInit(&seq);
2264 :
2265 148 : jspGetArg(jsp, &elem);
2266 148 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
2267 :
2268 144 : if (jperIsError(jper))
2269 0 : goto exit;
2270 :
2271 144 : jper = jperNotFound;
2272 :
2273 144 : hasNext = jspGetNext(jsp, &elem);
2274 :
2275 144 : JsonValueListInitIterator(&seq, &it);
2276 258 : while ((val = JsonValueListNext(&it)))
2277 : {
2278 150 : if ((val = getScalar(val, jbvNumeric)))
2279 : {
2280 119 : if (!found && !hasNext)
2281 : {
2282 10 : jper = jperOk;
2283 10 : goto exit;
2284 : }
2285 : }
2286 : else
2287 : {
2288 31 : if (!found && !hasNext)
2289 5 : continue; /* skip non-numerics processing */
2290 :
2291 26 : JsonValueListClear(&seq);
2292 26 : RETURN_ERROR(ereport(ERROR,
2293 : (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
2294 : errmsg("operand of unary jsonpath operator %s is not a numeric value",
2295 : jspOperationName(jsp->type)))));
2296 : }
2297 :
2298 109 : if (func)
2299 66 : val->val.numeric =
2300 66 : DatumGetNumeric(DirectFunctionCall1(func,
2301 : NumericGetDatum(val->val.numeric)));
2302 :
2303 109 : jper2 = executeNextItem(cxt, jsp, &elem, val, found);
2304 :
2305 109 : if (jperIsError(jper2))
2306 : {
2307 0 : jper = jper2;
2308 0 : goto exit;
2309 : }
2310 :
2311 109 : if (jper2 == jperOk)
2312 : {
2313 109 : jper = jperOk;
2314 109 : if (!found)
2315 0 : goto exit;
2316 : }
2317 : }
2318 :
2319 108 : exit:
2320 118 : JsonValueListClear(&seq);
2321 :
2322 118 : return jper;
2323 : }
2324 :
2325 : /*
2326 : * STARTS_WITH predicate callback.
2327 : *
2328 : * Check if the 'whole' string starts from 'initial' string.
2329 : */
2330 : static JsonPathBool
2331 116 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
2332 : void *param)
2333 : {
2334 116 : if (!(whole = getScalar(whole, jbvString)))
2335 32 : return jpbUnknown; /* error */
2336 :
2337 84 : if (!(initial = getScalar(initial, jbvString)))
2338 0 : return jpbUnknown; /* error */
2339 :
2340 84 : if (whole->val.string.len >= initial->val.string.len &&
2341 60 : !memcmp(whole->val.string.val,
2342 60 : initial->val.string.val,
2343 60 : initial->val.string.len))
2344 36 : return jpbTrue;
2345 :
2346 48 : return jpbFalse;
2347 : }
2348 :
2349 : /*
2350 : * LIKE_REGEX predicate callback.
2351 : *
2352 : * Check if the string matches regex pattern.
2353 : */
2354 : static JsonPathBool
2355 264 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
2356 : void *param)
2357 : {
2358 264 : JsonLikeRegexContext *cxt = param;
2359 :
2360 264 : if (!(str = getScalar(str, jbvString)))
2361 80 : return jpbUnknown;
2362 :
2363 : /* Cache regex text and converted flags. */
2364 184 : if (!cxt->regex)
2365 : {
2366 184 : cxt->regex =
2367 184 : cstring_to_text_with_len(jsp->content.like_regex.pattern,
2368 : jsp->content.like_regex.patternlen);
2369 184 : (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
2370 : &(cxt->cflags), NULL);
2371 : }
2372 :
2373 184 : if (RE_compile_and_execute(cxt->regex, str->val.string.val,
2374 : str->val.string.len,
2375 : cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
2376 68 : return jpbTrue;
2377 :
2378 116 : return jpbFalse;
2379 : }
2380 :
2381 : /*
2382 : * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
2383 : * user function 'func'.
2384 : */
2385 : static JsonPathExecResult
2386 172 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2387 : JsonbValue *jb, bool unwrap, PGFunction func,
2388 : JsonValueList *found)
2389 : {
2390 : JsonPathItem next;
2391 : Datum datum;
2392 : JsonbValue jbv;
2393 :
2394 172 : if (unwrap && JsonbType(jb) == jbvArray)
2395 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
2396 :
2397 172 : if (!(jb = getScalar(jb, jbvNumeric)))
2398 24 : RETURN_ERROR(ereport(ERROR,
2399 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
2400 : errmsg("jsonpath item method .%s() can only be applied to a numeric value",
2401 : jspOperationName(jsp->type)))));
2402 :
2403 148 : datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
2404 :
2405 148 : if (!jspGetNext(jsp, &next) && !found)
2406 0 : return jperOk;
2407 :
2408 148 : jbv.type = jbvNumeric;
2409 148 : jbv.val.numeric = DatumGetNumeric(datum);
2410 :
2411 148 : return executeNextItem(cxt, jsp, &next, &jbv, found);
2412 : }
2413 :
2414 : /*
2415 : * Implementation of the .datetime() and related methods.
2416 : *
2417 : * Converts a string into a date/time value. The actual type is determined at
2418 : * run time.
2419 : * If an argument is provided, this argument is used as a template string.
2420 : * Otherwise, the first fitting ISO format is selected.
2421 : *
2422 : * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
2423 : * have a format, so ISO format is used. However, except for .date(), they all
2424 : * take an optional time precision.
2425 : */
2426 : static JsonPathExecResult
2427 5662 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2428 : JsonbValue *jb, JsonValueList *found)
2429 : {
2430 : JsonbValue jbv;
2431 : Datum value;
2432 : text *datetime;
2433 : Oid collid;
2434 : Oid typid;
2435 5662 : int32 typmod = -1;
2436 5662 : int tz = 0;
2437 : bool hasNext;
2438 5662 : JsonPathExecResult res = jperNotFound;
2439 : JsonPathItem elem;
2440 5662 : int32 time_precision = -1;
2441 :
2442 5662 : if (!(jb = getScalar(jb, jbvString)))
2443 120 : RETURN_ERROR(ereport(ERROR,
2444 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2445 : errmsg("jsonpath item method .%s() can only be applied to a string",
2446 : jspOperationName(jsp->type)))));
2447 :
2448 5542 : datetime = cstring_to_text_with_len(jb->val.string.val,
2449 : jb->val.string.len);
2450 :
2451 : /*
2452 : * At some point we might wish to have callers supply the collation to
2453 : * use, but right now it's unclear that they'd be able to do better than
2454 : * DEFAULT_COLLATION_OID anyway.
2455 : */
2456 5542 : collid = DEFAULT_COLLATION_OID;
2457 :
2458 : /*
2459 : * .datetime(template) has an argument, the rest of the methods don't have
2460 : * an argument. So we handle that separately.
2461 : */
2462 5542 : if (jsp->type == jpiDatetime && jsp->content.arg)
2463 1061 : {
2464 : text *template;
2465 : char *template_str;
2466 : int template_len;
2467 1101 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2468 :
2469 1101 : jspGetArg(jsp, &elem);
2470 :
2471 1101 : if (elem.type != jpiString)
2472 0 : elog(ERROR, "invalid jsonpath item type for .datetime() argument");
2473 :
2474 1101 : template_str = jspGetString(&elem, &template_len);
2475 :
2476 1101 : template = cstring_to_text_with_len(template_str,
2477 : template_len);
2478 :
2479 1101 : value = parse_datetime(datetime, template, collid, true,
2480 : &typid, &typmod, &tz,
2481 1101 : jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
2482 :
2483 1061 : if (escontext.error_occurred)
2484 0 : res = jperError;
2485 : else
2486 1061 : res = jperOk;
2487 : }
2488 : else
2489 : {
2490 : /*
2491 : * According to SQL/JSON standard enumerate ISO formats for: date,
2492 : * timetz, time, timestamptz, timestamp.
2493 : *
2494 : * We also support ISO 8601 format (with "T") for timestamps, because
2495 : * to_json[b]() functions use this format.
2496 : */
2497 : static const char *fmt_str[] =
2498 : {
2499 : "yyyy-mm-dd", /* date */
2500 : "HH24:MI:SS.USTZ", /* timetz */
2501 : "HH24:MI:SSTZ",
2502 : "HH24:MI:SS.US", /* time without tz */
2503 : "HH24:MI:SS",
2504 : "yyyy-mm-dd HH24:MI:SS.USTZ", /* timestamptz */
2505 : "yyyy-mm-dd HH24:MI:SSTZ",
2506 : "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ",
2507 : "yyyy-mm-dd\"T\"HH24:MI:SSTZ",
2508 : "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */
2509 : "yyyy-mm-dd HH24:MI:SS",
2510 : "yyyy-mm-dd\"T\"HH24:MI:SS.US",
2511 : "yyyy-mm-dd\"T\"HH24:MI:SS"
2512 : };
2513 :
2514 : /* cache for format texts */
2515 : static text *fmt_txt[lengthof(fmt_str)] = {0};
2516 : int i;
2517 :
2518 : /*
2519 : * Check for optional precision for methods other than .datetime() and
2520 : * .date()
2521 : */
2522 4441 : if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
2523 2468 : jsp->content.arg)
2524 : {
2525 520 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2526 :
2527 520 : jspGetArg(jsp, &elem);
2528 :
2529 520 : if (elem.type != jpiNumeric)
2530 0 : elog(ERROR, "invalid jsonpath item type for %s argument",
2531 : jspOperationName(jsp->type));
2532 :
2533 520 : time_precision = numeric_int4_safe(jspGetNumeric(&elem),
2534 : (Node *) &escontext);
2535 520 : if (escontext.error_occurred)
2536 16 : RETURN_ERROR(ereport(ERROR,
2537 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2538 : errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
2539 : jspOperationName(jsp->type)))));
2540 : }
2541 :
2542 : /* loop until datetime format fits */
2543 25175 : for (i = 0; i < lengthof(fmt_str); i++)
2544 : {
2545 25143 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2546 :
2547 25143 : if (!fmt_txt[i])
2548 : {
2549 : MemoryContext oldcxt =
2550 52 : MemoryContextSwitchTo(TopMemoryContext);
2551 :
2552 52 : fmt_txt[i] = cstring_to_text(fmt_str[i]);
2553 52 : MemoryContextSwitchTo(oldcxt);
2554 : }
2555 :
2556 25143 : value = parse_datetime(datetime, fmt_txt[i], collid, true,
2557 : &typid, &typmod, &tz,
2558 : (Node *) &escontext);
2559 :
2560 25143 : if (!escontext.error_occurred)
2561 : {
2562 4393 : res = jperOk;
2563 4393 : break;
2564 : }
2565 : }
2566 :
2567 4425 : if (res == jperNotFound)
2568 : {
2569 32 : if (jsp->type == jpiDatetime)
2570 12 : RETURN_ERROR(ereport(ERROR,
2571 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2572 : errmsg("%s format is not recognized: \"%s\"",
2573 : "datetime", text_to_cstring(datetime)),
2574 : errhint("Use a datetime template argument to specify the input data format."))));
2575 : else
2576 20 : RETURN_ERROR(ereport(ERROR,
2577 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2578 : errmsg("%s format is not recognized: \"%s\"",
2579 : jspOperationName(jsp->type), text_to_cstring(datetime)))));
2580 :
2581 : }
2582 : }
2583 :
2584 : /*
2585 : * parse_datetime() processes the entire input string per the template or
2586 : * ISO format and returns the Datum in best fitted datetime type. So, if
2587 : * this call is for a specific datatype, then we do the conversion here.
2588 : * Throw an error for incompatible types.
2589 : */
2590 5454 : switch (jsp->type)
2591 : {
2592 2597 : case jpiDatetime: /* Nothing to do for DATETIME */
2593 2597 : break;
2594 421 : case jpiDate:
2595 : {
2596 : /* Convert result type to date */
2597 421 : switch (typid)
2598 : {
2599 317 : case DATEOID: /* Nothing to do for DATE */
2600 317 : break;
2601 8 : case TIMEOID:
2602 : case TIMETZOID:
2603 8 : RETURN_ERROR(ereport(ERROR,
2604 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2605 : errmsg("%s format is not recognized: \"%s\"",
2606 : "date", text_to_cstring(datetime)))));
2607 : break;
2608 52 : case TIMESTAMPOID:
2609 52 : value = DirectFunctionCall1(timestamp_date,
2610 : value);
2611 52 : break;
2612 44 : case TIMESTAMPTZOID:
2613 44 : checkTimezoneIsUsedForCast(cxt->useTz,
2614 : "timestamptz", "date");
2615 28 : value = DirectFunctionCall1(timestamptz_date,
2616 : value);
2617 28 : break;
2618 0 : default:
2619 0 : elog(ERROR, "type with oid %u not supported", typid);
2620 : }
2621 :
2622 397 : typid = DATEOID;
2623 : }
2624 397 : break;
2625 541 : case jpiTime:
2626 : {
2627 : /* Convert result type to time without time zone */
2628 541 : switch (typid)
2629 : {
2630 4 : case DATEOID:
2631 4 : RETURN_ERROR(ereport(ERROR,
2632 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2633 : errmsg("%s format is not recognized: \"%s\"",
2634 : "time", text_to_cstring(datetime)))));
2635 : break;
2636 405 : case TIMEOID: /* Nothing to do for TIME */
2637 405 : break;
2638 72 : case TIMETZOID:
2639 72 : checkTimezoneIsUsedForCast(cxt->useTz,
2640 : "timetz", "time");
2641 52 : value = DirectFunctionCall1(timetz_time,
2642 : value);
2643 52 : break;
2644 20 : case TIMESTAMPOID:
2645 20 : value = DirectFunctionCall1(timestamp_time,
2646 : value);
2647 20 : break;
2648 40 : case TIMESTAMPTZOID:
2649 40 : checkTimezoneIsUsedForCast(cxt->useTz,
2650 : "timestamptz", "time");
2651 28 : value = DirectFunctionCall1(timestamptz_time,
2652 : value);
2653 28 : break;
2654 0 : default:
2655 0 : elog(ERROR, "type with oid %u not supported", typid);
2656 : }
2657 :
2658 : /* Force the user-given time precision, if any */
2659 505 : if (time_precision != -1)
2660 : {
2661 : TimeADT result;
2662 :
2663 : /* Get a warning when precision is reduced */
2664 108 : time_precision = anytime_typmod_check(false,
2665 : time_precision);
2666 108 : result = DatumGetTimeADT(value);
2667 108 : AdjustTimeForTypmod(&result, time_precision);
2668 108 : value = TimeADTGetDatum(result);
2669 :
2670 : /* Update the typmod value with the user-given precision */
2671 108 : typmod = time_precision;
2672 : }
2673 :
2674 505 : typid = TIMEOID;
2675 : }
2676 505 : break;
2677 641 : case jpiTimeTz:
2678 : {
2679 : /* Convert result type to time with time zone */
2680 641 : switch (typid)
2681 : {
2682 8 : case DATEOID:
2683 : case TIMESTAMPOID:
2684 8 : RETURN_ERROR(ereport(ERROR,
2685 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2686 : errmsg("%s format is not recognized: \"%s\"",
2687 : "time_tz", text_to_cstring(datetime)))));
2688 : break;
2689 76 : case TIMEOID:
2690 76 : checkTimezoneIsUsedForCast(cxt->useTz,
2691 : "time", "timetz");
2692 56 : value = DirectFunctionCall1(time_timetz,
2693 : value);
2694 56 : break;
2695 529 : case TIMETZOID: /* Nothing to do for TIMETZ */
2696 529 : break;
2697 28 : case TIMESTAMPTZOID:
2698 28 : value = DirectFunctionCall1(timestamptz_timetz,
2699 : value);
2700 28 : break;
2701 0 : default:
2702 0 : elog(ERROR, "type with oid %u not supported", typid);
2703 : }
2704 :
2705 : /* Force the user-given time precision, if any */
2706 613 : if (time_precision != -1)
2707 : {
2708 : TimeTzADT *result;
2709 :
2710 : /* Get a warning when precision is reduced */
2711 132 : time_precision = anytime_typmod_check(true,
2712 : time_precision);
2713 132 : result = DatumGetTimeTzADTP(value);
2714 132 : AdjustTimeForTypmod(&result->time, time_precision);
2715 132 : value = TimeTzADTPGetDatum(result);
2716 :
2717 : /* Update the typmod value with the user-given precision */
2718 132 : typmod = time_precision;
2719 : }
2720 :
2721 613 : typid = TIMETZOID;
2722 : }
2723 613 : break;
2724 549 : case jpiTimestamp:
2725 : {
2726 : /* Convert result type to timestamp without time zone */
2727 549 : switch (typid)
2728 : {
2729 36 : case DATEOID:
2730 36 : value = DirectFunctionCall1(date_timestamp,
2731 : value);
2732 36 : break;
2733 8 : case TIMEOID:
2734 : case TIMETZOID:
2735 8 : RETURN_ERROR(ereport(ERROR,
2736 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2737 : errmsg("%s format is not recognized: \"%s\"",
2738 : "timestamp", text_to_cstring(datetime)))));
2739 : break;
2740 409 : case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
2741 409 : break;
2742 96 : case TIMESTAMPTZOID:
2743 96 : checkTimezoneIsUsedForCast(cxt->useTz,
2744 : "timestamptz", "timestamp");
2745 64 : value = DirectFunctionCall1(timestamptz_timestamp,
2746 : value);
2747 64 : break;
2748 0 : default:
2749 0 : elog(ERROR, "type with oid %u not supported", typid);
2750 : }
2751 :
2752 : /* Force the user-given time precision, if any */
2753 509 : if (time_precision != -1)
2754 : {
2755 : Timestamp result;
2756 108 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2757 :
2758 : /* Get a warning when precision is reduced */
2759 108 : time_precision = anytimestamp_typmod_check(false,
2760 : time_precision);
2761 108 : result = DatumGetTimestamp(value);
2762 108 : AdjustTimestampForTypmod(&result, time_precision,
2763 : (Node *) &escontext);
2764 108 : if (escontext.error_occurred) /* should not happen */
2765 0 : RETURN_ERROR(ereport(ERROR,
2766 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2767 : errmsg("time precision of jsonpath item method .%s() is invalid",
2768 : jspOperationName(jsp->type)))));
2769 108 : value = TimestampGetDatum(result);
2770 :
2771 : /* Update the typmod value with the user-given precision */
2772 108 : typmod = time_precision;
2773 : }
2774 :
2775 509 : typid = TIMESTAMPOID;
2776 : }
2777 509 : break;
2778 705 : case jpiTimestampTz:
2779 : {
2780 : struct pg_tm tm;
2781 : fsec_t fsec;
2782 :
2783 : /* Convert result type to timestamp with time zone */
2784 705 : switch (typid)
2785 : {
2786 40 : case DATEOID:
2787 40 : checkTimezoneIsUsedForCast(cxt->useTz,
2788 : "date", "timestamptz");
2789 :
2790 : /*
2791 : * Get the timezone value explicitly since JsonbValue
2792 : * keeps that separate.
2793 : */
2794 36 : j2date(DatumGetDateADT(value) + POSTGRES_EPOCH_JDATE,
2795 : &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2796 36 : tm.tm_hour = 0;
2797 36 : tm.tm_min = 0;
2798 36 : tm.tm_sec = 0;
2799 36 : tz = DetermineTimeZoneOffset(&tm, session_timezone);
2800 :
2801 36 : value = DirectFunctionCall1(date_timestamptz,
2802 : value);
2803 36 : break;
2804 8 : case TIMEOID:
2805 : case TIMETZOID:
2806 8 : RETURN_ERROR(ereport(ERROR,
2807 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2808 : errmsg("%s format is not recognized: \"%s\"",
2809 : "timestamp_tz", text_to_cstring(datetime)))));
2810 : break;
2811 88 : case TIMESTAMPOID:
2812 88 : checkTimezoneIsUsedForCast(cxt->useTz,
2813 : "timestamp", "timestamptz");
2814 :
2815 : /*
2816 : * Get the timezone value explicitly since JsonbValue
2817 : * keeps that separate.
2818 : */
2819 60 : if (timestamp2tm(DatumGetTimestamp(value), NULL, &tm,
2820 : &fsec, NULL, NULL) == 0)
2821 60 : tz = DetermineTimeZoneOffset(&tm,
2822 : session_timezone);
2823 :
2824 60 : value = DirectFunctionCall1(timestamp_timestamptz,
2825 : value);
2826 60 : break;
2827 569 : case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
2828 569 : break;
2829 0 : default:
2830 0 : elog(ERROR, "type with oid %u not supported", typid);
2831 : }
2832 :
2833 : /* Force the user-given time precision, if any */
2834 665 : if (time_precision != -1)
2835 : {
2836 : Timestamp result;
2837 140 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2838 :
2839 : /* Get a warning when precision is reduced */
2840 140 : time_precision = anytimestamp_typmod_check(true,
2841 : time_precision);
2842 140 : result = DatumGetTimestampTz(value);
2843 140 : AdjustTimestampForTypmod(&result, time_precision,
2844 : (Node *) &escontext);
2845 140 : if (escontext.error_occurred) /* should not happen */
2846 0 : RETURN_ERROR(ereport(ERROR,
2847 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2848 : errmsg("time precision of jsonpath item method .%s() is invalid",
2849 : jspOperationName(jsp->type)))));
2850 140 : value = TimestampTzGetDatum(result);
2851 :
2852 : /* Update the typmod value with the user-given precision */
2853 140 : typmod = time_precision;
2854 : }
2855 :
2856 665 : typid = TIMESTAMPTZOID;
2857 : }
2858 665 : break;
2859 0 : default:
2860 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
2861 : }
2862 :
2863 5286 : pfree(datetime);
2864 :
2865 5286 : if (jperIsError(res))
2866 0 : return res;
2867 :
2868 5286 : hasNext = jspGetNext(jsp, &elem);
2869 :
2870 5286 : if (!hasNext && !found)
2871 30 : return res;
2872 :
2873 5256 : jbv.type = jbvDatetime;
2874 5256 : jbv.val.datetime.value = value;
2875 5256 : jbv.val.datetime.typid = typid;
2876 5256 : jbv.val.datetime.typmod = typmod;
2877 5256 : jbv.val.datetime.tz = tz;
2878 :
2879 5256 : return executeNextItem(cxt, jsp, &elem, &jbv, found);
2880 : }
2881 :
2882 : /*
2883 : * Implementation of .keyvalue() method.
2884 : *
2885 : * .keyvalue() method returns a sequence of object's key-value pairs in the
2886 : * following format: '{ "key": key, "value": value, "id": id }'.
2887 : *
2888 : * "id" field is an object identifier which is constructed from the two parts:
2889 : * base object id and its binary offset in base object's jsonb:
2890 : * id = 10000000000 * base_object_id + obj_offset_in_base_object
2891 : *
2892 : * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
2893 : * (maximal offset in jsonb). Decimal multiplier is used here to improve the
2894 : * readability of identifiers.
2895 : *
2896 : * Base object is usually a root object of the path: context item '$' or path
2897 : * variable '$var', literals can't produce objects for now. But if the path
2898 : * contains generated objects (.keyvalue() itself, for example), then they
2899 : * become base object for the subsequent .keyvalue().
2900 : *
2901 : * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
2902 : * of variables (see getJsonPathVariable()). Ids for generated objects
2903 : * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
2904 : */
2905 : static JsonPathExecResult
2906 58 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2907 : JsonbValue *jb, JsonValueList *found)
2908 : {
2909 58 : JsonPathExecResult res = jperNotFound;
2910 : JsonPathItem next;
2911 : JsonbContainer *jbc;
2912 : JsonbValue key;
2913 : JsonbValue val;
2914 : JsonbValue idval;
2915 : JsonbValue keystr;
2916 : JsonbValue valstr;
2917 : JsonbValue idstr;
2918 : JsonbIterator *it;
2919 : JsonbIteratorToken tok;
2920 : int64 id;
2921 : bool hasNext;
2922 :
2923 58 : if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
2924 16 : RETURN_ERROR(ereport(ERROR,
2925 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
2926 : errmsg("jsonpath item method .%s() can only be applied to an object",
2927 : jspOperationName(jsp->type)))));
2928 :
2929 42 : jbc = jb->val.binary.data;
2930 :
2931 42 : if (!JsonContainerSize(jbc))
2932 12 : return jperNotFound; /* no key-value pairs */
2933 :
2934 30 : hasNext = jspGetNext(jsp, &next);
2935 :
2936 30 : keystr.type = jbvString;
2937 30 : keystr.val.string.val = "key";
2938 30 : keystr.val.string.len = 3;
2939 :
2940 30 : valstr.type = jbvString;
2941 30 : valstr.val.string.val = "value";
2942 30 : valstr.val.string.len = 5;
2943 :
2944 30 : idstr.type = jbvString;
2945 30 : idstr.val.string.val = "id";
2946 30 : idstr.val.string.len = 2;
2947 :
2948 : /* construct object id from its base object and offset inside that */
2949 30 : id = jb->type != jbvBinary ? 0 :
2950 30 : (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
2951 30 : id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
2952 :
2953 30 : idval.type = jbvNumeric;
2954 30 : idval.val.numeric = int64_to_numeric(id);
2955 :
2956 30 : it = JsonbIteratorInit(jbc);
2957 :
2958 116 : while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
2959 : {
2960 : JsonBaseObjectInfo baseObject;
2961 : JsonbValue obj;
2962 : JsonbInState ps;
2963 : Jsonb *jsonb;
2964 :
2965 96 : if (tok != WJB_KEY)
2966 50 : continue;
2967 :
2968 46 : res = jperOk;
2969 :
2970 46 : if (!hasNext && !found)
2971 10 : break;
2972 :
2973 41 : tok = JsonbIteratorNext(&it, &val, true);
2974 : Assert(tok == WJB_VALUE);
2975 :
2976 41 : memset(&ps, 0, sizeof(ps));
2977 :
2978 41 : pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2979 :
2980 41 : pushJsonbValue(&ps, WJB_KEY, &keystr);
2981 41 : pushJsonbValue(&ps, WJB_VALUE, &key);
2982 :
2983 41 : pushJsonbValue(&ps, WJB_KEY, &valstr);
2984 41 : pushJsonbValue(&ps, WJB_VALUE, &val);
2985 :
2986 41 : pushJsonbValue(&ps, WJB_KEY, &idstr);
2987 41 : pushJsonbValue(&ps, WJB_VALUE, &idval);
2988 :
2989 41 : pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2990 :
2991 41 : jsonb = JsonbValueToJsonb(ps.result);
2992 :
2993 41 : JsonbInitBinary(&obj, jsonb);
2994 :
2995 41 : baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2996 :
2997 41 : res = executeNextItem(cxt, jsp, &next, &obj, found);
2998 :
2999 41 : cxt->baseObject = baseObject;
3000 :
3001 41 : if (jperIsError(res))
3002 0 : return res;
3003 :
3004 41 : if (res == jperOk && !found)
3005 5 : break;
3006 : }
3007 :
3008 30 : return res;
3009 : }
3010 :
3011 : /*
3012 : * Convert boolean execution status 'res' to a boolean JSON item and execute
3013 : * next jsonpath.
3014 : */
3015 : static JsonPathExecResult
3016 68127 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
3017 : JsonValueList *found, JsonPathBool res)
3018 : {
3019 : JsonPathItem next;
3020 : JsonbValue jbv;
3021 :
3022 68127 : if (!jspGetNext(jsp, &next) && !found)
3023 13 : return jperOk; /* found singleton boolean value */
3024 :
3025 68114 : if (res == jpbUnknown)
3026 : {
3027 23 : jbv.type = jbvNull;
3028 : }
3029 : else
3030 : {
3031 68091 : jbv.type = jbvBool;
3032 68091 : jbv.val.boolean = res == jpbTrue;
3033 : }
3034 :
3035 68114 : return executeNextItem(cxt, jsp, &next, &jbv, found);
3036 : }
3037 :
3038 : /*
3039 : * Convert jsonpath's scalar or variable node to actual jsonb value.
3040 : *
3041 : * If node is a variable then its id returned, otherwise 0 returned.
3042 : */
3043 : static void
3044 40693 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
3045 : JsonbValue *value)
3046 : {
3047 40693 : switch (item->type)
3048 : {
3049 5036 : case jpiNull:
3050 5036 : value->type = jbvNull;
3051 5036 : break;
3052 950 : case jpiBool:
3053 950 : value->type = jbvBool;
3054 950 : value->val.boolean = jspGetBool(item);
3055 950 : break;
3056 13328 : case jpiNumeric:
3057 13328 : value->type = jbvNumeric;
3058 13328 : value->val.numeric = jspGetNumeric(item);
3059 13328 : break;
3060 16103 : case jpiString:
3061 16103 : value->type = jbvString;
3062 32206 : value->val.string.val = jspGetString(item,
3063 16103 : &value->val.string.len);
3064 16103 : break;
3065 5276 : case jpiVariable:
3066 5276 : getJsonPathVariable(cxt, item, value);
3067 5244 : return;
3068 0 : default:
3069 0 : elog(ERROR, "unexpected jsonpath item type");
3070 : }
3071 : }
3072 :
3073 : /*
3074 : * Returns the computed value of a JSON path variable with given name.
3075 : */
3076 : static JsonbValue *
3077 1740 : GetJsonPathVar(void *cxt, char *varName, int varNameLen,
3078 : JsonbValue *baseObject, int *baseObjectId)
3079 : {
3080 1740 : JsonPathVariable *var = NULL;
3081 1740 : List *vars = cxt;
3082 : ListCell *lc;
3083 : JsonbValue *result;
3084 1740 : int id = 1;
3085 :
3086 2504 : foreach(lc, vars)
3087 : {
3088 2492 : JsonPathVariable *curvar = lfirst(lc);
3089 :
3090 2492 : if (curvar->namelen == varNameLen &&
3091 2484 : strncmp(curvar->name, varName, varNameLen) == 0)
3092 : {
3093 1728 : var = curvar;
3094 1728 : break;
3095 : }
3096 :
3097 764 : id++;
3098 : }
3099 :
3100 1740 : if (var == NULL)
3101 : {
3102 12 : *baseObjectId = -1;
3103 12 : return NULL;
3104 : }
3105 :
3106 1728 : result = palloc_object(JsonbValue);
3107 1728 : if (var->isnull)
3108 : {
3109 0 : *baseObjectId = 0;
3110 0 : result->type = jbvNull;
3111 : }
3112 : else
3113 1728 : JsonItemFromDatum(var->value, var->typid, var->typmod, result);
3114 :
3115 1728 : *baseObject = *result;
3116 1728 : *baseObjectId = id;
3117 :
3118 1728 : return result;
3119 : }
3120 :
3121 : static int
3122 4204 : CountJsonPathVars(void *cxt)
3123 : {
3124 4204 : List *vars = (List *) cxt;
3125 :
3126 4204 : return list_length(vars);
3127 : }
3128 :
3129 :
3130 : /*
3131 : * Initialize JsonbValue to pass to jsonpath executor from given
3132 : * datum value of the specified type.
3133 : */
3134 : static void
3135 1728 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
3136 : {
3137 1728 : switch (typid)
3138 : {
3139 0 : case BOOLOID:
3140 0 : res->type = jbvBool;
3141 0 : res->val.boolean = DatumGetBool(val);
3142 0 : break;
3143 0 : case NUMERICOID:
3144 0 : JsonbValueInitNumericDatum(res, val);
3145 0 : break;
3146 0 : case INT2OID:
3147 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
3148 0 : break;
3149 1660 : case INT4OID:
3150 1660 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
3151 1660 : break;
3152 0 : case INT8OID:
3153 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
3154 0 : break;
3155 0 : case FLOAT4OID:
3156 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
3157 0 : break;
3158 0 : case FLOAT8OID:
3159 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
3160 0 : break;
3161 8 : case TEXTOID:
3162 : case VARCHAROID:
3163 8 : res->type = jbvString;
3164 8 : res->val.string.val = VARDATA_ANY(DatumGetPointer(val));
3165 8 : res->val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(val));
3166 8 : break;
3167 48 : case DATEOID:
3168 : case TIMEOID:
3169 : case TIMETZOID:
3170 : case TIMESTAMPOID:
3171 : case TIMESTAMPTZOID:
3172 48 : res->type = jbvDatetime;
3173 48 : res->val.datetime.value = val;
3174 48 : res->val.datetime.typid = typid;
3175 48 : res->val.datetime.typmod = typmod;
3176 48 : res->val.datetime.tz = 0;
3177 48 : break;
3178 12 : case JSONBOID:
3179 : {
3180 12 : JsonbValue *jbv = res;
3181 12 : Jsonb *jb = DatumGetJsonbP(val);
3182 :
3183 12 : if (JsonContainerIsScalar(&jb->root))
3184 : {
3185 : bool result PG_USED_FOR_ASSERTS_ONLY;
3186 :
3187 12 : result = JsonbExtractScalar(&jb->root, jbv);
3188 : Assert(result);
3189 : }
3190 : else
3191 0 : JsonbInitBinary(jbv, jb);
3192 12 : break;
3193 : }
3194 0 : case JSONOID:
3195 : {
3196 0 : text *txt = DatumGetTextP(val);
3197 0 : char *str = text_to_cstring(txt);
3198 : Jsonb *jb;
3199 :
3200 0 : jb = DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
3201 : CStringGetDatum(str)));
3202 0 : pfree(str);
3203 :
3204 0 : JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
3205 0 : break;
3206 : }
3207 0 : default:
3208 0 : ereport(ERROR,
3209 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3210 : errmsg("could not convert value of type %s to jsonpath",
3211 : format_type_be(typid)));
3212 : }
3213 1728 : }
3214 :
3215 : /* Initialize numeric value from the given datum */
3216 : static void
3217 1660 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
3218 : {
3219 1660 : jbv->type = jbvNumeric;
3220 1660 : jbv->val.numeric = DatumGetNumeric(num);
3221 1660 : }
3222 :
3223 : /*
3224 : * Get the value of variable passed to jsonpath executor
3225 : */
3226 : static void
3227 5276 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
3228 : JsonbValue *value)
3229 : {
3230 : char *varName;
3231 : int varNameLength;
3232 : JsonbValue baseObject;
3233 : int baseObjectId;
3234 : JsonbValue *v;
3235 :
3236 : Assert(variable->type == jpiVariable);
3237 5276 : varName = jspGetString(variable, &varNameLength);
3238 :
3239 10552 : if (cxt->vars == NULL ||
3240 5276 : (v = cxt->getVar(cxt->vars, varName, varNameLength,
3241 : &baseObject, &baseObjectId)) == NULL)
3242 32 : ereport(ERROR,
3243 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3244 : errmsg("could not find jsonpath variable \"%s\"",
3245 : pnstrdup(varName, varNameLength))));
3246 :
3247 5244 : if (baseObjectId > 0)
3248 : {
3249 5244 : *value = *v;
3250 5244 : setBaseObject(cxt, &baseObject, baseObjectId);
3251 : }
3252 5244 : }
3253 :
3254 : /*
3255 : * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
3256 : * is specified as a jsonb value.
3257 : */
3258 : static JsonbValue *
3259 3536 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
3260 : JsonbValue *baseObject, int *baseObjectId)
3261 : {
3262 3536 : Jsonb *vars = varsJsonb;
3263 : JsonbValue tmp;
3264 : JsonbValue *result;
3265 :
3266 3536 : tmp.type = jbvString;
3267 3536 : tmp.val.string.val = varName;
3268 3536 : tmp.val.string.len = varNameLength;
3269 :
3270 3536 : result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
3271 :
3272 3536 : if (result == NULL)
3273 : {
3274 20 : *baseObjectId = -1;
3275 20 : return NULL;
3276 : }
3277 :
3278 3516 : *baseObjectId = 1;
3279 3516 : JsonbInitBinary(baseObject, vars);
3280 :
3281 3516 : return result;
3282 : }
3283 :
3284 : /*
3285 : * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
3286 : * is specified as a jsonb value.
3287 : */
3288 : static int
3289 128456 : countVariablesFromJsonb(void *varsJsonb)
3290 : {
3291 128456 : Jsonb *vars = varsJsonb;
3292 :
3293 128456 : if (vars && !JsonContainerIsObject(&vars->root))
3294 : {
3295 8 : ereport(ERROR,
3296 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3297 : errmsg("\"vars\" argument is not an object"),
3298 : errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
3299 : }
3300 :
3301 : /* count of base objects */
3302 128448 : return vars != NULL ? 1 : 0;
3303 : }
3304 :
3305 : /**************** Support functions for JsonPath execution *****************/
3306 :
3307 : /*
3308 : * Returns the size of an array item, or -1 if item is not an array.
3309 : */
3310 : static int
3311 381 : JsonbArraySize(JsonbValue *jb)
3312 : {
3313 : Assert(jb->type != jbvArray);
3314 :
3315 381 : if (jb->type == jbvBinary)
3316 : {
3317 353 : JsonbContainer *jbc = jb->val.binary.data;
3318 :
3319 353 : if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
3320 345 : return JsonContainerSize(jbc);
3321 : }
3322 :
3323 36 : return -1;
3324 : }
3325 :
3326 : /* Comparison predicate callback. */
3327 : static JsonPathBool
3328 14593 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
3329 : {
3330 14593 : JsonPathExecContext *cxt = (JsonPathExecContext *) p;
3331 :
3332 14593 : return compareItems(cmp->type, lv, rv, cxt->useTz);
3333 : }
3334 :
3335 : /*
3336 : * Perform per-byte comparison of two strings.
3337 : */
3338 : static int
3339 2304 : binaryCompareStrings(const char *s1, int len1,
3340 : const char *s2, int len2)
3341 : {
3342 : int cmp;
3343 :
3344 2304 : cmp = memcmp(s1, s2, Min(len1, len2));
3345 :
3346 2304 : if (cmp != 0)
3347 1312 : return cmp;
3348 :
3349 992 : if (len1 == len2)
3350 192 : return 0;
3351 :
3352 800 : return len1 < len2 ? -1 : 1;
3353 : }
3354 :
3355 : /*
3356 : * Compare two strings in the current server encoding using Unicode codepoint
3357 : * collation.
3358 : */
3359 : static int
3360 2304 : compareStrings(const char *mbstr1, int mblen1,
3361 : const char *mbstr2, int mblen2)
3362 : {
3363 4608 : if (GetDatabaseEncoding() == PG_SQL_ASCII ||
3364 2304 : GetDatabaseEncoding() == PG_UTF8)
3365 : {
3366 : /*
3367 : * It's known property of UTF-8 strings that their per-byte comparison
3368 : * result matches codepoints comparison result. ASCII can be
3369 : * considered as special case of UTF-8.
3370 : */
3371 2304 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3372 : }
3373 : else
3374 : {
3375 : char *utf8str1,
3376 : *utf8str2;
3377 : int cmp,
3378 : utf8len1,
3379 : utf8len2;
3380 :
3381 : /*
3382 : * We have to convert other encodings to UTF-8 first, then compare.
3383 : * Input strings may be not null-terminated and pg_server_to_any() may
3384 : * return them "as is". So, use strlen() only if there is real
3385 : * conversion.
3386 : */
3387 0 : utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
3388 0 : utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
3389 0 : utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
3390 0 : utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
3391 :
3392 0 : cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
3393 :
3394 : /*
3395 : * If pg_server_to_any() did no real conversion, then we actually
3396 : * compared original strings. So, we already done.
3397 : */
3398 0 : if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
3399 0 : return cmp;
3400 :
3401 : /* Free memory if needed */
3402 0 : if (mbstr1 != utf8str1)
3403 0 : pfree(utf8str1);
3404 0 : if (mbstr2 != utf8str2)
3405 0 : pfree(utf8str2);
3406 :
3407 : /*
3408 : * When all Unicode codepoints are equal, return result of binary
3409 : * comparison. In some edge cases, same characters may have different
3410 : * representations in encoding. Then our behavior could diverge from
3411 : * standard. However, that allow us to do simple binary comparison
3412 : * for "==" operator, which is performance critical in typical cases.
3413 : * In future to implement strict standard conformance, we can do
3414 : * normalization of input JSON strings.
3415 : */
3416 0 : if (cmp == 0)
3417 0 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3418 : else
3419 0 : return cmp;
3420 : }
3421 : }
3422 :
3423 : /*
3424 : * Compare two SQL/JSON items using comparison operation 'op'.
3425 : */
3426 : static JsonPathBool
3427 14593 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
3428 : {
3429 : int cmp;
3430 : bool res;
3431 :
3432 14593 : if (jb1->type != jb2->type)
3433 : {
3434 2096 : if (jb1->type == jbvNull || jb2->type == jbvNull)
3435 :
3436 : /*
3437 : * Equality and order comparison of nulls to non-nulls returns
3438 : * always false, but inequality comparison returns true.
3439 : */
3440 1918 : return op == jpiNotEqual ? jpbTrue : jpbFalse;
3441 :
3442 : /* Non-null items of different types are not comparable. */
3443 178 : return jpbUnknown;
3444 : }
3445 :
3446 12497 : switch (jb1->type)
3447 : {
3448 124 : case jbvNull:
3449 124 : cmp = 0;
3450 124 : break;
3451 580 : case jbvBool:
3452 844 : cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
3453 264 : jb1->val.boolean ? 1 : -1;
3454 580 : break;
3455 2749 : case jbvNumeric:
3456 2749 : cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
3457 2749 : break;
3458 6620 : case jbvString:
3459 6620 : if (op == jpiEqual)
3460 4316 : return jb1->val.string.len != jb2->val.string.len ||
3461 2388 : memcmp(jb1->val.string.val,
3462 2388 : jb2->val.string.val,
3463 4316 : jb1->val.string.len) ? jpbFalse : jpbTrue;
3464 :
3465 2304 : cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
3466 2304 : jb2->val.string.val, jb2->val.string.len);
3467 2304 : break;
3468 2416 : case jbvDatetime:
3469 : {
3470 : bool cast_error;
3471 :
3472 2416 : cmp = compareDatetime(jb1->val.datetime.value,
3473 : jb1->val.datetime.typid,
3474 : jb2->val.datetime.value,
3475 : jb2->val.datetime.typid,
3476 : useTz,
3477 : &cast_error);
3478 :
3479 2356 : if (cast_error)
3480 204 : return jpbUnknown;
3481 : }
3482 2152 : break;
3483 :
3484 8 : case jbvBinary:
3485 : case jbvArray:
3486 : case jbvObject:
3487 8 : return jpbUnknown; /* non-scalars are not comparable */
3488 :
3489 0 : default:
3490 0 : elog(ERROR, "invalid jsonb value type %d", jb1->type);
3491 : }
3492 :
3493 7909 : switch (op)
3494 : {
3495 1818 : case jpiEqual:
3496 1818 : res = (cmp == 0);
3497 1818 : break;
3498 4 : case jpiNotEqual:
3499 4 : res = (cmp != 0);
3500 4 : break;
3501 1418 : case jpiLess:
3502 1418 : res = (cmp < 0);
3503 1418 : break;
3504 1105 : case jpiGreater:
3505 1105 : res = (cmp > 0);
3506 1105 : break;
3507 1361 : case jpiLessOrEqual:
3508 1361 : res = (cmp <= 0);
3509 1361 : break;
3510 2203 : case jpiGreaterOrEqual:
3511 2203 : res = (cmp >= 0);
3512 2203 : break;
3513 0 : default:
3514 0 : elog(ERROR, "unrecognized jsonpath operation: %d", op);
3515 : return jpbUnknown;
3516 : }
3517 :
3518 7909 : return res ? jpbTrue : jpbFalse;
3519 : }
3520 :
3521 : /* Compare two numerics */
3522 : static int
3523 2749 : compareNumeric(Numeric a, Numeric b)
3524 : {
3525 2749 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
3526 : NumericGetDatum(a),
3527 : NumericGetDatum(b)));
3528 : }
3529 :
3530 : static JsonbValue *
3531 1248 : copyJsonbValue(JsonbValue *src)
3532 : {
3533 1248 : JsonbValue *dst = palloc_object(JsonbValue);
3534 :
3535 1248 : *dst = *src;
3536 :
3537 1248 : return dst;
3538 : }
3539 :
3540 : /*
3541 : * Execute array subscript expression and convert resulting numeric item to
3542 : * the integer type with truncation.
3543 : */
3544 : static JsonPathExecResult
3545 362 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
3546 : int32 *index)
3547 : {
3548 : JsonbValue *jbv;
3549 : JsonValueList found;
3550 : JsonPathExecResult res;
3551 : Datum numeric_index;
3552 362 : ErrorSaveContext escontext = {T_ErrorSaveContext};
3553 :
3554 362 : JsonValueListInit(&found);
3555 :
3556 362 : res = executeItem(cxt, jsp, jb, &found);
3557 :
3558 358 : if (jperIsError(res))
3559 : {
3560 0 : JsonValueListClear(&found);
3561 0 : return res;
3562 : }
3563 :
3564 708 : if (!JsonValueListIsSingleton(&found) ||
3565 350 : !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
3566 : {
3567 16 : JsonValueListClear(&found);
3568 16 : RETURN_ERROR(ereport(ERROR,
3569 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3570 : errmsg("jsonpath array subscript is not a single numeric value"))));
3571 : }
3572 :
3573 342 : numeric_index = DirectFunctionCall2(numeric_trunc,
3574 : NumericGetDatum(jbv->val.numeric),
3575 : Int32GetDatum(0));
3576 :
3577 342 : *index = numeric_int4_safe(DatumGetNumeric(numeric_index),
3578 : (Node *) &escontext);
3579 :
3580 342 : JsonValueListClear(&found);
3581 :
3582 342 : if (escontext.error_occurred)
3583 18 : RETURN_ERROR(ereport(ERROR,
3584 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3585 : errmsg("jsonpath array subscript is out of integer range"))));
3586 :
3587 324 : return jperOk;
3588 : }
3589 :
3590 : /* Save base object and its id needed for the execution of .keyvalue(). */
3591 : static JsonBaseObjectInfo
3592 145986 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
3593 : {
3594 145986 : JsonBaseObjectInfo baseObject = cxt->baseObject;
3595 :
3596 145986 : cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
3597 : (JsonbContainer *) jbv->val.binary.data;
3598 145986 : cxt->baseObject.id = id;
3599 :
3600 145986 : return baseObject;
3601 : }
3602 :
3603 : /*
3604 : * JsonValueList support functions
3605 : */
3606 :
3607 : static void
3608 235811 : JsonValueListInit(JsonValueList *jvl)
3609 : {
3610 235811 : jvl->nitems = 0;
3611 235811 : jvl->maxitems = BASE_JVL_ITEMS;
3612 235811 : jvl->next = NULL;
3613 235811 : jvl->last = jvl;
3614 235811 : }
3615 :
3616 : static void
3617 161461 : JsonValueListClear(JsonValueList *jvl)
3618 : {
3619 : JsonValueList *nxt;
3620 :
3621 : /* Release any extra chunks */
3622 161917 : for (JsonValueList *chunk = jvl->next; chunk != NULL; chunk = nxt)
3623 : {
3624 456 : nxt = chunk->next;
3625 456 : pfree(chunk);
3626 : }
3627 : /* ... and reset to empty */
3628 161461 : jvl->nitems = 0;
3629 : Assert(jvl->maxitems == BASE_JVL_ITEMS);
3630 161461 : jvl->next = NULL;
3631 161461 : jvl->last = jvl;
3632 161461 : }
3633 :
3634 : static void
3635 185186 : JsonValueListAppend(JsonValueList *jvl, const JsonbValue *jbv)
3636 : {
3637 185186 : JsonValueList *last = jvl->last;
3638 :
3639 185186 : if (last->nitems < last->maxitems)
3640 : {
3641 : /* there's still room in the last existing chunk */
3642 184326 : last->items[last->nitems] = *jbv;
3643 184326 : last->nitems++;
3644 : }
3645 : else
3646 : {
3647 : /* need a new last chunk */
3648 : JsonValueList *nxt;
3649 : int nxtsize;
3650 :
3651 860 : nxtsize = last->maxitems * 2; /* double the size with each chunk */
3652 860 : nxtsize = Max(nxtsize, MIN_EXTRA_JVL_ITEMS); /* but at least this */
3653 860 : nxt = palloc(offsetof(JsonValueList, items) +
3654 860 : nxtsize * sizeof(JsonbValue));
3655 860 : nxt->nitems = 1;
3656 860 : nxt->maxitems = nxtsize;
3657 860 : nxt->next = NULL;
3658 860 : nxt->items[0] = *jbv;
3659 860 : last->next = nxt;
3660 860 : jvl->last = nxt;
3661 : }
3662 185186 : }
3663 :
3664 : static bool
3665 7518 : JsonValueListIsEmpty(const JsonValueList *jvl)
3666 : {
3667 : /* We need not examine extra chunks for this */
3668 7518 : return (jvl->nitems == 0);
3669 : }
3670 :
3671 : static bool
3672 66476 : JsonValueListIsSingleton(const JsonValueList *jvl)
3673 : {
3674 : #if BASE_JVL_ITEMS > 1
3675 : /* We need not examine extra chunks in this case */
3676 66476 : return (jvl->nitems == 1);
3677 : #else
3678 : return (jvl->nitems == 1 && jvl->next == NULL);
3679 : #endif
3680 : }
3681 :
3682 : static bool
3683 2732 : JsonValueListHasMultipleItems(const JsonValueList *jvl)
3684 : {
3685 : #if BASE_JVL_ITEMS > 1
3686 : /* We need not examine extra chunks in this case */
3687 2732 : return (jvl->nitems > 1);
3688 : #else
3689 : return (jvl->nitems == 1 && jvl->next != NULL);
3690 : #endif
3691 : }
3692 :
3693 : static JsonbValue *
3694 71706 : JsonValueListHead(JsonValueList *jvl)
3695 : {
3696 : Assert(jvl->nitems > 0);
3697 71706 : return &jvl->items[0];
3698 : }
3699 :
3700 : /*
3701 : * JsonValueListIterator functions
3702 : */
3703 :
3704 : static void
3705 140580 : JsonValueListInitIterator(JsonValueList *jvl, JsonValueListIterator *it)
3706 : {
3707 140580 : it->chunk = jvl;
3708 140580 : it->nextitem = 0;
3709 140580 : }
3710 :
3711 : /*
3712 : * Get the next item from the sequence advancing iterator.
3713 : * Returns NULL if no more items.
3714 : */
3715 : static JsonbValue *
3716 222365 : JsonValueListNext(JsonValueListIterator *it)
3717 : {
3718 222365 : if (it->chunk == NULL)
3719 300 : return NULL;
3720 222065 : if (it->nextitem >= it->chunk->nitems)
3721 : {
3722 131627 : it->chunk = it->chunk->next;
3723 131627 : if (it->chunk == NULL)
3724 130892 : return NULL;
3725 735 : it->nextitem = 0;
3726 : Assert(it->chunk->nitems > 0);
3727 : }
3728 91173 : return &it->chunk->items[it->nextitem++];
3729 : }
3730 :
3731 : /*
3732 : * Initialize a binary JsonbValue with the given jsonb container.
3733 : */
3734 : static JsonbValue *
3735 132375 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
3736 : {
3737 132375 : jbv->type = jbvBinary;
3738 132375 : jbv->val.binary.data = &jb->root;
3739 132375 : jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
3740 :
3741 132375 : return jbv;
3742 : }
3743 :
3744 : /*
3745 : * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
3746 : */
3747 : static int
3748 190689 : JsonbType(JsonbValue *jb)
3749 : {
3750 190689 : int type = jb->type;
3751 :
3752 190689 : if (jb->type == jbvBinary)
3753 : {
3754 122333 : JsonbContainer *jbc = jb->val.binary.data;
3755 :
3756 : /* Scalars should be always extracted during jsonpath execution. */
3757 : Assert(!JsonContainerIsScalar(jbc));
3758 :
3759 122333 : if (JsonContainerIsObject(jbc))
3760 119846 : type = jbvObject;
3761 2487 : else if (JsonContainerIsArray(jbc))
3762 2487 : type = jbvArray;
3763 : else
3764 0 : elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
3765 : }
3766 :
3767 190689 : return type;
3768 : }
3769 :
3770 : /* Get scalar of given type or NULL on type mismatch */
3771 : static JsonbValue *
3772 7557 : getScalar(JsonbValue *scalar, enum jbvType type)
3773 : {
3774 : /* Scalars should be always extracted during jsonpath execution. */
3775 : Assert(scalar->type != jbvBinary ||
3776 : !JsonContainerIsScalar(scalar->val.binary.data));
3777 :
3778 7557 : return scalar->type == type ? scalar : NULL;
3779 : }
3780 :
3781 : /* Construct a JSON array from the item list */
3782 : static JsonbValue *
3783 300 : wrapItemsInArray(JsonValueList *items)
3784 : {
3785 300 : JsonbInState ps = {0};
3786 : JsonValueListIterator it;
3787 : JsonbValue *jbv;
3788 :
3789 300 : pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
3790 :
3791 300 : JsonValueListInitIterator(items, &it);
3792 790 : while ((jbv = JsonValueListNext(&it)))
3793 490 : pushJsonbValue(&ps, WJB_ELEM, jbv);
3794 :
3795 300 : pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
3796 :
3797 300 : return ps.result;
3798 : }
3799 :
3800 : /* Check if the timezone required for casting from type1 to type2 is used */
3801 : static void
3802 900 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
3803 : {
3804 900 : if (!useTz)
3805 192 : ereport(ERROR,
3806 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3807 : errmsg("cannot convert value from %s to %s without time zone usage",
3808 : type1, type2),
3809 : errhint("Use *_tz() function for time zone support.")));
3810 708 : }
3811 :
3812 : /* Convert time datum to timetz datum */
3813 : static Datum
3814 168 : castTimeToTimeTz(Datum time, bool useTz)
3815 : {
3816 168 : checkTimezoneIsUsedForCast(useTz, "time", "timetz");
3817 :
3818 144 : return DirectFunctionCall1(time_timetz, time);
3819 : }
3820 :
3821 : /*
3822 : * Compare date to timestamp.
3823 : * Note that this doesn't involve any timezone considerations.
3824 : */
3825 : static int
3826 124 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
3827 : {
3828 124 : return date_cmp_timestamp_internal(date1, ts2);
3829 : }
3830 :
3831 : /*
3832 : * Compare date to timestamptz.
3833 : */
3834 : static int
3835 108 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
3836 : {
3837 108 : checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
3838 :
3839 96 : return date_cmp_timestamptz_internal(date1, tstz2);
3840 : }
3841 :
3842 : /*
3843 : * Compare timestamp to timestamptz.
3844 : */
3845 : static int
3846 168 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
3847 : {
3848 168 : checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
3849 :
3850 144 : return timestamp_cmp_timestamptz_internal(ts1, tstz2);
3851 : }
3852 :
3853 : /*
3854 : * Cross-type comparison of two datetime SQL/JSON items. If items are
3855 : * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
3856 : * If the cast requires timezone and it is not used, then explicit error is thrown.
3857 : */
3858 : static int
3859 2416 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
3860 : bool useTz, bool *cast_error)
3861 : {
3862 : PGFunction cmpfunc;
3863 :
3864 2416 : *cast_error = false;
3865 :
3866 2416 : switch (typid1)
3867 : {
3868 376 : case DATEOID:
3869 376 : switch (typid2)
3870 : {
3871 252 : case DATEOID:
3872 252 : cmpfunc = date_cmp;
3873 :
3874 252 : break;
3875 :
3876 52 : case TIMESTAMPOID:
3877 52 : return cmpDateToTimestamp(DatumGetDateADT(val1),
3878 : DatumGetTimestamp(val2),
3879 : useTz);
3880 :
3881 48 : case TIMESTAMPTZOID:
3882 48 : return cmpDateToTimestampTz(DatumGetDateADT(val1),
3883 : DatumGetTimestampTz(val2),
3884 : useTz);
3885 :
3886 24 : case TIMEOID:
3887 : case TIMETZOID:
3888 24 : *cast_error = true; /* uncomparable types */
3889 24 : return 0;
3890 :
3891 0 : default:
3892 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3893 : typid2);
3894 : }
3895 252 : break;
3896 :
3897 416 : case TIMEOID:
3898 416 : switch (typid2)
3899 : {
3900 284 : case TIMEOID:
3901 284 : cmpfunc = time_cmp;
3902 :
3903 284 : break;
3904 :
3905 84 : case TIMETZOID:
3906 84 : val1 = castTimeToTimeTz(val1, useTz);
3907 72 : cmpfunc = timetz_cmp;
3908 :
3909 72 : break;
3910 :
3911 48 : case DATEOID:
3912 : case TIMESTAMPOID:
3913 : case TIMESTAMPTZOID:
3914 48 : *cast_error = true; /* uncomparable types */
3915 48 : return 0;
3916 :
3917 0 : default:
3918 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3919 : typid2);
3920 : }
3921 356 : break;
3922 :
3923 536 : case TIMETZOID:
3924 536 : switch (typid2)
3925 : {
3926 84 : case TIMEOID:
3927 84 : val2 = castTimeToTimeTz(val2, useTz);
3928 72 : cmpfunc = timetz_cmp;
3929 :
3930 72 : break;
3931 :
3932 404 : case TIMETZOID:
3933 404 : cmpfunc = timetz_cmp;
3934 :
3935 404 : break;
3936 :
3937 48 : case DATEOID:
3938 : case TIMESTAMPOID:
3939 : case TIMESTAMPTZOID:
3940 48 : *cast_error = true; /* uncomparable types */
3941 48 : return 0;
3942 :
3943 0 : default:
3944 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3945 : typid2);
3946 : }
3947 476 : break;
3948 :
3949 476 : case TIMESTAMPOID:
3950 476 : switch (typid2)
3951 : {
3952 72 : case DATEOID:
3953 72 : return -cmpDateToTimestamp(DatumGetDateADT(val2),
3954 : DatumGetTimestamp(val1),
3955 : useTz);
3956 :
3957 284 : case TIMESTAMPOID:
3958 284 : cmpfunc = timestamp_cmp;
3959 :
3960 284 : break;
3961 :
3962 84 : case TIMESTAMPTZOID:
3963 84 : return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
3964 : DatumGetTimestampTz(val2),
3965 : useTz);
3966 :
3967 36 : case TIMEOID:
3968 : case TIMETZOID:
3969 36 : *cast_error = true; /* uncomparable types */
3970 36 : return 0;
3971 :
3972 0 : default:
3973 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3974 : typid2);
3975 : }
3976 284 : break;
3977 :
3978 612 : case TIMESTAMPTZOID:
3979 612 : switch (typid2)
3980 : {
3981 60 : case DATEOID:
3982 60 : return -cmpDateToTimestampTz(DatumGetDateADT(val2),
3983 : DatumGetTimestampTz(val1),
3984 : useTz);
3985 :
3986 84 : case TIMESTAMPOID:
3987 84 : return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
3988 : DatumGetTimestampTz(val1),
3989 : useTz);
3990 :
3991 420 : case TIMESTAMPTZOID:
3992 420 : cmpfunc = timestamp_cmp;
3993 :
3994 420 : break;
3995 :
3996 48 : case TIMEOID:
3997 : case TIMETZOID:
3998 48 : *cast_error = true; /* uncomparable types */
3999 48 : return 0;
4000 :
4001 0 : default:
4002 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
4003 : typid2);
4004 : }
4005 420 : break;
4006 :
4007 0 : default:
4008 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
4009 : }
4010 :
4011 1788 : if (*cast_error)
4012 0 : return 0; /* cast error */
4013 :
4014 1788 : return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
4015 : }
4016 :
4017 : /*
4018 : * Executor-callable JSON_EXISTS implementation
4019 : *
4020 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4021 : * *error to true.
4022 : */
4023 : bool
4024 388 : JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
4025 : {
4026 : JsonPathExecResult res;
4027 :
4028 388 : res = executeJsonPath(jp, vars,
4029 : GetJsonPathVar, CountJsonPathVars,
4030 : DatumGetJsonbP(jb), !error, NULL, true);
4031 :
4032 : Assert(error || !jperIsError(res));
4033 :
4034 384 : if (error && jperIsError(res))
4035 104 : *error = true;
4036 :
4037 384 : return res == jperOk;
4038 : }
4039 :
4040 : /*
4041 : * Executor-callable JSON_QUERY implementation
4042 : *
4043 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4044 : * *error to true. *empty is set to true if no match is found.
4045 : */
4046 : Datum
4047 1640 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
4048 : bool *error, List *vars,
4049 : const char *column_name)
4050 : {
4051 : bool wrap;
4052 : JsonValueList found;
4053 : JsonPathExecResult res;
4054 :
4055 1640 : JsonValueListInit(&found);
4056 :
4057 1640 : res = executeJsonPath(jp, vars,
4058 : GetJsonPathVar, CountJsonPathVars,
4059 : DatumGetJsonbP(jb), !error, &found, true);
4060 : Assert(error || !jperIsError(res));
4061 1628 : if (error && jperIsError(res))
4062 : {
4063 20 : *error = true;
4064 20 : *empty = false;
4065 20 : return (Datum) 0;
4066 : }
4067 :
4068 : /*
4069 : * Determine whether to wrap the result in a JSON array or not.
4070 : *
4071 : * If the returned JsonValueList is empty, no wrapping is necessary.
4072 : *
4073 : * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
4074 : * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
4075 : * default when no WRAPPER clause is specified.
4076 : *
4077 : * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
4078 : * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
4079 : * UNCONDITIONAL WRAPPER clause.
4080 : *
4081 : * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
4082 : * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
4083 : */
4084 1608 : if (JsonValueListIsEmpty(&found))
4085 140 : wrap = false;
4086 1468 : else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
4087 1140 : wrap = false;
4088 328 : else if (wrapper == JSW_UNCONDITIONAL)
4089 212 : wrap = true;
4090 116 : else if (wrapper == JSW_CONDITIONAL)
4091 116 : wrap = JsonValueListHasMultipleItems(&found);
4092 : else
4093 : {
4094 0 : elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
4095 : wrap = false;
4096 : }
4097 :
4098 1608 : if (wrap)
4099 252 : return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
4100 :
4101 : /* No wrapping means at most one item is expected. */
4102 1356 : if (JsonValueListHasMultipleItems(&found))
4103 : {
4104 40 : if (error)
4105 : {
4106 32 : *error = true;
4107 32 : return (Datum) 0;
4108 : }
4109 :
4110 8 : if (column_name)
4111 4 : ereport(ERROR,
4112 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4113 : errmsg("JSON path expression for column \"%s\" must return single item when no wrapper is requested",
4114 : column_name),
4115 : errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
4116 : else
4117 4 : ereport(ERROR,
4118 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4119 : errmsg("JSON path expression in JSON_QUERY must return single item when no wrapper is requested"),
4120 : errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
4121 : }
4122 :
4123 1316 : if (!JsonValueListIsEmpty(&found))
4124 1176 : return JsonbPGetDatum(JsonbValueToJsonb(JsonValueListHead(&found)));
4125 :
4126 140 : *empty = true;
4127 140 : return PointerGetDatum(NULL);
4128 : }
4129 :
4130 : /*
4131 : * Executor-callable JSON_VALUE implementation
4132 : *
4133 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4134 : * *error to true. *empty is set to true if no match is found.
4135 : */
4136 : JsonbValue *
4137 1508 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
4138 : const char *column_name)
4139 : {
4140 : JsonbValue *res;
4141 : JsonValueList found;
4142 : JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
4143 :
4144 1508 : JsonValueListInit(&found);
4145 :
4146 1508 : jper = executeJsonPath(jp, vars, GetJsonPathVar, CountJsonPathVars,
4147 : DatumGetJsonbP(jb),
4148 : !error, &found, true);
4149 :
4150 : Assert(error || !jperIsError(jper));
4151 :
4152 1500 : if (error && jperIsError(jper))
4153 : {
4154 12 : *error = true;
4155 12 : *empty = false;
4156 12 : return NULL;
4157 : }
4158 :
4159 1488 : *empty = JsonValueListIsEmpty(&found);
4160 :
4161 1488 : if (*empty)
4162 228 : return NULL;
4163 :
4164 : /* JSON_VALUE expects to get only singletons. */
4165 1260 : if (JsonValueListHasMultipleItems(&found))
4166 : {
4167 12 : if (error)
4168 : {
4169 8 : *error = true;
4170 8 : return NULL;
4171 : }
4172 :
4173 4 : if (column_name)
4174 0 : ereport(ERROR,
4175 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4176 : errmsg("JSON path expression for column \"%s\" must return single scalar item",
4177 : column_name)));
4178 : else
4179 4 : ereport(ERROR,
4180 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4181 : errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
4182 : }
4183 :
4184 1248 : res = copyJsonbValue(JsonValueListHead(&found));
4185 1248 : if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
4186 0 : JsonbExtractScalar(res->val.binary.data, res);
4187 :
4188 : /* JSON_VALUE expects to get only scalars. */
4189 1248 : if (!IsAJsonbScalar(res))
4190 : {
4191 64 : if (error)
4192 : {
4193 56 : *error = true;
4194 56 : return NULL;
4195 : }
4196 :
4197 8 : if (column_name)
4198 0 : ereport(ERROR,
4199 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4200 : errmsg("JSON path expression for column \"%s\" must return single scalar item",
4201 : column_name)));
4202 : else
4203 8 : ereport(ERROR,
4204 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4205 : errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
4206 : }
4207 :
4208 1184 : if (res->type == jbvNull)
4209 48 : return NULL;
4210 :
4211 1136 : return res;
4212 : }
4213 :
4214 : /************************ JSON_TABLE functions ***************************/
4215 :
4216 : /*
4217 : * Sanity-checks and returns the opaque JsonTableExecContext from the
4218 : * given executor state struct.
4219 : */
4220 : static inline JsonTableExecContext *
4221 4860 : GetJsonTableExecContext(TableFuncScanState *state, const char *fname)
4222 : {
4223 : JsonTableExecContext *result;
4224 :
4225 4860 : if (!IsA(state, TableFuncScanState))
4226 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4227 4860 : result = (JsonTableExecContext *) state->opaque;
4228 4860 : if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
4229 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4230 :
4231 4860 : return result;
4232 : }
4233 :
4234 : /*
4235 : * JsonTableInitOpaque
4236 : * Fill in TableFuncScanState->opaque for processing JSON_TABLE
4237 : *
4238 : * This initializes the PASSING arguments and the JsonTablePlanState for
4239 : * JsonTablePlan given in TableFunc.
4240 : */
4241 : static void
4242 352 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
4243 : {
4244 : JsonTableExecContext *cxt;
4245 352 : PlanState *ps = &state->ss.ps;
4246 352 : TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
4247 352 : TableFunc *tf = tfs->tablefunc;
4248 352 : JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
4249 352 : JsonExpr *je = castNode(JsonExpr, tf->docexpr);
4250 352 : List *args = NIL;
4251 :
4252 352 : cxt = palloc0_object(JsonTableExecContext);
4253 352 : cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC;
4254 :
4255 : /*
4256 : * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
4257 : * executor via JsonPathVariables.
4258 : */
4259 352 : if (state->passingvalexprs)
4260 : {
4261 : ListCell *exprlc;
4262 : ListCell *namelc;
4263 :
4264 : Assert(list_length(state->passingvalexprs) ==
4265 : list_length(je->passing_names));
4266 248 : forboth(exprlc, state->passingvalexprs,
4267 : namelc, je->passing_names)
4268 : {
4269 164 : ExprState *state = lfirst_node(ExprState, exprlc);
4270 164 : String *name = lfirst_node(String, namelc);
4271 164 : JsonPathVariable *var = palloc_object(JsonPathVariable);
4272 :
4273 164 : var->name = pstrdup(name->sval);
4274 164 : var->namelen = strlen(var->name);
4275 164 : var->typid = exprType((Node *) state->expr);
4276 164 : var->typmod = exprTypmod((Node *) state->expr);
4277 :
4278 : /*
4279 : * Evaluate the expression and save the value to be returned by
4280 : * GetJsonPathVar().
4281 : */
4282 164 : var->value = ExecEvalExpr(state, ps->ps_ExprContext,
4283 : &var->isnull);
4284 :
4285 164 : args = lappend(args, var);
4286 : }
4287 : }
4288 :
4289 352 : cxt->colplanstates = palloc_array(JsonTablePlanState *, list_length(tf->colvalexprs));
4290 :
4291 : /*
4292 : * Initialize plan for the root path and, recursively, also any child
4293 : * plans that compute the NESTED paths.
4294 : */
4295 352 : cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
4296 : CurrentMemoryContext);
4297 :
4298 352 : state->opaque = cxt;
4299 352 : }
4300 :
4301 : /*
4302 : * JsonTableDestroyOpaque
4303 : * Resets state->opaque
4304 : */
4305 : static void
4306 352 : JsonTableDestroyOpaque(TableFuncScanState *state)
4307 : {
4308 : JsonTableExecContext *cxt =
4309 352 : GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
4310 :
4311 : /* not valid anymore */
4312 352 : cxt->magic = 0;
4313 :
4314 352 : state->opaque = NULL;
4315 352 : }
4316 :
4317 : /*
4318 : * JsonTableInitPlan
4319 : * Initialize information for evaluating jsonpath in the given
4320 : * JsonTablePlan and, recursively, in any child plans
4321 : */
4322 : static JsonTablePlanState *
4323 688 : JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan,
4324 : JsonTablePlanState *parentstate,
4325 : List *args, MemoryContext mcxt)
4326 : {
4327 688 : JsonTablePlanState *planstate = palloc0_object(JsonTablePlanState);
4328 :
4329 688 : planstate->plan = plan;
4330 688 : planstate->parent = parentstate;
4331 688 : JsonValueListInit(&planstate->found);
4332 :
4333 688 : if (IsA(plan, JsonTablePathScan))
4334 : {
4335 612 : JsonTablePathScan *scan = (JsonTablePathScan *) plan;
4336 : int i;
4337 :
4338 612 : planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
4339 612 : planstate->args = args;
4340 612 : planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
4341 : ALLOCSET_DEFAULT_SIZES);
4342 :
4343 : /* No row pattern evaluated yet. */
4344 612 : planstate->current.value = PointerGetDatum(NULL);
4345 612 : planstate->current.isnull = true;
4346 :
4347 1556 : for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
4348 944 : cxt->colplanstates[i] = planstate;
4349 :
4350 612 : planstate->nested = scan->child ?
4351 612 : JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
4352 : }
4353 76 : else if (IsA(plan, JsonTableSiblingJoin))
4354 : {
4355 76 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
4356 :
4357 76 : planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
4358 : args, mcxt);
4359 76 : planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
4360 : args, mcxt);
4361 : }
4362 :
4363 688 : return planstate;
4364 : }
4365 :
4366 : /*
4367 : * JsonTableSetDocument
4368 : * Install the input document and evaluate the row pattern
4369 : */
4370 : static void
4371 348 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
4372 : {
4373 : JsonTableExecContext *cxt =
4374 348 : GetJsonTableExecContext(state, "JsonTableSetDocument");
4375 :
4376 348 : JsonTableResetRowPattern(cxt->rootplanstate, value);
4377 344 : }
4378 :
4379 : /*
4380 : * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
4381 : * the given context item
4382 : */
4383 : static void
4384 668 : JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item)
4385 : {
4386 668 : JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
4387 : MemoryContext oldcxt;
4388 : JsonPathExecResult res;
4389 668 : Jsonb *js = (Jsonb *) DatumGetJsonbP(item);
4390 :
4391 668 : JsonValueListClear(&planstate->found);
4392 :
4393 668 : MemoryContextResetOnly(planstate->mcxt);
4394 :
4395 668 : oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4396 :
4397 668 : res = executeJsonPath(planstate->path, planstate->args,
4398 : GetJsonPathVar, CountJsonPathVars,
4399 668 : js, scan->errorOnError,
4400 : &planstate->found,
4401 : true);
4402 :
4403 664 : MemoryContextSwitchTo(oldcxt);
4404 :
4405 664 : if (jperIsError(res))
4406 : {
4407 : Assert(!scan->errorOnError);
4408 12 : JsonValueListClear(&planstate->found);
4409 : }
4410 :
4411 : /* Reset plan iterator to the beginning of the item list */
4412 664 : JsonValueListInitIterator(&planstate->found, &planstate->iter);
4413 664 : planstate->current.value = PointerGetDatum(NULL);
4414 664 : planstate->current.isnull = true;
4415 664 : planstate->ordinal = 0;
4416 664 : }
4417 :
4418 : /*
4419 : * Fetch next row from a JsonTablePlan.
4420 : *
4421 : * Returns false if the plan has run out of rows, true otherwise.
4422 : */
4423 : static bool
4424 2800 : JsonTablePlanNextRow(JsonTablePlanState *planstate)
4425 : {
4426 2800 : if (IsA(planstate->plan, JsonTablePathScan))
4427 2188 : return JsonTablePlanScanNextRow(planstate);
4428 612 : else if (IsA(planstate->plan, JsonTableSiblingJoin))
4429 612 : return JsonTablePlanJoinNextRow(planstate);
4430 : else
4431 0 : elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
4432 :
4433 : Assert(false);
4434 : /* Appease compiler */
4435 : return false;
4436 : }
4437 :
4438 : /*
4439 : * Fetch next row from a JsonTablePlan's path evaluation result and from
4440 : * any child nested path(s).
4441 : *
4442 : * Returns true if any of the paths (this or the nested) has more rows to
4443 : * return.
4444 : *
4445 : * By fetching the nested path(s)'s rows based on the parent row at each
4446 : * level, this essentially joins the rows of different levels. If a nested
4447 : * path at a given level has no matching rows, the columns of that level will
4448 : * compute to NULL, making it an OUTER join.
4449 : */
4450 : static bool
4451 2188 : JsonTablePlanScanNextRow(JsonTablePlanState *planstate)
4452 : {
4453 : JsonbValue *jbv;
4454 : MemoryContext oldcxt;
4455 :
4456 : /*
4457 : * If planstate already has an active row and there is a nested plan,
4458 : * check if it has an active row to join with the former.
4459 : */
4460 2188 : if (!planstate->current.isnull)
4461 : {
4462 1224 : if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
4463 324 : return true;
4464 : }
4465 :
4466 : /* Fetch new row from the list of found values to set as active. */
4467 1864 : jbv = JsonValueListNext(&planstate->iter);
4468 :
4469 : /* End of list? */
4470 1864 : if (jbv == NULL)
4471 : {
4472 904 : planstate->current.value = PointerGetDatum(NULL);
4473 904 : planstate->current.isnull = true;
4474 904 : return false;
4475 : }
4476 :
4477 : /*
4478 : * Set current row item for subsequent JsonTableGetValue() calls for
4479 : * evaluating individual columns.
4480 : */
4481 960 : oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4482 960 : planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
4483 960 : planstate->current.isnull = false;
4484 960 : MemoryContextSwitchTo(oldcxt);
4485 :
4486 : /* Next row! */
4487 960 : planstate->ordinal++;
4488 :
4489 : /* Process nested plan(s), if any. */
4490 960 : if (planstate->nested)
4491 : {
4492 : /* Re-evaluate the nested path using the above parent row. */
4493 232 : JsonTableResetNestedPlan(planstate->nested);
4494 :
4495 : /*
4496 : * Now fetch the nested plan's current row to be joined against the
4497 : * parent row. Any further nested plans' paths will be re-evaluated
4498 : * recursively, level at a time, after setting each nested plan's
4499 : * current row.
4500 : */
4501 232 : (void) JsonTablePlanNextRow(planstate->nested);
4502 : }
4503 :
4504 : /* There are more rows. */
4505 960 : return true;
4506 : }
4507 :
4508 : /*
4509 : * Re-evaluate the row pattern of a nested plan using the new parent row
4510 : * pattern.
4511 : */
4512 : static void
4513 408 : JsonTableResetNestedPlan(JsonTablePlanState *planstate)
4514 : {
4515 : /* This better be a child plan. */
4516 : Assert(planstate->parent != NULL);
4517 408 : if (IsA(planstate->plan, JsonTablePathScan))
4518 : {
4519 320 : JsonTablePlanState *parent = planstate->parent;
4520 :
4521 320 : if (!parent->current.isnull)
4522 320 : JsonTableResetRowPattern(planstate, parent->current.value);
4523 :
4524 : /*
4525 : * If this plan itself has a child nested plan, it will be reset when
4526 : * the caller calls JsonTablePlanNextRow() on this plan.
4527 : */
4528 : }
4529 88 : else if (IsA(planstate->plan, JsonTableSiblingJoin))
4530 : {
4531 88 : JsonTableResetNestedPlan(planstate->left);
4532 88 : JsonTableResetNestedPlan(planstate->right);
4533 : }
4534 408 : }
4535 :
4536 : /*
4537 : * Fetch the next row from a JsonTableSiblingJoin.
4538 : *
4539 : * This is essentially a UNION between the rows from left and right siblings.
4540 : */
4541 : static bool
4542 612 : JsonTablePlanJoinNextRow(JsonTablePlanState *planstate)
4543 : {
4544 :
4545 : /* Fetch row from left sibling. */
4546 612 : if (!JsonTablePlanNextRow(planstate->left))
4547 : {
4548 : /*
4549 : * Left sibling ran out of rows, so start fetching from the right
4550 : * sibling.
4551 : */
4552 364 : if (!JsonTablePlanNextRow(planstate->right))
4553 : {
4554 : /* Right sibling ran out of row, so there are more rows. */
4555 216 : return false;
4556 : }
4557 : }
4558 :
4559 396 : return true;
4560 : }
4561 :
4562 : /*
4563 : * JsonTableFetchRow
4564 : * Prepare the next "current" row for upcoming GetValue calls.
4565 : *
4566 : * Returns false if no more rows can be returned.
4567 : */
4568 : static bool
4569 1036 : JsonTableFetchRow(TableFuncScanState *state)
4570 : {
4571 : JsonTableExecContext *cxt =
4572 1036 : GetJsonTableExecContext(state, "JsonTableFetchRow");
4573 :
4574 1036 : return JsonTablePlanNextRow(cxt->rootplanstate);
4575 : }
4576 :
4577 : /*
4578 : * JsonTableGetValue
4579 : * Return the value for column number 'colnum' for the current row.
4580 : *
4581 : * This leaks memory, so be sure to reset often the context in which it's
4582 : * called.
4583 : */
4584 : static Datum
4585 3124 : JsonTableGetValue(TableFuncScanState *state, int colnum,
4586 : Oid typid, int32 typmod, bool *isnull)
4587 : {
4588 : JsonTableExecContext *cxt =
4589 3124 : GetJsonTableExecContext(state, "JsonTableGetValue");
4590 3124 : ExprContext *econtext = state->ss.ps.ps_ExprContext;
4591 3124 : ExprState *estate = list_nth(state->colvalexprs, colnum);
4592 3124 : JsonTablePlanState *planstate = cxt->colplanstates[colnum];
4593 3124 : JsonTablePlanRowSource *current = &planstate->current;
4594 : Datum result;
4595 :
4596 : /* Row pattern value is NULL */
4597 3124 : if (current->isnull)
4598 : {
4599 596 : result = (Datum) 0;
4600 596 : *isnull = true;
4601 : }
4602 : /* Evaluate JsonExpr. */
4603 2528 : else if (estate)
4604 : {
4605 2228 : Datum saved_caseValue = econtext->caseValue_datum;
4606 2228 : bool saved_caseIsNull = econtext->caseValue_isNull;
4607 :
4608 : /* Pass the row pattern value via CaseTestExpr. */
4609 2228 : econtext->caseValue_datum = current->value;
4610 2228 : econtext->caseValue_isNull = false;
4611 :
4612 2228 : result = ExecEvalExpr(estate, econtext, isnull);
4613 :
4614 2168 : econtext->caseValue_datum = saved_caseValue;
4615 2168 : econtext->caseValue_isNull = saved_caseIsNull;
4616 : }
4617 : /* ORDINAL column */
4618 : else
4619 : {
4620 300 : result = Int32GetDatum(planstate->ordinal);
4621 300 : *isnull = false;
4622 : }
4623 :
4624 3064 : return result;
4625 : }
|