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