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