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-2025, 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 198774 : 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 198774 : jspInit(&jsp, path);
688 :
689 198774 : if (!JsonbExtractScalar(&json->root, &jbv))
690 193038 : JsonbInitBinary(&jbv, json);
691 :
692 198774 : cxt.vars = vars;
693 198774 : cxt.getVar = getVar;
694 198774 : cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
695 198774 : cxt.ignoreStructuralErrors = cxt.laxMode;
696 198774 : cxt.root = &jbv;
697 198774 : cxt.current = &jbv;
698 198774 : cxt.baseObject.jbc = NULL;
699 198774 : cxt.baseObject.id = 0;
700 : /* 1 + number of base objects in vars */
701 198774 : cxt.lastGeneratedObjectId = 1 + countVars(vars);
702 198762 : cxt.innermostArraySize = -1;
703 198762 : cxt.throwErrors = throwErrors;
704 198762 : cxt.useTz = useTz;
705 :
706 198762 : 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 198528 : res = executeItem(&cxt, &jsp, &jbv, result);
723 :
724 : Assert(!throwErrors || !jperIsError(res));
725 :
726 197136 : return res;
727 : }
728 :
729 : /*
730 : * Execute jsonpath with automatic unwrapping of current item in lax mode.
731 : */
732 : static JsonPathExecResult
733 595252 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
734 : JsonbValue *jb, JsonValueList *found)
735 : {
736 595252 : 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 603436 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
746 : JsonbValue *jb, JsonValueList *found, bool unwrap)
747 : {
748 : JsonPathItem elem;
749 603436 : JsonPathExecResult res = jperNotFound;
750 : JsonBaseObjectInfo baseObject;
751 :
752 603436 : check_stack_depth();
753 603436 : CHECK_FOR_INTERRUPTS();
754 :
755 603436 : switch (jsp->type)
756 : {
757 60876 : case jpiNull:
758 : case jpiBool:
759 : case jpiNumeric:
760 : case jpiString:
761 : case jpiVariable:
762 : {
763 : JsonbValue vbuf;
764 : JsonbValue *v;
765 60876 : bool hasNext = jspGetNext(jsp, &elem);
766 :
767 60876 : 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 60864 : v = hasNext ? &vbuf : palloc(sizeof(*v));
778 :
779 60864 : baseObject = cxt->baseObject;
780 60864 : getJsonPathItem(cxt, jsp, v);
781 :
782 60816 : res = executeNextItem(cxt, jsp, &elem,
783 : v, found, hasNext);
784 60816 : cxt->baseObject = baseObject;
785 : }
786 60816 : 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 %s",
1166 : tmp, jspOperationName(jsp->type), "double precision"))));
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 %s",
1192 : tmp, jspOperationName(jsp->type), "double precision"))));
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 %s",
1280 : DatumGetCString(DirectFunctionCall1(numeric_out,
1281 : NumericGetDatum(jb->val.numeric))),
1282 : jspOperationName(jsp->type),
1283 : "bigint"))));
1284 :
1285 36 : datum = Int64GetDatum(val);
1286 36 : res = jperOk;
1287 : }
1288 126 : else if (jb->type == jbvString)
1289 : {
1290 : /* cast string as bigint */
1291 78 : char *tmp = pnstrdup(jb->val.string.val,
1292 78 : jb->val.string.len);
1293 78 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1294 : bool noerr;
1295 :
1296 78 : noerr = DirectInputFunctionCallSafe(int8in, tmp,
1297 : InvalidOid, -1,
1298 : (Node *) &escontext,
1299 : &datum);
1300 :
1301 78 : if (!noerr || escontext.error_occurred)
1302 54 : RETURN_ERROR(ereport(ERROR,
1303 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1304 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1305 : tmp, jspOperationName(jsp->type), "bigint"))));
1306 24 : res = jperOk;
1307 : }
1308 :
1309 108 : if (res == jperNotFound)
1310 48 : RETURN_ERROR(ereport(ERROR,
1311 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1312 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1313 : jspOperationName(jsp->type)))));
1314 :
1315 60 : jb = &jbv;
1316 60 : jb->type = jbvNumeric;
1317 60 : jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1318 : datum));
1319 :
1320 60 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1321 : }
1322 60 : break;
1323 :
1324 252 : case jpiBoolean:
1325 : {
1326 : JsonbValue jbv;
1327 : bool bval;
1328 :
1329 252 : if (unwrap && JsonbType(jb) == jbvArray)
1330 36 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1331 : false);
1332 :
1333 246 : if (jb->type == jbvBool)
1334 : {
1335 24 : bval = jb->val.boolean;
1336 :
1337 24 : res = jperOk;
1338 : }
1339 222 : else if (jb->type == jbvNumeric)
1340 : {
1341 : int ival;
1342 : Datum datum;
1343 : bool noerr;
1344 48 : char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1345 : NumericGetDatum(jb->val.numeric)));
1346 48 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1347 :
1348 48 : noerr = DirectInputFunctionCallSafe(int4in, tmp,
1349 : InvalidOid, -1,
1350 : (Node *) &escontext,
1351 : &datum);
1352 :
1353 48 : if (!noerr || escontext.error_occurred)
1354 12 : RETURN_ERROR(ereport(ERROR,
1355 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1356 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1357 : tmp, jspOperationName(jsp->type), "boolean"))));
1358 :
1359 36 : ival = DatumGetInt32(datum);
1360 36 : if (ival == 0)
1361 6 : bval = false;
1362 : else
1363 30 : bval = true;
1364 :
1365 36 : res = jperOk;
1366 : }
1367 174 : else if (jb->type == jbvString)
1368 : {
1369 : /* cast string as boolean */
1370 138 : char *tmp = pnstrdup(jb->val.string.val,
1371 138 : jb->val.string.len);
1372 :
1373 138 : if (!parse_bool(tmp, &bval))
1374 54 : RETURN_ERROR(ereport(ERROR,
1375 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1376 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1377 : tmp, jspOperationName(jsp->type), "boolean"))));
1378 :
1379 84 : res = jperOk;
1380 : }
1381 :
1382 180 : if (res == jperNotFound)
1383 36 : RETURN_ERROR(ereport(ERROR,
1384 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1385 : errmsg("jsonpath item method .%s() can only be applied to a boolean, string, or numeric value",
1386 : jspOperationName(jsp->type)))));
1387 :
1388 144 : jb = &jbv;
1389 144 : jb->type = jbvBool;
1390 144 : jb->val.boolean = bval;
1391 :
1392 144 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1393 : }
1394 144 : break;
1395 :
1396 426 : case jpiDecimal:
1397 : case jpiNumber:
1398 : {
1399 : JsonbValue jbv;
1400 : Numeric num;
1401 426 : char *numstr = NULL;
1402 :
1403 426 : if (unwrap && JsonbType(jb) == jbvArray)
1404 84 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1405 : false);
1406 :
1407 414 : if (jb->type == jbvNumeric)
1408 : {
1409 174 : num = jb->val.numeric;
1410 174 : if (numeric_is_nan(num) || numeric_is_inf(num))
1411 0 : RETURN_ERROR(ereport(ERROR,
1412 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1413 : errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1414 : jspOperationName(jsp->type)))));
1415 :
1416 174 : if (jsp->type == jpiDecimal)
1417 138 : numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
1418 : NumericGetDatum(num)));
1419 174 : res = jperOk;
1420 : }
1421 240 : else if (jb->type == jbvString)
1422 : {
1423 : /* cast string as number */
1424 : Datum datum;
1425 : bool noerr;
1426 144 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1427 :
1428 144 : numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
1429 :
1430 144 : noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1431 : InvalidOid, -1,
1432 : (Node *) &escontext,
1433 : &datum);
1434 :
1435 144 : if (!noerr || escontext.error_occurred)
1436 36 : RETURN_ERROR(ereport(ERROR,
1437 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1438 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1439 : numstr, jspOperationName(jsp->type), "numeric"))));
1440 :
1441 132 : num = DatumGetNumeric(datum);
1442 132 : if (numeric_is_nan(num) || numeric_is_inf(num))
1443 72 : RETURN_ERROR(ereport(ERROR,
1444 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1445 : errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1446 : jspOperationName(jsp->type)))));
1447 :
1448 60 : res = jperOk;
1449 : }
1450 :
1451 330 : if (res == jperNotFound)
1452 96 : RETURN_ERROR(ereport(ERROR,
1453 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1454 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1455 : jspOperationName(jsp->type)))));
1456 :
1457 : /*
1458 : * If we have arguments, then they must be the precision and
1459 : * optional scale used in .decimal(). Convert them to the
1460 : * typmod equivalent and then truncate the numeric value per
1461 : * this typmod details.
1462 : */
1463 234 : if (jsp->type == jpiDecimal && jsp->content.args.left)
1464 : {
1465 : Datum numdatum;
1466 : Datum dtypmod;
1467 : int32 precision;
1468 102 : int32 scale = 0;
1469 : bool have_error;
1470 : bool noerr;
1471 : ArrayType *arrtypmod;
1472 : Datum datums[2];
1473 : char pstr[12]; /* sign, 10 digits and '\0' */
1474 : char sstr[12]; /* sign, 10 digits and '\0' */
1475 102 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1476 :
1477 102 : jspGetLeftArg(jsp, &elem);
1478 102 : if (elem.type != jpiNumeric)
1479 0 : elog(ERROR, "invalid jsonpath item type for .decimal() precision");
1480 :
1481 102 : precision = numeric_int4_opt_error(jspGetNumeric(&elem),
1482 : &have_error);
1483 102 : if (have_error)
1484 6 : RETURN_ERROR(ereport(ERROR,
1485 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1486 : errmsg("precision of jsonpath item method .%s() is out of range for type integer",
1487 : jspOperationName(jsp->type)))));
1488 :
1489 96 : if (jsp->content.args.right)
1490 : {
1491 96 : jspGetRightArg(jsp, &elem);
1492 96 : if (elem.type != jpiNumeric)
1493 0 : elog(ERROR, "invalid jsonpath item type for .decimal() scale");
1494 :
1495 96 : scale = numeric_int4_opt_error(jspGetNumeric(&elem),
1496 : &have_error);
1497 96 : if (have_error)
1498 6 : RETURN_ERROR(ereport(ERROR,
1499 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1500 : errmsg("scale of jsonpath item method .%s() is out of range for type integer",
1501 : jspOperationName(jsp->type)))));
1502 : }
1503 :
1504 : /*
1505 : * numerictypmodin() takes the precision and scale in the
1506 : * form of CString arrays.
1507 : */
1508 90 : pg_ltoa(precision, pstr);
1509 90 : datums[0] = CStringGetDatum(pstr);
1510 90 : pg_ltoa(scale, sstr);
1511 90 : datums[1] = CStringGetDatum(sstr);
1512 90 : arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
1513 :
1514 90 : dtypmod = DirectFunctionCall1(numerictypmodin,
1515 : PointerGetDatum(arrtypmod));
1516 :
1517 : /* Convert numstr to Numeric with typmod */
1518 : Assert(numstr != NULL);
1519 60 : noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1520 : InvalidOid, dtypmod,
1521 : (Node *) &escontext,
1522 : &numdatum);
1523 :
1524 60 : if (!noerr || escontext.error_occurred)
1525 12 : RETURN_ERROR(ereport(ERROR,
1526 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1527 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1528 : numstr, jspOperationName(jsp->type), "numeric"))));
1529 :
1530 48 : num = DatumGetNumeric(numdatum);
1531 48 : pfree(arrtypmod);
1532 : }
1533 :
1534 180 : jb = &jbv;
1535 180 : jb->type = jbvNumeric;
1536 180 : jb->val.numeric = num;
1537 :
1538 180 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1539 : }
1540 180 : break;
1541 :
1542 168 : case jpiInteger:
1543 : {
1544 : JsonbValue jbv;
1545 : Datum datum;
1546 :
1547 168 : if (unwrap && JsonbType(jb) == jbvArray)
1548 42 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1549 : false);
1550 :
1551 162 : if (jb->type == jbvNumeric)
1552 : {
1553 : bool have_error;
1554 : int32 val;
1555 :
1556 42 : val = numeric_int4_opt_error(jb->val.numeric, &have_error);
1557 42 : if (have_error)
1558 12 : RETURN_ERROR(ereport(ERROR,
1559 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1560 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1561 : DatumGetCString(DirectFunctionCall1(numeric_out,
1562 : NumericGetDatum(jb->val.numeric))),
1563 : jspOperationName(jsp->type), "integer"))));
1564 :
1565 30 : datum = Int32GetDatum(val);
1566 30 : res = jperOk;
1567 : }
1568 120 : else if (jb->type == jbvString)
1569 : {
1570 : /* cast string as integer */
1571 72 : char *tmp = pnstrdup(jb->val.string.val,
1572 72 : jb->val.string.len);
1573 72 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1574 : bool noerr;
1575 :
1576 72 : noerr = DirectInputFunctionCallSafe(int4in, tmp,
1577 : InvalidOid, -1,
1578 : (Node *) &escontext,
1579 : &datum);
1580 :
1581 72 : if (!noerr || escontext.error_occurred)
1582 54 : RETURN_ERROR(ereport(ERROR,
1583 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1584 : errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1585 : tmp, jspOperationName(jsp->type), "integer"))));
1586 18 : res = jperOk;
1587 : }
1588 :
1589 96 : if (res == jperNotFound)
1590 48 : RETURN_ERROR(ereport(ERROR,
1591 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1592 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1593 : jspOperationName(jsp->type)))));
1594 :
1595 48 : jb = &jbv;
1596 48 : jb->type = jbvNumeric;
1597 48 : jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1598 : datum));
1599 :
1600 48 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1601 : }
1602 48 : break;
1603 :
1604 192 : case jpiStringFunc:
1605 : {
1606 : JsonbValue jbv;
1607 192 : char *tmp = NULL;
1608 :
1609 192 : if (unwrap && JsonbType(jb) == jbvArray)
1610 30 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1611 :
1612 180 : switch (JsonbType(jb))
1613 : {
1614 24 : case jbvString:
1615 :
1616 : /*
1617 : * Value is not necessarily null-terminated, so we do
1618 : * pnstrdup() here.
1619 : */
1620 24 : tmp = pnstrdup(jb->val.string.val,
1621 24 : jb->val.string.len);
1622 24 : break;
1623 36 : case jbvNumeric:
1624 36 : tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1625 : NumericGetDatum(jb->val.numeric)));
1626 36 : break;
1627 24 : case jbvBool:
1628 24 : tmp = (jb->val.boolean) ? "true" : "false";
1629 24 : break;
1630 60 : case jbvDatetime:
1631 : {
1632 : char buf[MAXDATELEN + 1];
1633 :
1634 60 : JsonEncodeDateTime(buf,
1635 : jb->val.datetime.value,
1636 : jb->val.datetime.typid,
1637 60 : &jb->val.datetime.tz);
1638 60 : tmp = pstrdup(buf);
1639 : }
1640 60 : break;
1641 36 : case jbvNull:
1642 : case jbvArray:
1643 : case jbvObject:
1644 : case jbvBinary:
1645 36 : RETURN_ERROR(ereport(ERROR,
1646 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1647 : errmsg("jsonpath item method .%s() can only be applied to a boolean, string, numeric, or datetime value",
1648 : jspOperationName(jsp->type)))));
1649 : break;
1650 : }
1651 :
1652 144 : jb = &jbv;
1653 : Assert(tmp != NULL); /* We must have set tmp above */
1654 144 : jb->val.string.val = tmp;
1655 144 : jb->val.string.len = strlen(jb->val.string.val);
1656 144 : jb->type = jbvString;
1657 :
1658 144 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1659 : }
1660 144 : break;
1661 :
1662 0 : default:
1663 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1664 : }
1665 :
1666 590422 : return res;
1667 : }
1668 :
1669 : /*
1670 : * Unwrap current array item and execute jsonpath for each of its elements.
1671 : */
1672 : static JsonPathExecResult
1673 3026 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1674 : JsonbValue *jb, JsonValueList *found,
1675 : bool unwrapElements)
1676 : {
1677 3026 : if (jb->type != jbvBinary)
1678 : {
1679 : Assert(jb->type != jbvArray);
1680 0 : elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1681 : }
1682 :
1683 3026 : return executeAnyItem
1684 : (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1685 : false, unwrapElements);
1686 : }
1687 :
1688 : /*
1689 : * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1690 : * list if provided.
1691 : */
1692 : static JsonPathExecResult
1693 439352 : executeNextItem(JsonPathExecContext *cxt,
1694 : JsonPathItem *cur, JsonPathItem *next,
1695 : JsonbValue *v, JsonValueList *found, bool copy)
1696 : {
1697 : JsonPathItem elem;
1698 : bool hasNext;
1699 :
1700 439352 : if (!cur)
1701 0 : hasNext = next != NULL;
1702 439352 : else if (next)
1703 172350 : hasNext = jspHasNext(cur);
1704 : else
1705 : {
1706 267002 : next = &elem;
1707 267002 : hasNext = jspGetNext(cur, next);
1708 : }
1709 :
1710 439352 : if (hasNext)
1711 196420 : return executeItem(cxt, next, v, found);
1712 :
1713 242932 : if (found)
1714 192790 : JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1715 :
1716 242932 : return jperOk;
1717 : }
1718 :
1719 : /*
1720 : * Same as executeItem(), but when "unwrap == true" automatically unwraps
1721 : * each array item from the resulting sequence in lax mode.
1722 : */
1723 : static JsonPathExecResult
1724 199554 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1725 : JsonbValue *jb, bool unwrap,
1726 : JsonValueList *found)
1727 : {
1728 199554 : if (unwrap && jspAutoUnwrap(cxt))
1729 : {
1730 119088 : JsonValueList seq = {0};
1731 : JsonValueListIterator it;
1732 119088 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1733 : JsonbValue *item;
1734 :
1735 119064 : if (jperIsError(res))
1736 66 : return res;
1737 :
1738 118998 : JsonValueListInitIterator(&seq, &it);
1739 200196 : while ((item = JsonValueListNext(&seq, &it)))
1740 : {
1741 : Assert(item->type != jbvArray);
1742 :
1743 81198 : if (JsonbType(item) == jbvArray)
1744 54 : executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1745 : else
1746 81144 : JsonValueListAppend(found, item);
1747 : }
1748 :
1749 118998 : return jperOk;
1750 : }
1751 :
1752 80466 : return executeItem(cxt, jsp, jb, found);
1753 : }
1754 :
1755 : /*
1756 : * Same as executeItemOptUnwrapResult(), but with error suppression.
1757 : */
1758 : static JsonPathExecResult
1759 198066 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1760 : JsonPathItem *jsp,
1761 : JsonbValue *jb, bool unwrap,
1762 : JsonValueList *found)
1763 : {
1764 : JsonPathExecResult res;
1765 198066 : bool throwErrors = cxt->throwErrors;
1766 :
1767 198066 : cxt->throwErrors = false;
1768 198066 : res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1769 198060 : cxt->throwErrors = throwErrors;
1770 :
1771 198060 : return res;
1772 : }
1773 :
1774 : /* Execute boolean-valued jsonpath expression. */
1775 : static JsonPathBool
1776 178554 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1777 : JsonbValue *jb, bool canHaveNext)
1778 : {
1779 : JsonPathItem larg;
1780 : JsonPathItem rarg;
1781 : JsonPathBool res;
1782 : JsonPathBool res2;
1783 :
1784 : /* since this function recurses, it could be driven to stack overflow */
1785 178554 : check_stack_depth();
1786 :
1787 178554 : if (!canHaveNext && jspHasNext(jsp))
1788 0 : elog(ERROR, "boolean jsonpath item cannot have next item");
1789 :
1790 178554 : switch (jsp->type)
1791 : {
1792 26448 : case jpiAnd:
1793 26448 : jspGetLeftArg(jsp, &larg);
1794 26448 : res = executeBoolItem(cxt, &larg, jb, false);
1795 :
1796 26448 : if (res == jpbFalse)
1797 22800 : return jpbFalse;
1798 :
1799 : /*
1800 : * SQL/JSON says that we should check second arg in case of
1801 : * jperError
1802 : */
1803 :
1804 3648 : jspGetRightArg(jsp, &rarg);
1805 3648 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1806 :
1807 3648 : return res2 == jpbTrue ? res : res2;
1808 :
1809 12954 : case jpiOr:
1810 12954 : jspGetLeftArg(jsp, &larg);
1811 12954 : res = executeBoolItem(cxt, &larg, jb, false);
1812 :
1813 12954 : if (res == jpbTrue)
1814 2490 : return jpbTrue;
1815 :
1816 10464 : jspGetRightArg(jsp, &rarg);
1817 10464 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1818 :
1819 10464 : return res2 == jpbFalse ? res : res2;
1820 :
1821 108 : case jpiNot:
1822 108 : jspGetArg(jsp, &larg);
1823 :
1824 108 : res = executeBoolItem(cxt, &larg, jb, false);
1825 :
1826 108 : if (res == jpbUnknown)
1827 36 : return jpbUnknown;
1828 :
1829 72 : return res == jpbTrue ? jpbFalse : jpbTrue;
1830 :
1831 210 : case jpiIsUnknown:
1832 210 : jspGetArg(jsp, &larg);
1833 210 : res = executeBoolItem(cxt, &larg, jb, false);
1834 210 : return res == jpbUnknown ? jpbTrue : jpbFalse;
1835 :
1836 59166 : case jpiEqual:
1837 : case jpiNotEqual:
1838 : case jpiLess:
1839 : case jpiGreater:
1840 : case jpiLessOrEqual:
1841 : case jpiGreaterOrEqual:
1842 59166 : jspGetLeftArg(jsp, &larg);
1843 59166 : jspGetRightArg(jsp, &rarg);
1844 59166 : return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1845 : executeComparison, cxt);
1846 :
1847 84 : case jpiStartsWith: /* 'whole STARTS WITH initial' */
1848 84 : jspGetLeftArg(jsp, &larg); /* 'whole' */
1849 84 : jspGetRightArg(jsp, &rarg); /* 'initial' */
1850 84 : return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1851 : executeStartsWith, NULL);
1852 :
1853 396 : case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1854 : {
1855 : /*
1856 : * 'expr' is a sequence-returning expression. 'pattern' is a
1857 : * regex string literal. SQL/JSON standard requires XQuery
1858 : * regexes, but we use Postgres regexes here. 'flags' is a
1859 : * string literal converted to integer flags at compile-time.
1860 : */
1861 396 : JsonLikeRegexContext lrcxt = {0};
1862 :
1863 396 : jspInitByBuffer(&larg, jsp->base,
1864 : jsp->content.like_regex.expr);
1865 :
1866 396 : return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1867 : executeLikeRegex, &lrcxt);
1868 : }
1869 :
1870 79188 : case jpiExists:
1871 79188 : jspGetArg(jsp, &larg);
1872 :
1873 79188 : if (jspStrictAbsenceOfErrors(cxt))
1874 : {
1875 : /*
1876 : * In strict mode we must get a complete list of values to
1877 : * check that there are no errors at all.
1878 : */
1879 48 : JsonValueList vals = {0};
1880 : JsonPathExecResult res =
1881 48 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1882 : false, &vals);
1883 :
1884 48 : if (jperIsError(res))
1885 36 : return jpbUnknown;
1886 :
1887 12 : return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1888 : }
1889 : else
1890 : {
1891 : JsonPathExecResult res =
1892 79140 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1893 : false, NULL);
1894 :
1895 79140 : if (jperIsError(res))
1896 24 : return jpbUnknown;
1897 :
1898 79116 : return res == jperOk ? jpbTrue : jpbFalse;
1899 : }
1900 :
1901 0 : default:
1902 0 : elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1903 : return jpbUnknown;
1904 : }
1905 : }
1906 :
1907 : /*
1908 : * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1909 : * item onto the stack.
1910 : */
1911 : static JsonPathBool
1912 22548 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1913 : JsonbValue *jb)
1914 : {
1915 : JsonbValue *prev;
1916 : JsonPathBool res;
1917 :
1918 22548 : prev = cxt->current;
1919 22548 : cxt->current = jb;
1920 22548 : res = executeBoolItem(cxt, jsp, jb, false);
1921 22452 : cxt->current = prev;
1922 :
1923 22452 : return res;
1924 : }
1925 :
1926 : /*
1927 : * Implementation of several jsonpath nodes:
1928 : * - jpiAny (.** accessor),
1929 : * - jpiAnyKey (.* accessor),
1930 : * - jpiAnyArray ([*] accessor)
1931 : */
1932 : static JsonPathExecResult
1933 3644 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1934 : JsonValueList *found, uint32 level, uint32 first, uint32 last,
1935 : bool ignoreStructuralErrors, bool unwrapNext)
1936 : {
1937 3644 : JsonPathExecResult res = jperNotFound;
1938 : JsonbIterator *it;
1939 : int32 r;
1940 : JsonbValue v;
1941 :
1942 3644 : check_stack_depth();
1943 :
1944 3644 : if (level > last)
1945 30 : return res;
1946 :
1947 3614 : it = JsonbIteratorInit(jbc);
1948 :
1949 : /*
1950 : * Recursively iterate over jsonb objects/arrays
1951 : */
1952 20618 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1953 : {
1954 17730 : if (r == WJB_KEY)
1955 : {
1956 660 : r = JsonbIteratorNext(&it, &v, true);
1957 : Assert(r == WJB_VALUE);
1958 : }
1959 :
1960 17730 : if (r == WJB_VALUE || r == WJB_ELEM)
1961 : {
1962 :
1963 11228 : if (level >= first ||
1964 12 : (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1965 12 : v.type != jbvBinary)) /* leaves only requested */
1966 : {
1967 : /* check expression */
1968 11168 : if (jsp)
1969 : {
1970 8184 : if (ignoreStructuralErrors)
1971 : {
1972 : bool savedIgnoreStructuralErrors;
1973 :
1974 348 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1975 348 : cxt->ignoreStructuralErrors = true;
1976 348 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1977 348 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1978 : }
1979 : else
1980 7836 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1981 :
1982 7944 : if (jperIsError(res))
1983 66 : break;
1984 :
1985 7878 : if (res == jperOk && !found)
1986 348 : break;
1987 : }
1988 2984 : else if (found)
1989 2948 : JsonValueListAppend(found, copyJsonbValue(&v));
1990 : else
1991 36 : return jperOk;
1992 : }
1993 :
1994 10538 : if (level < last && v.type == jbvBinary)
1995 : {
1996 186 : res = executeAnyItem
1997 : (cxt, jsp, v.val.binary.data, found,
1998 : level + 1, first, last,
1999 : ignoreStructuralErrors, unwrapNext);
2000 :
2001 186 : if (jperIsError(res))
2002 0 : break;
2003 :
2004 186 : if (res == jperOk && found == NULL)
2005 36 : break;
2006 : }
2007 : }
2008 : }
2009 :
2010 3338 : return res;
2011 : }
2012 :
2013 : /*
2014 : * Execute unary or binary predicate.
2015 : *
2016 : * Predicates have existence semantics, because their operands are item
2017 : * sequences. Pairs of items from the left and right operand's sequences are
2018 : * checked. TRUE returned only if any pair satisfying the condition is found.
2019 : * In strict mode, even if the desired pair has already been found, all pairs
2020 : * still need to be examined to check the absence of errors. If any error
2021 : * occurs, UNKNOWN (analogous to SQL NULL) is returned.
2022 : */
2023 : static JsonPathBool
2024 59646 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
2025 : JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
2026 : bool unwrapRightArg, JsonPathPredicateCallback exec,
2027 : void *param)
2028 : {
2029 : JsonPathExecResult res;
2030 : JsonValueListIterator lseqit;
2031 59646 : JsonValueList lseq = {0};
2032 59646 : JsonValueList rseq = {0};
2033 : JsonbValue *lval;
2034 59646 : bool error = false;
2035 59646 : bool found = false;
2036 :
2037 : /* Left argument is always auto-unwrapped. */
2038 59646 : res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
2039 59646 : if (jperIsError(res))
2040 18 : return jpbUnknown;
2041 :
2042 59628 : if (rarg)
2043 : {
2044 : /* Right argument is conditionally auto-unwrapped. */
2045 59232 : res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
2046 : unwrapRightArg, &rseq);
2047 59226 : if (jperIsError(res))
2048 54 : return jpbUnknown;
2049 : }
2050 :
2051 59568 : JsonValueListInitIterator(&lseq, &lseqit);
2052 79806 : while ((lval = JsonValueListNext(&lseq, &lseqit)))
2053 : {
2054 : JsonValueListIterator rseqit;
2055 : JsonbValue *rval;
2056 27204 : bool first = true;
2057 :
2058 27204 : JsonValueListInitIterator(&rseq, &rseqit);
2059 27204 : if (rarg)
2060 26808 : rval = JsonValueListNext(&rseq, &rseqit);
2061 : else
2062 396 : rval = NULL;
2063 :
2064 : /* Loop over right arg sequence or do single pass otherwise */
2065 42498 : while (rarg ? (rval != NULL) : first)
2066 : {
2067 22260 : JsonPathBool res = exec(pred, lval, rval, param);
2068 :
2069 22170 : if (res == jpbUnknown)
2070 : {
2071 744 : if (jspStrictAbsenceOfErrors(cxt))
2072 6876 : return jpbUnknown;
2073 :
2074 720 : error = true;
2075 : }
2076 21426 : else if (res == jpbTrue)
2077 : {
2078 7164 : if (!jspStrictAbsenceOfErrors(cxt))
2079 6852 : return jpbTrue;
2080 :
2081 312 : found = true;
2082 : }
2083 :
2084 15294 : first = false;
2085 15294 : if (rarg)
2086 15000 : rval = JsonValueListNext(&rseq, &rseqit);
2087 : }
2088 : }
2089 :
2090 52602 : if (found) /* possible only in strict mode */
2091 210 : return jpbTrue;
2092 :
2093 52392 : if (error) /* possible only in lax mode */
2094 690 : return jpbUnknown;
2095 :
2096 51702 : return jpbFalse;
2097 : }
2098 :
2099 : /*
2100 : * Execute binary arithmetic expression on singleton numeric operands.
2101 : * Array operands are automatically unwrapped in lax mode.
2102 : */
2103 : static JsonPathExecResult
2104 654 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
2105 : JsonbValue *jb, BinaryArithmFunc func,
2106 : JsonValueList *found)
2107 : {
2108 : JsonPathExecResult jper;
2109 : JsonPathItem elem;
2110 654 : JsonValueList lseq = {0};
2111 654 : JsonValueList rseq = {0};
2112 : JsonbValue *lval;
2113 : JsonbValue *rval;
2114 : Numeric res;
2115 :
2116 654 : jspGetLeftArg(jsp, &elem);
2117 :
2118 : /*
2119 : * XXX: By standard only operands of multiplicative expressions are
2120 : * unwrapped. We extend it to other binary arithmetic expressions too.
2121 : */
2122 654 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
2123 648 : if (jperIsError(jper))
2124 0 : return jper;
2125 :
2126 648 : jspGetRightArg(jsp, &elem);
2127 :
2128 648 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
2129 642 : if (jperIsError(jper))
2130 0 : return jper;
2131 :
2132 1218 : if (JsonValueListLength(&lseq) != 1 ||
2133 576 : !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
2134 66 : RETURN_ERROR(ereport(ERROR,
2135 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2136 : errmsg("left operand of jsonpath operator %s is not a single numeric value",
2137 : jspOperationName(jsp->type)))));
2138 :
2139 1122 : if (JsonValueListLength(&rseq) != 1 ||
2140 546 : !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
2141 54 : RETURN_ERROR(ereport(ERROR,
2142 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2143 : errmsg("right operand of jsonpath operator %s is not a single numeric value",
2144 : jspOperationName(jsp->type)))));
2145 :
2146 522 : if (jspThrowErrors(cxt))
2147 : {
2148 102 : res = func(lval->val.numeric, rval->val.numeric, NULL);
2149 : }
2150 : else
2151 : {
2152 420 : bool error = false;
2153 :
2154 420 : res = func(lval->val.numeric, rval->val.numeric, &error);
2155 :
2156 420 : if (error)
2157 12 : return jperError;
2158 : }
2159 :
2160 486 : if (!jspGetNext(jsp, &elem) && !found)
2161 6 : return jperOk;
2162 :
2163 480 : lval = palloc(sizeof(*lval));
2164 480 : lval->type = jbvNumeric;
2165 480 : lval->val.numeric = res;
2166 :
2167 480 : return executeNextItem(cxt, jsp, &elem, lval, found, false);
2168 : }
2169 :
2170 : /*
2171 : * Execute unary arithmetic expression for each numeric item in its operand's
2172 : * sequence. Array operand is automatically unwrapped in lax mode.
2173 : */
2174 : static JsonPathExecResult
2175 186 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
2176 : JsonbValue *jb, PGFunction func, JsonValueList *found)
2177 : {
2178 : JsonPathExecResult jper;
2179 : JsonPathExecResult jper2;
2180 : JsonPathItem elem;
2181 186 : JsonValueList seq = {0};
2182 : JsonValueListIterator it;
2183 : JsonbValue *val;
2184 : bool hasNext;
2185 :
2186 186 : jspGetArg(jsp, &elem);
2187 186 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
2188 :
2189 180 : if (jperIsError(jper))
2190 0 : return jper;
2191 :
2192 180 : jper = jperNotFound;
2193 :
2194 180 : hasNext = jspGetNext(jsp, &elem);
2195 :
2196 180 : JsonValueListInitIterator(&seq, &it);
2197 324 : while ((val = JsonValueListNext(&seq, &it)))
2198 : {
2199 192 : if ((val = getScalar(val, jbvNumeric)))
2200 : {
2201 150 : if (!found && !hasNext)
2202 12 : return jperOk;
2203 : }
2204 : else
2205 : {
2206 42 : if (!found && !hasNext)
2207 6 : continue; /* skip non-numerics processing */
2208 :
2209 36 : RETURN_ERROR(ereport(ERROR,
2210 : (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
2211 : errmsg("operand of unary jsonpath operator %s is not a numeric value",
2212 : jspOperationName(jsp->type)))));
2213 : }
2214 :
2215 138 : if (func)
2216 84 : val->val.numeric =
2217 84 : DatumGetNumeric(DirectFunctionCall1(func,
2218 : NumericGetDatum(val->val.numeric)));
2219 :
2220 138 : jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
2221 :
2222 138 : if (jperIsError(jper2))
2223 0 : return jper2;
2224 :
2225 138 : if (jper2 == jperOk)
2226 : {
2227 138 : if (!found)
2228 0 : return jperOk;
2229 138 : jper = jperOk;
2230 : }
2231 : }
2232 :
2233 132 : return jper;
2234 : }
2235 :
2236 : /*
2237 : * STARTS_WITH predicate callback.
2238 : *
2239 : * Check if the 'whole' string starts from 'initial' string.
2240 : */
2241 : static JsonPathBool
2242 174 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
2243 : void *param)
2244 : {
2245 174 : if (!(whole = getScalar(whole, jbvString)))
2246 48 : return jpbUnknown; /* error */
2247 :
2248 126 : if (!(initial = getScalar(initial, jbvString)))
2249 0 : return jpbUnknown; /* error */
2250 :
2251 126 : if (whole->val.string.len >= initial->val.string.len &&
2252 90 : !memcmp(whole->val.string.val,
2253 90 : initial->val.string.val,
2254 90 : initial->val.string.len))
2255 54 : return jpbTrue;
2256 :
2257 72 : return jpbFalse;
2258 : }
2259 :
2260 : /*
2261 : * LIKE_REGEX predicate callback.
2262 : *
2263 : * Check if the string matches regex pattern.
2264 : */
2265 : static JsonPathBool
2266 396 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
2267 : void *param)
2268 : {
2269 396 : JsonLikeRegexContext *cxt = param;
2270 :
2271 396 : if (!(str = getScalar(str, jbvString)))
2272 120 : return jpbUnknown;
2273 :
2274 : /* Cache regex text and converted flags. */
2275 276 : if (!cxt->regex)
2276 : {
2277 276 : cxt->regex =
2278 276 : cstring_to_text_with_len(jsp->content.like_regex.pattern,
2279 : jsp->content.like_regex.patternlen);
2280 276 : (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
2281 : &(cxt->cflags), NULL);
2282 : }
2283 :
2284 276 : if (RE_compile_and_execute(cxt->regex, str->val.string.val,
2285 : str->val.string.len,
2286 : cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
2287 102 : return jpbTrue;
2288 :
2289 174 : return jpbFalse;
2290 : }
2291 :
2292 : /*
2293 : * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
2294 : * user function 'func'.
2295 : */
2296 : static JsonPathExecResult
2297 258 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2298 : JsonbValue *jb, bool unwrap, PGFunction func,
2299 : JsonValueList *found)
2300 : {
2301 : JsonPathItem next;
2302 : Datum datum;
2303 :
2304 258 : if (unwrap && JsonbType(jb) == jbvArray)
2305 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
2306 :
2307 258 : if (!(jb = getScalar(jb, jbvNumeric)))
2308 36 : RETURN_ERROR(ereport(ERROR,
2309 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
2310 : errmsg("jsonpath item method .%s() can only be applied to a numeric value",
2311 : jspOperationName(jsp->type)))));
2312 :
2313 222 : datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
2314 :
2315 222 : if (!jspGetNext(jsp, &next) && !found)
2316 0 : return jperOk;
2317 :
2318 222 : jb = palloc(sizeof(*jb));
2319 222 : jb->type = jbvNumeric;
2320 222 : jb->val.numeric = DatumGetNumeric(datum);
2321 :
2322 222 : return executeNextItem(cxt, jsp, &next, jb, found, false);
2323 : }
2324 :
2325 : /*
2326 : * Implementation of the .datetime() and related methods.
2327 : *
2328 : * Converts a string into a date/time value. The actual type is determined at
2329 : * run time.
2330 : * If an argument is provided, this argument is used as a template string.
2331 : * Otherwise, the first fitting ISO format is selected.
2332 : *
2333 : * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
2334 : * have a format, so ISO format is used. However, except for .date(), they all
2335 : * take an optional time precision.
2336 : */
2337 : static JsonPathExecResult
2338 8484 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2339 : JsonbValue *jb, JsonValueList *found)
2340 : {
2341 : JsonbValue jbvbuf;
2342 : Datum value;
2343 : text *datetime;
2344 : Oid collid;
2345 : Oid typid;
2346 8484 : int32 typmod = -1;
2347 8484 : int tz = 0;
2348 : bool hasNext;
2349 8484 : JsonPathExecResult res = jperNotFound;
2350 : JsonPathItem elem;
2351 8484 : int32 time_precision = -1;
2352 :
2353 8484 : if (!(jb = getScalar(jb, jbvString)))
2354 180 : RETURN_ERROR(ereport(ERROR,
2355 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2356 : errmsg("jsonpath item method .%s() can only be applied to a string",
2357 : jspOperationName(jsp->type)))));
2358 :
2359 8304 : datetime = cstring_to_text_with_len(jb->val.string.val,
2360 : jb->val.string.len);
2361 :
2362 : /*
2363 : * At some point we might wish to have callers supply the collation to
2364 : * use, but right now it's unclear that they'd be able to do better than
2365 : * DEFAULT_COLLATION_OID anyway.
2366 : */
2367 8304 : collid = DEFAULT_COLLATION_OID;
2368 :
2369 : /*
2370 : * .datetime(template) has an argument, the rest of the methods don't have
2371 : * an argument. So we handle that separately.
2372 : */
2373 8304 : if (jsp->type == jpiDatetime && jsp->content.arg)
2374 1590 : {
2375 : text *template;
2376 : char *template_str;
2377 : int template_len;
2378 1650 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2379 :
2380 1650 : jspGetArg(jsp, &elem);
2381 :
2382 1650 : if (elem.type != jpiString)
2383 0 : elog(ERROR, "invalid jsonpath item type for .datetime() argument");
2384 :
2385 1650 : template_str = jspGetString(&elem, &template_len);
2386 :
2387 1650 : template = cstring_to_text_with_len(template_str,
2388 : template_len);
2389 :
2390 1650 : value = parse_datetime(datetime, template, collid, true,
2391 : &typid, &typmod, &tz,
2392 1650 : jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
2393 :
2394 1590 : if (escontext.error_occurred)
2395 0 : res = jperError;
2396 : else
2397 1590 : res = jperOk;
2398 : }
2399 : else
2400 : {
2401 : /*
2402 : * According to SQL/JSON standard enumerate ISO formats for: date,
2403 : * timetz, time, timestamptz, timestamp.
2404 : *
2405 : * We also support ISO 8601 format (with "T") for timestamps, because
2406 : * to_json[b]() functions use this format.
2407 : */
2408 : static const char *fmt_str[] =
2409 : {
2410 : "yyyy-mm-dd", /* date */
2411 : "HH24:MI:SS.USTZ", /* timetz */
2412 : "HH24:MI:SSTZ",
2413 : "HH24:MI:SS.US", /* time without tz */
2414 : "HH24:MI:SS",
2415 : "yyyy-mm-dd HH24:MI:SS.USTZ", /* timestamptz */
2416 : "yyyy-mm-dd HH24:MI:SSTZ",
2417 : "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ",
2418 : "yyyy-mm-dd\"T\"HH24:MI:SSTZ",
2419 : "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */
2420 : "yyyy-mm-dd HH24:MI:SS",
2421 : "yyyy-mm-dd\"T\"HH24:MI:SS.US",
2422 : "yyyy-mm-dd\"T\"HH24:MI:SS"
2423 : };
2424 :
2425 : /* cache for format texts */
2426 : static text *fmt_txt[lengthof(fmt_str)] = {0};
2427 : int i;
2428 :
2429 : /*
2430 : * Check for optional precision for methods other than .datetime() and
2431 : * .date()
2432 : */
2433 6654 : if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
2434 3696 : jsp->content.arg)
2435 : {
2436 : bool have_error;
2437 :
2438 780 : jspGetArg(jsp, &elem);
2439 :
2440 780 : if (elem.type != jpiNumeric)
2441 0 : elog(ERROR, "invalid jsonpath item type for %s argument",
2442 : jspOperationName(jsp->type));
2443 :
2444 780 : time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
2445 : &have_error);
2446 780 : if (have_error)
2447 24 : RETURN_ERROR(ereport(ERROR,
2448 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2449 : errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
2450 : jspOperationName(jsp->type)))));
2451 : }
2452 :
2453 : /* loop until datetime format fits */
2454 37722 : for (i = 0; i < lengthof(fmt_str); i++)
2455 : {
2456 37674 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2457 :
2458 37674 : if (!fmt_txt[i])
2459 : {
2460 : MemoryContext oldcxt =
2461 78 : MemoryContextSwitchTo(TopMemoryContext);
2462 :
2463 78 : fmt_txt[i] = cstring_to_text(fmt_str[i]);
2464 78 : MemoryContextSwitchTo(oldcxt);
2465 : }
2466 :
2467 37674 : value = parse_datetime(datetime, fmt_txt[i], collid, true,
2468 : &typid, &typmod, &tz,
2469 : (Node *) &escontext);
2470 :
2471 37674 : if (!escontext.error_occurred)
2472 : {
2473 6582 : res = jperOk;
2474 6582 : break;
2475 : }
2476 : }
2477 :
2478 6630 : if (res == jperNotFound)
2479 : {
2480 48 : if (jsp->type == jpiDatetime)
2481 18 : RETURN_ERROR(ereport(ERROR,
2482 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2483 : errmsg("%s format is not recognized: \"%s\"",
2484 : "datetime", text_to_cstring(datetime)),
2485 : errhint("Use a datetime template argument to specify the input data format."))));
2486 : else
2487 30 : RETURN_ERROR(ereport(ERROR,
2488 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2489 : errmsg("%s format is not recognized: \"%s\"",
2490 : jspOperationName(jsp->type), text_to_cstring(datetime)))));
2491 :
2492 : }
2493 : }
2494 :
2495 : /*
2496 : * parse_datetime() processes the entire input string per the template or
2497 : * ISO format and returns the Datum in best fitted datetime type. So, if
2498 : * this call is for a specific datatype, then we do the conversion here.
2499 : * Throw an error for incompatible types.
2500 : */
2501 8172 : switch (jsp->type)
2502 : {
2503 3894 : case jpiDatetime: /* Nothing to do for DATETIME */
2504 3894 : break;
2505 630 : case jpiDate:
2506 : {
2507 : /* Convert result type to date */
2508 630 : switch (typid)
2509 : {
2510 474 : case DATEOID: /* Nothing to do for DATE */
2511 474 : break;
2512 12 : case TIMEOID:
2513 : case TIMETZOID:
2514 12 : RETURN_ERROR(ereport(ERROR,
2515 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2516 : errmsg("%s format is not recognized: \"%s\"",
2517 : "date", text_to_cstring(datetime)))));
2518 : break;
2519 78 : case TIMESTAMPOID:
2520 78 : value = DirectFunctionCall1(timestamp_date,
2521 : value);
2522 78 : break;
2523 66 : case TIMESTAMPTZOID:
2524 66 : checkTimezoneIsUsedForCast(cxt->useTz,
2525 : "timestamptz", "date");
2526 42 : value = DirectFunctionCall1(timestamptz_date,
2527 : value);
2528 42 : break;
2529 0 : default:
2530 0 : elog(ERROR, "type with oid %u not supported", typid);
2531 : }
2532 :
2533 594 : typid = DATEOID;
2534 : }
2535 594 : break;
2536 810 : case jpiTime:
2537 : {
2538 : /* Convert result type to time without time zone */
2539 810 : switch (typid)
2540 : {
2541 6 : case DATEOID:
2542 6 : RETURN_ERROR(ereport(ERROR,
2543 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2544 : errmsg("%s format is not recognized: \"%s\"",
2545 : "time", text_to_cstring(datetime)))));
2546 : break;
2547 606 : case TIMEOID: /* Nothing to do for TIME */
2548 606 : break;
2549 108 : case TIMETZOID:
2550 108 : checkTimezoneIsUsedForCast(cxt->useTz,
2551 : "timetz", "time");
2552 78 : value = DirectFunctionCall1(timetz_time,
2553 : value);
2554 78 : break;
2555 30 : case TIMESTAMPOID:
2556 30 : value = DirectFunctionCall1(timestamp_time,
2557 : value);
2558 30 : break;
2559 60 : case TIMESTAMPTZOID:
2560 60 : checkTimezoneIsUsedForCast(cxt->useTz,
2561 : "timestamptz", "time");
2562 42 : value = DirectFunctionCall1(timestamptz_time,
2563 : value);
2564 42 : break;
2565 0 : default:
2566 0 : elog(ERROR, "type with oid %u not supported", typid);
2567 : }
2568 :
2569 : /* Force the user-given time precision, if any */
2570 756 : if (time_precision != -1)
2571 : {
2572 : TimeADT result;
2573 :
2574 : /* Get a warning when precision is reduced */
2575 162 : time_precision = anytime_typmod_check(false,
2576 : time_precision);
2577 162 : result = DatumGetTimeADT(value);
2578 162 : AdjustTimeForTypmod(&result, time_precision);
2579 162 : value = TimeADTGetDatum(result);
2580 :
2581 : /* Update the typmod value with the user-given precision */
2582 162 : typmod = time_precision;
2583 : }
2584 :
2585 756 : typid = TIMEOID;
2586 : }
2587 756 : break;
2588 960 : case jpiTimeTz:
2589 : {
2590 : /* Convert result type to time with time zone */
2591 960 : switch (typid)
2592 : {
2593 12 : case DATEOID:
2594 : case TIMESTAMPOID:
2595 12 : RETURN_ERROR(ereport(ERROR,
2596 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2597 : errmsg("%s format is not recognized: \"%s\"",
2598 : "time_tz", text_to_cstring(datetime)))));
2599 : break;
2600 114 : case TIMEOID:
2601 114 : checkTimezoneIsUsedForCast(cxt->useTz,
2602 : "time", "timetz");
2603 84 : value = DirectFunctionCall1(time_timetz,
2604 : value);
2605 84 : break;
2606 792 : case TIMETZOID: /* Nothing to do for TIMETZ */
2607 792 : break;
2608 42 : case TIMESTAMPTZOID:
2609 42 : value = DirectFunctionCall1(timestamptz_timetz,
2610 : value);
2611 42 : break;
2612 0 : default:
2613 0 : elog(ERROR, "type with oid %u not supported", typid);
2614 : }
2615 :
2616 : /* Force the user-given time precision, if any */
2617 918 : if (time_precision != -1)
2618 : {
2619 : TimeTzADT *result;
2620 :
2621 : /* Get a warning when precision is reduced */
2622 198 : time_precision = anytime_typmod_check(true,
2623 : time_precision);
2624 198 : result = DatumGetTimeTzADTP(value);
2625 198 : AdjustTimeForTypmod(&result->time, time_precision);
2626 198 : value = TimeTzADTPGetDatum(result);
2627 :
2628 : /* Update the typmod value with the user-given precision */
2629 198 : typmod = time_precision;
2630 : }
2631 :
2632 918 : typid = TIMETZOID;
2633 : }
2634 918 : break;
2635 822 : case jpiTimestamp:
2636 : {
2637 : /* Convert result type to timestamp without time zone */
2638 822 : switch (typid)
2639 : {
2640 54 : case DATEOID:
2641 54 : value = DirectFunctionCall1(date_timestamp,
2642 : value);
2643 54 : break;
2644 12 : case TIMEOID:
2645 : case TIMETZOID:
2646 12 : RETURN_ERROR(ereport(ERROR,
2647 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2648 : errmsg("%s format is not recognized: \"%s\"",
2649 : "timestamp", text_to_cstring(datetime)))));
2650 : break;
2651 612 : case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
2652 612 : break;
2653 144 : case TIMESTAMPTZOID:
2654 144 : checkTimezoneIsUsedForCast(cxt->useTz,
2655 : "timestamptz", "timestamp");
2656 96 : value = DirectFunctionCall1(timestamptz_timestamp,
2657 : value);
2658 96 : break;
2659 0 : default:
2660 0 : elog(ERROR, "type with oid %u not supported", typid);
2661 : }
2662 :
2663 : /* Force the user-given time precision, if any */
2664 762 : if (time_precision != -1)
2665 : {
2666 : Timestamp result;
2667 162 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2668 :
2669 : /* Get a warning when precision is reduced */
2670 162 : time_precision = anytimestamp_typmod_check(false,
2671 : time_precision);
2672 162 : result = DatumGetTimestamp(value);
2673 162 : AdjustTimestampForTypmod(&result, time_precision,
2674 : (Node *) &escontext);
2675 162 : if (escontext.error_occurred) /* should not happen */
2676 0 : RETURN_ERROR(ereport(ERROR,
2677 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2678 : errmsg("time precision of jsonpath item method .%s() is invalid",
2679 : jspOperationName(jsp->type)))));
2680 162 : value = TimestampGetDatum(result);
2681 :
2682 : /* Update the typmod value with the user-given precision */
2683 162 : typmod = time_precision;
2684 : }
2685 :
2686 762 : typid = TIMESTAMPOID;
2687 : }
2688 762 : break;
2689 1056 : case jpiTimestampTz:
2690 : {
2691 : struct pg_tm tm;
2692 : fsec_t fsec;
2693 :
2694 : /* Convert result type to timestamp with time zone */
2695 1056 : switch (typid)
2696 : {
2697 60 : case DATEOID:
2698 60 : checkTimezoneIsUsedForCast(cxt->useTz,
2699 : "date", "timestamptz");
2700 :
2701 : /*
2702 : * Get the timezone value explicitly since JsonbValue
2703 : * keeps that separate.
2704 : */
2705 54 : j2date(DatumGetDateADT(value) + POSTGRES_EPOCH_JDATE,
2706 : &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2707 54 : tm.tm_hour = 0;
2708 54 : tm.tm_min = 0;
2709 54 : tm.tm_sec = 0;
2710 54 : tz = DetermineTimeZoneOffset(&tm, session_timezone);
2711 :
2712 54 : value = DirectFunctionCall1(date_timestamptz,
2713 : value);
2714 54 : break;
2715 12 : case TIMEOID:
2716 : case TIMETZOID:
2717 12 : RETURN_ERROR(ereport(ERROR,
2718 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2719 : errmsg("%s format is not recognized: \"%s\"",
2720 : "timestamp_tz", text_to_cstring(datetime)))));
2721 : break;
2722 132 : case TIMESTAMPOID:
2723 132 : checkTimezoneIsUsedForCast(cxt->useTz,
2724 : "timestamp", "timestamptz");
2725 :
2726 : /*
2727 : * Get the timezone value explicitly since JsonbValue
2728 : * keeps that separate.
2729 : */
2730 90 : if (timestamp2tm(DatumGetTimestamp(value), NULL, &tm,
2731 : &fsec, NULL, NULL) == 0)
2732 90 : tz = DetermineTimeZoneOffset(&tm,
2733 : session_timezone);
2734 :
2735 90 : value = DirectFunctionCall1(timestamp_timestamptz,
2736 : value);
2737 90 : break;
2738 852 : case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
2739 852 : break;
2740 0 : default:
2741 0 : elog(ERROR, "type with oid %u not supported", typid);
2742 : }
2743 :
2744 : /* Force the user-given time precision, if any */
2745 996 : if (time_precision != -1)
2746 : {
2747 : Timestamp result;
2748 210 : ErrorSaveContext escontext = {T_ErrorSaveContext};
2749 :
2750 : /* Get a warning when precision is reduced */
2751 210 : time_precision = anytimestamp_typmod_check(true,
2752 : time_precision);
2753 210 : result = DatumGetTimestampTz(value);
2754 210 : AdjustTimestampForTypmod(&result, time_precision,
2755 : (Node *) &escontext);
2756 210 : if (escontext.error_occurred) /* should not happen */
2757 0 : RETURN_ERROR(ereport(ERROR,
2758 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2759 : errmsg("time precision of jsonpath item method .%s() is invalid",
2760 : jspOperationName(jsp->type)))));
2761 210 : value = TimestampTzGetDatum(result);
2762 :
2763 : /* Update the typmod value with the user-given precision */
2764 210 : typmod = time_precision;
2765 : }
2766 :
2767 996 : typid = TIMESTAMPTZOID;
2768 : }
2769 996 : break;
2770 0 : default:
2771 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
2772 : }
2773 :
2774 7920 : pfree(datetime);
2775 :
2776 7920 : if (jperIsError(res))
2777 0 : return res;
2778 :
2779 7920 : hasNext = jspGetNext(jsp, &elem);
2780 :
2781 7920 : if (!hasNext && !found)
2782 36 : return res;
2783 :
2784 7884 : jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
2785 :
2786 7884 : jb->type = jbvDatetime;
2787 7884 : jb->val.datetime.value = value;
2788 7884 : jb->val.datetime.typid = typid;
2789 7884 : jb->val.datetime.typmod = typmod;
2790 7884 : jb->val.datetime.tz = tz;
2791 :
2792 7884 : return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
2793 : }
2794 :
2795 : /*
2796 : * Implementation of .keyvalue() method.
2797 : *
2798 : * .keyvalue() method returns a sequence of object's key-value pairs in the
2799 : * following format: '{ "key": key, "value": value, "id": id }'.
2800 : *
2801 : * "id" field is an object identifier which is constructed from the two parts:
2802 : * base object id and its binary offset in base object's jsonb:
2803 : * id = 10000000000 * base_object_id + obj_offset_in_base_object
2804 : *
2805 : * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
2806 : * (maximal offset in jsonb). Decimal multiplier is used here to improve the
2807 : * readability of identifiers.
2808 : *
2809 : * Base object is usually a root object of the path: context item '$' or path
2810 : * variable '$var', literals can't produce objects for now. But if the path
2811 : * contains generated objects (.keyvalue() itself, for example), then they
2812 : * become base object for the subsequent .keyvalue().
2813 : *
2814 : * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
2815 : * of variables (see getJsonPathVariable()). Ids for generated objects
2816 : * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
2817 : */
2818 : static JsonPathExecResult
2819 84 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2820 : JsonbValue *jb, JsonValueList *found)
2821 : {
2822 84 : JsonPathExecResult res = jperNotFound;
2823 : JsonPathItem next;
2824 : JsonbContainer *jbc;
2825 : JsonbValue key;
2826 : JsonbValue val;
2827 : JsonbValue idval;
2828 : JsonbValue keystr;
2829 : JsonbValue valstr;
2830 : JsonbValue idstr;
2831 : JsonbIterator *it;
2832 : JsonbIteratorToken tok;
2833 : int64 id;
2834 : bool hasNext;
2835 :
2836 84 : if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
2837 24 : RETURN_ERROR(ereport(ERROR,
2838 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
2839 : errmsg("jsonpath item method .%s() can only be applied to an object",
2840 : jspOperationName(jsp->type)))));
2841 :
2842 60 : jbc = jb->val.binary.data;
2843 :
2844 60 : if (!JsonContainerSize(jbc))
2845 18 : return jperNotFound; /* no key-value pairs */
2846 :
2847 42 : hasNext = jspGetNext(jsp, &next);
2848 :
2849 42 : keystr.type = jbvString;
2850 42 : keystr.val.string.val = "key";
2851 42 : keystr.val.string.len = 3;
2852 :
2853 42 : valstr.type = jbvString;
2854 42 : valstr.val.string.val = "value";
2855 42 : valstr.val.string.len = 5;
2856 :
2857 42 : idstr.type = jbvString;
2858 42 : idstr.val.string.val = "id";
2859 42 : idstr.val.string.len = 2;
2860 :
2861 : /* construct object id from its base object and offset inside that */
2862 42 : id = jb->type != jbvBinary ? 0 :
2863 42 : (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
2864 42 : id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
2865 :
2866 42 : idval.type = jbvNumeric;
2867 42 : idval.val.numeric = int64_to_numeric(id);
2868 :
2869 42 : it = JsonbIteratorInit(jbc);
2870 :
2871 168 : while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
2872 : {
2873 : JsonBaseObjectInfo baseObject;
2874 : JsonbValue obj;
2875 : JsonbParseState *ps;
2876 : JsonbValue *keyval;
2877 : Jsonb *jsonb;
2878 :
2879 138 : if (tok != WJB_KEY)
2880 72 : continue;
2881 :
2882 66 : res = jperOk;
2883 :
2884 66 : if (!hasNext && !found)
2885 12 : break;
2886 :
2887 60 : tok = JsonbIteratorNext(&it, &val, true);
2888 : Assert(tok == WJB_VALUE);
2889 :
2890 60 : ps = NULL;
2891 60 : pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2892 :
2893 60 : pushJsonbValue(&ps, WJB_KEY, &keystr);
2894 60 : pushJsonbValue(&ps, WJB_VALUE, &key);
2895 :
2896 60 : pushJsonbValue(&ps, WJB_KEY, &valstr);
2897 60 : pushJsonbValue(&ps, WJB_VALUE, &val);
2898 :
2899 60 : pushJsonbValue(&ps, WJB_KEY, &idstr);
2900 60 : pushJsonbValue(&ps, WJB_VALUE, &idval);
2901 :
2902 60 : keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2903 :
2904 60 : jsonb = JsonbValueToJsonb(keyval);
2905 :
2906 60 : JsonbInitBinary(&obj, jsonb);
2907 :
2908 60 : baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2909 :
2910 60 : res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2911 :
2912 60 : cxt->baseObject = baseObject;
2913 :
2914 60 : if (jperIsError(res))
2915 0 : return res;
2916 :
2917 60 : if (res == jperOk && !found)
2918 6 : break;
2919 : }
2920 :
2921 42 : return res;
2922 : }
2923 :
2924 : /*
2925 : * Convert boolean execution status 'res' to a boolean JSON item and execute
2926 : * next jsonpath.
2927 : */
2928 : static JsonPathExecResult
2929 102174 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2930 : JsonValueList *found, JsonPathBool res)
2931 : {
2932 : JsonPathItem next;
2933 : JsonbValue jbv;
2934 :
2935 102174 : if (!jspGetNext(jsp, &next) && !found)
2936 18 : return jperOk; /* found singleton boolean value */
2937 :
2938 102156 : if (res == jpbUnknown)
2939 : {
2940 30 : jbv.type = jbvNull;
2941 : }
2942 : else
2943 : {
2944 102126 : jbv.type = jbvBool;
2945 102126 : jbv.val.boolean = res == jpbTrue;
2946 : }
2947 :
2948 102156 : return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2949 : }
2950 :
2951 : /*
2952 : * Convert jsonpath's scalar or variable node to actual jsonb value.
2953 : *
2954 : * If node is a variable then its id returned, otherwise 0 returned.
2955 : */
2956 : static void
2957 60864 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2958 : JsonbValue *value)
2959 : {
2960 60864 : switch (item->type)
2961 : {
2962 7554 : case jpiNull:
2963 7554 : value->type = jbvNull;
2964 7554 : break;
2965 1422 : case jpiBool:
2966 1422 : value->type = jbvBool;
2967 1422 : value->val.boolean = jspGetBool(item);
2968 1422 : break;
2969 19878 : case jpiNumeric:
2970 19878 : value->type = jbvNumeric;
2971 19878 : value->val.numeric = jspGetNumeric(item);
2972 19878 : break;
2973 24150 : case jpiString:
2974 24150 : value->type = jbvString;
2975 48300 : value->val.string.val = jspGetString(item,
2976 24150 : &value->val.string.len);
2977 24150 : break;
2978 7860 : case jpiVariable:
2979 7860 : getJsonPathVariable(cxt, item, value);
2980 7812 : return;
2981 0 : default:
2982 0 : elog(ERROR, "unexpected jsonpath item type");
2983 : }
2984 : }
2985 :
2986 : /*
2987 : * Returns the computed value of a JSON path variable with given name.
2988 : */
2989 : static JsonbValue *
2990 2610 : GetJsonPathVar(void *cxt, char *varName, int varNameLen,
2991 : JsonbValue *baseObject, int *baseObjectId)
2992 : {
2993 2610 : JsonPathVariable *var = NULL;
2994 2610 : List *vars = cxt;
2995 : ListCell *lc;
2996 : JsonbValue *result;
2997 2610 : int id = 1;
2998 :
2999 3756 : foreach(lc, vars)
3000 : {
3001 3738 : JsonPathVariable *curvar = lfirst(lc);
3002 :
3003 3738 : if (curvar->namelen == varNameLen &&
3004 3726 : strncmp(curvar->name, varName, varNameLen) == 0)
3005 : {
3006 2592 : var = curvar;
3007 2592 : break;
3008 : }
3009 :
3010 1146 : id++;
3011 : }
3012 :
3013 2610 : if (var == NULL)
3014 : {
3015 18 : *baseObjectId = -1;
3016 18 : return NULL;
3017 : }
3018 :
3019 2592 : result = palloc(sizeof(JsonbValue));
3020 2592 : if (var->isnull)
3021 : {
3022 0 : *baseObjectId = 0;
3023 0 : result->type = jbvNull;
3024 : }
3025 : else
3026 2592 : JsonItemFromDatum(var->value, var->typid, var->typmod, result);
3027 :
3028 2592 : *baseObject = *result;
3029 2592 : *baseObjectId = id;
3030 :
3031 2592 : return result;
3032 : }
3033 :
3034 : static int
3035 6288 : CountJsonPathVars(void *cxt)
3036 : {
3037 6288 : List *vars = (List *) cxt;
3038 :
3039 6288 : return list_length(vars);
3040 : }
3041 :
3042 :
3043 : /*
3044 : * Initialize JsonbValue to pass to jsonpath executor from given
3045 : * datum value of the specified type.
3046 : */
3047 : static void
3048 2592 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
3049 : {
3050 2592 : switch (typid)
3051 : {
3052 0 : case BOOLOID:
3053 0 : res->type = jbvBool;
3054 0 : res->val.boolean = DatumGetBool(val);
3055 0 : break;
3056 0 : case NUMERICOID:
3057 0 : JsonbValueInitNumericDatum(res, val);
3058 0 : break;
3059 0 : case INT2OID:
3060 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
3061 0 : break;
3062 2490 : case INT4OID:
3063 2490 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
3064 2490 : break;
3065 0 : case INT8OID:
3066 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
3067 0 : break;
3068 0 : case FLOAT4OID:
3069 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
3070 0 : break;
3071 0 : case FLOAT8OID:
3072 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
3073 0 : break;
3074 12 : case TEXTOID:
3075 : case VARCHAROID:
3076 12 : res->type = jbvString;
3077 12 : res->val.string.val = VARDATA_ANY(val);
3078 12 : res->val.string.len = VARSIZE_ANY_EXHDR(val);
3079 12 : break;
3080 72 : case DATEOID:
3081 : case TIMEOID:
3082 : case TIMETZOID:
3083 : case TIMESTAMPOID:
3084 : case TIMESTAMPTZOID:
3085 72 : res->type = jbvDatetime;
3086 72 : res->val.datetime.value = val;
3087 72 : res->val.datetime.typid = typid;
3088 72 : res->val.datetime.typmod = typmod;
3089 72 : res->val.datetime.tz = 0;
3090 72 : break;
3091 18 : case JSONBOID:
3092 : {
3093 18 : JsonbValue *jbv = res;
3094 18 : Jsonb *jb = DatumGetJsonbP(val);
3095 :
3096 18 : if (JsonContainerIsScalar(&jb->root))
3097 : {
3098 : bool result PG_USED_FOR_ASSERTS_ONLY;
3099 :
3100 18 : result = JsonbExtractScalar(&jb->root, jbv);
3101 : Assert(result);
3102 : }
3103 : else
3104 0 : JsonbInitBinary(jbv, jb);
3105 18 : break;
3106 : }
3107 0 : case JSONOID:
3108 : {
3109 0 : text *txt = DatumGetTextP(val);
3110 0 : char *str = text_to_cstring(txt);
3111 : Jsonb *jb;
3112 :
3113 0 : jb = DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
3114 : CStringGetDatum(str)));
3115 0 : pfree(str);
3116 :
3117 0 : JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
3118 0 : break;
3119 : }
3120 0 : default:
3121 0 : ereport(ERROR,
3122 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3123 : errmsg("could not convert value of type %s to jsonpath",
3124 : format_type_be(typid)));
3125 : }
3126 2592 : }
3127 :
3128 : /* Initialize numeric value from the given datum */
3129 : static void
3130 2490 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
3131 : {
3132 2490 : jbv->type = jbvNumeric;
3133 2490 : jbv->val.numeric = DatumGetNumeric(num);
3134 2490 : }
3135 :
3136 : /*
3137 : * Get the value of variable passed to jsonpath executor
3138 : */
3139 : static void
3140 7860 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
3141 : JsonbValue *value)
3142 : {
3143 : char *varName;
3144 : int varNameLength;
3145 : JsonbValue baseObject;
3146 : int baseObjectId;
3147 : JsonbValue *v;
3148 :
3149 : Assert(variable->type == jpiVariable);
3150 7860 : varName = jspGetString(variable, &varNameLength);
3151 :
3152 15720 : if (cxt->vars == NULL ||
3153 7860 : (v = cxt->getVar(cxt->vars, varName, varNameLength,
3154 : &baseObject, &baseObjectId)) == NULL)
3155 48 : ereport(ERROR,
3156 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3157 : errmsg("could not find jsonpath variable \"%s\"",
3158 : pnstrdup(varName, varNameLength))));
3159 :
3160 7812 : if (baseObjectId > 0)
3161 : {
3162 7812 : *value = *v;
3163 7812 : setBaseObject(cxt, &baseObject, baseObjectId);
3164 : }
3165 7812 : }
3166 :
3167 : /*
3168 : * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
3169 : * is specified as a jsonb value.
3170 : */
3171 : static JsonbValue *
3172 5250 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
3173 : JsonbValue *baseObject, int *baseObjectId)
3174 : {
3175 5250 : Jsonb *vars = varsJsonb;
3176 : JsonbValue tmp;
3177 : JsonbValue *result;
3178 :
3179 5250 : tmp.type = jbvString;
3180 5250 : tmp.val.string.val = varName;
3181 5250 : tmp.val.string.len = varNameLength;
3182 :
3183 5250 : result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
3184 :
3185 5250 : if (result == NULL)
3186 : {
3187 30 : *baseObjectId = -1;
3188 30 : return NULL;
3189 : }
3190 :
3191 5220 : *baseObjectId = 1;
3192 5220 : JsonbInitBinary(baseObject, vars);
3193 :
3194 5220 : return result;
3195 : }
3196 :
3197 : /*
3198 : * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
3199 : * is specified as a jsonb value.
3200 : */
3201 : static int
3202 192486 : countVariablesFromJsonb(void *varsJsonb)
3203 : {
3204 192486 : Jsonb *vars = varsJsonb;
3205 :
3206 192486 : if (vars && !JsonContainerIsObject(&vars->root))
3207 : {
3208 12 : ereport(ERROR,
3209 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3210 : errmsg("\"vars\" argument is not an object"),
3211 : errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
3212 : }
3213 :
3214 : /* count of base objects */
3215 192474 : return vars != NULL ? 1 : 0;
3216 : }
3217 :
3218 : /**************** Support functions for JsonPath execution *****************/
3219 :
3220 : /*
3221 : * Returns the size of an array item, or -1 if item is not an array.
3222 : */
3223 : static int
3224 546 : JsonbArraySize(JsonbValue *jb)
3225 : {
3226 : Assert(jb->type != jbvArray);
3227 :
3228 546 : if (jb->type == jbvBinary)
3229 : {
3230 504 : JsonbContainer *jbc = jb->val.binary.data;
3231 :
3232 504 : if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
3233 492 : return JsonContainerSize(jbc);
3234 : }
3235 :
3236 54 : return -1;
3237 : }
3238 :
3239 : /* Comparison predicate callback. */
3240 : static JsonPathBool
3241 21690 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
3242 : {
3243 21690 : JsonPathExecContext *cxt = (JsonPathExecContext *) p;
3244 :
3245 21690 : return compareItems(cmp->type, lv, rv, cxt->useTz);
3246 : }
3247 :
3248 : /*
3249 : * Perform per-byte comparison of two strings.
3250 : */
3251 : static int
3252 3456 : binaryCompareStrings(const char *s1, int len1,
3253 : const char *s2, int len2)
3254 : {
3255 : int cmp;
3256 :
3257 3456 : cmp = memcmp(s1, s2, Min(len1, len2));
3258 :
3259 3456 : if (cmp != 0)
3260 1968 : return cmp;
3261 :
3262 1488 : if (len1 == len2)
3263 288 : return 0;
3264 :
3265 1200 : return len1 < len2 ? -1 : 1;
3266 : }
3267 :
3268 : /*
3269 : * Compare two strings in the current server encoding using Unicode codepoint
3270 : * collation.
3271 : */
3272 : static int
3273 3456 : compareStrings(const char *mbstr1, int mblen1,
3274 : const char *mbstr2, int mblen2)
3275 : {
3276 6912 : if (GetDatabaseEncoding() == PG_SQL_ASCII ||
3277 3456 : GetDatabaseEncoding() == PG_UTF8)
3278 : {
3279 : /*
3280 : * It's known property of UTF-8 strings that their per-byte comparison
3281 : * result matches codepoints comparison result. ASCII can be
3282 : * considered as special case of UTF-8.
3283 : */
3284 3456 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3285 : }
3286 : else
3287 : {
3288 : char *utf8str1,
3289 : *utf8str2;
3290 : int cmp,
3291 : utf8len1,
3292 : utf8len2;
3293 :
3294 : /*
3295 : * We have to convert other encodings to UTF-8 first, then compare.
3296 : * Input strings may be not null-terminated and pg_server_to_any() may
3297 : * return them "as is". So, use strlen() only if there is real
3298 : * conversion.
3299 : */
3300 0 : utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
3301 0 : utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
3302 0 : utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
3303 0 : utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
3304 :
3305 0 : cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
3306 :
3307 : /*
3308 : * If pg_server_to_any() did no real conversion, then we actually
3309 : * compared original strings. So, we already done.
3310 : */
3311 0 : if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
3312 0 : return cmp;
3313 :
3314 : /* Free memory if needed */
3315 0 : if (mbstr1 != utf8str1)
3316 0 : pfree(utf8str1);
3317 0 : if (mbstr2 != utf8str2)
3318 0 : pfree(utf8str2);
3319 :
3320 : /*
3321 : * When all Unicode codepoints are equal, return result of binary
3322 : * comparison. In some edge cases, same characters may have different
3323 : * representations in encoding. Then our behavior could diverge from
3324 : * standard. However, that allow us to do simple binary comparison
3325 : * for "==" operator, which is performance critical in typical cases.
3326 : * In future to implement strict standard conformance, we can do
3327 : * normalization of input JSON strings.
3328 : */
3329 0 : if (cmp == 0)
3330 0 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3331 : else
3332 0 : return cmp;
3333 : }
3334 : }
3335 :
3336 : /*
3337 : * Compare two SQL/JSON items using comparison operation 'op'.
3338 : */
3339 : static JsonPathBool
3340 21690 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
3341 : {
3342 : int cmp;
3343 : bool res;
3344 :
3345 21690 : if (jb1->type != jb2->type)
3346 : {
3347 3132 : if (jb1->type == jbvNull || jb2->type == jbvNull)
3348 :
3349 : /*
3350 : * Equality and order comparison of nulls to non-nulls returns
3351 : * always false, but inequality comparison returns true.
3352 : */
3353 2874 : return op == jpiNotEqual ? jpbTrue : jpbFalse;
3354 :
3355 : /* Non-null items of different types are not comparable. */
3356 258 : return jpbUnknown;
3357 : }
3358 :
3359 18558 : switch (jb1->type)
3360 : {
3361 186 : case jbvNull:
3362 186 : cmp = 0;
3363 186 : break;
3364 870 : case jbvBool:
3365 1266 : cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
3366 396 : jb1->val.boolean ? 1 : -1;
3367 870 : break;
3368 3936 : case jbvNumeric:
3369 3936 : cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
3370 3936 : break;
3371 9930 : case jbvString:
3372 9930 : if (op == jpiEqual)
3373 6474 : return jb1->val.string.len != jb2->val.string.len ||
3374 3582 : memcmp(jb1->val.string.val,
3375 3582 : jb2->val.string.val,
3376 6474 : jb1->val.string.len) ? jpbFalse : jpbTrue;
3377 :
3378 3456 : cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
3379 3456 : jb2->val.string.val, jb2->val.string.len);
3380 3456 : break;
3381 3624 : case jbvDatetime:
3382 : {
3383 : bool cast_error;
3384 :
3385 3624 : cmp = compareDatetime(jb1->val.datetime.value,
3386 : jb1->val.datetime.typid,
3387 : jb2->val.datetime.value,
3388 : jb2->val.datetime.typid,
3389 : useTz,
3390 : &cast_error);
3391 :
3392 3534 : if (cast_error)
3393 306 : return jpbUnknown;
3394 : }
3395 3228 : break;
3396 :
3397 12 : case jbvBinary:
3398 : case jbvArray:
3399 : case jbvObject:
3400 12 : return jpbUnknown; /* non-scalars are not comparable */
3401 :
3402 0 : default:
3403 0 : elog(ERROR, "invalid jsonb value type %d", jb1->type);
3404 : }
3405 :
3406 11676 : switch (op)
3407 : {
3408 2700 : case jpiEqual:
3409 2700 : res = (cmp == 0);
3410 2700 : break;
3411 6 : case jpiNotEqual:
3412 6 : res = (cmp != 0);
3413 6 : break;
3414 2100 : case jpiLess:
3415 2100 : res = (cmp < 0);
3416 2100 : break;
3417 1554 : case jpiGreater:
3418 1554 : res = (cmp > 0);
3419 1554 : break;
3420 2040 : case jpiLessOrEqual:
3421 2040 : res = (cmp <= 0);
3422 2040 : break;
3423 3276 : case jpiGreaterOrEqual:
3424 3276 : res = (cmp >= 0);
3425 3276 : break;
3426 0 : default:
3427 0 : elog(ERROR, "unrecognized jsonpath operation: %d", op);
3428 : return jpbUnknown;
3429 : }
3430 :
3431 11676 : return res ? jpbTrue : jpbFalse;
3432 : }
3433 :
3434 : /* Compare two numerics */
3435 : static int
3436 3936 : compareNumeric(Numeric a, Numeric b)
3437 : {
3438 3936 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
3439 : NumericGetDatum(a),
3440 : NumericGetDatum(b)));
3441 : }
3442 :
3443 : static JsonbValue *
3444 122416 : copyJsonbValue(JsonbValue *src)
3445 : {
3446 122416 : JsonbValue *dst = palloc(sizeof(*dst));
3447 :
3448 122416 : *dst = *src;
3449 :
3450 122416 : return dst;
3451 : }
3452 :
3453 : /*
3454 : * Execute array subscript expression and convert resulting numeric item to
3455 : * the integer type with truncation.
3456 : */
3457 : static JsonPathExecResult
3458 516 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
3459 : int32 *index)
3460 : {
3461 : JsonbValue *jbv;
3462 516 : JsonValueList found = {0};
3463 516 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
3464 : Datum numeric_index;
3465 510 : bool have_error = false;
3466 :
3467 510 : if (jperIsError(res))
3468 0 : return res;
3469 :
3470 1008 : if (JsonValueListLength(&found) != 1 ||
3471 498 : !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
3472 24 : RETURN_ERROR(ereport(ERROR,
3473 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3474 : errmsg("jsonpath array subscript is not a single numeric value"))));
3475 :
3476 486 : numeric_index = DirectFunctionCall2(numeric_trunc,
3477 : NumericGetDatum(jbv->val.numeric),
3478 : Int32GetDatum(0));
3479 :
3480 486 : *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
3481 : &have_error);
3482 :
3483 486 : if (have_error)
3484 24 : RETURN_ERROR(ereport(ERROR,
3485 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3486 : errmsg("jsonpath array subscript is out of integer range"))));
3487 :
3488 462 : return jperOk;
3489 : }
3490 :
3491 : /* Save base object and its id needed for the execution of .keyvalue(). */
3492 : static JsonBaseObjectInfo
3493 218712 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
3494 : {
3495 218712 : JsonBaseObjectInfo baseObject = cxt->baseObject;
3496 :
3497 218712 : cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
3498 : (JsonbContainer *) jbv->val.binary.data;
3499 218712 : cxt->baseObject.id = id;
3500 :
3501 218712 : return baseObject;
3502 : }
3503 :
3504 : static void
3505 1024 : JsonValueListClear(JsonValueList *jvl)
3506 : {
3507 1024 : jvl->singleton = NULL;
3508 1024 : jvl->list = NIL;
3509 1024 : }
3510 :
3511 : static void
3512 276882 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
3513 : {
3514 276882 : if (jvl->singleton)
3515 : {
3516 1914 : jvl->list = list_make2(jvl->singleton, jbv);
3517 1914 : jvl->singleton = NULL;
3518 : }
3519 274968 : else if (!jvl->list)
3520 272886 : jvl->singleton = jbv;
3521 : else
3522 2082 : jvl->list = lappend(jvl->list, jbv);
3523 276882 : }
3524 :
3525 : static int
3526 108614 : JsonValueListLength(const JsonValueList *jvl)
3527 : {
3528 108614 : return jvl->singleton ? 1 : list_length(jvl->list);
3529 : }
3530 :
3531 : static bool
3532 30 : JsonValueListIsEmpty(JsonValueList *jvl)
3533 : {
3534 30 : return !jvl->singleton && (jvl->list == NIL);
3535 : }
3536 :
3537 : static JsonbValue *
3538 107906 : JsonValueListHead(JsonValueList *jvl)
3539 : {
3540 107906 : return jvl->singleton ? jvl->singleton : linitial(jvl->list);
3541 : }
3542 :
3543 : static List *
3544 2736 : JsonValueListGetList(JsonValueList *jvl)
3545 : {
3546 2736 : if (jvl->singleton)
3547 1686 : return list_make1(jvl->singleton);
3548 :
3549 1050 : return jvl->list;
3550 : }
3551 :
3552 : static void
3553 207388 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
3554 : {
3555 207388 : if (jvl->singleton)
3556 : {
3557 129238 : it->value = jvl->singleton;
3558 129238 : it->list = NIL;
3559 129238 : it->next = NULL;
3560 : }
3561 78150 : else if (jvl->list != NIL)
3562 : {
3563 1194 : it->value = (JsonbValue *) linitial(jvl->list);
3564 1194 : it->list = jvl->list;
3565 1194 : it->next = list_second_cell(jvl->list);
3566 : }
3567 : else
3568 : {
3569 76956 : it->value = NULL;
3570 76956 : it->list = NIL;
3571 76956 : it->next = NULL;
3572 : }
3573 207388 : }
3574 :
3575 : /*
3576 : * Get the next item from the sequence advancing iterator.
3577 : */
3578 : static JsonbValue *
3579 326090 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
3580 : {
3581 326090 : JsonbValue *result = it->value;
3582 :
3583 326090 : if (it->next)
3584 : {
3585 2400 : it->value = lfirst(it->next);
3586 2400 : it->next = lnext(it->list, it->next);
3587 : }
3588 : else
3589 : {
3590 323690 : it->value = NULL;
3591 : }
3592 :
3593 326090 : return result;
3594 : }
3595 :
3596 : /*
3597 : * Initialize a binary JsonbValue with the given jsonb container.
3598 : */
3599 : static JsonbValue *
3600 198318 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
3601 : {
3602 198318 : jbv->type = jbvBinary;
3603 198318 : jbv->val.binary.data = &jb->root;
3604 198318 : jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
3605 :
3606 198318 : return jbv;
3607 : }
3608 :
3609 : /*
3610 : * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
3611 : */
3612 : static int
3613 285100 : JsonbType(JsonbValue *jb)
3614 : {
3615 285100 : int type = jb->type;
3616 :
3617 285100 : if (jb->type == jbvBinary)
3618 : {
3619 183070 : JsonbContainer *jbc = jb->val.binary.data;
3620 :
3621 : /* Scalars should be always extracted during jsonpath execution. */
3622 : Assert(!JsonContainerIsScalar(jbc));
3623 :
3624 183070 : if (JsonContainerIsObject(jbc))
3625 179450 : type = jbvObject;
3626 3620 : else if (JsonContainerIsArray(jbc))
3627 3620 : type = jbvArray;
3628 : else
3629 0 : elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
3630 : }
3631 :
3632 285100 : return type;
3633 : }
3634 :
3635 : /* Get scalar of given type or NULL on type mismatch */
3636 : static JsonbValue *
3637 11250 : getScalar(JsonbValue *scalar, enum jbvType type)
3638 : {
3639 : /* Scalars should be always extracted during jsonpath execution. */
3640 : Assert(scalar->type != jbvBinary ||
3641 : !JsonContainerIsScalar(scalar->val.binary.data));
3642 :
3643 11250 : return scalar->type == type ? scalar : NULL;
3644 : }
3645 :
3646 : /* Construct a JSON array from the item list */
3647 : static JsonbValue *
3648 438 : wrapItemsInArray(const JsonValueList *items)
3649 : {
3650 438 : JsonbParseState *ps = NULL;
3651 : JsonValueListIterator it;
3652 : JsonbValue *jbv;
3653 :
3654 438 : pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
3655 :
3656 438 : JsonValueListInitIterator(items, &it);
3657 1152 : while ((jbv = JsonValueListNext(items, &it)))
3658 714 : pushJsonbValue(&ps, WJB_ELEM, jbv);
3659 :
3660 438 : return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
3661 : }
3662 :
3663 : /* Check if the timezone required for casting from type1 to type2 is used */
3664 : static void
3665 1350 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
3666 : {
3667 1350 : if (!useTz)
3668 288 : ereport(ERROR,
3669 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3670 : errmsg("cannot convert value from %s to %s without time zone usage",
3671 : type1, type2),
3672 : errhint("Use *_tz() function for time zone support.")));
3673 1062 : }
3674 :
3675 : /* Convert time datum to timetz datum */
3676 : static Datum
3677 252 : castTimeToTimeTz(Datum time, bool useTz)
3678 : {
3679 252 : checkTimezoneIsUsedForCast(useTz, "time", "timetz");
3680 :
3681 216 : return DirectFunctionCall1(time_timetz, time);
3682 : }
3683 :
3684 : /*
3685 : * Compare date to timestamp.
3686 : * Note that this doesn't involve any timezone considerations.
3687 : */
3688 : static int
3689 186 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
3690 : {
3691 186 : return date_cmp_timestamp_internal(date1, ts2);
3692 : }
3693 :
3694 : /*
3695 : * Compare date to timestamptz.
3696 : */
3697 : static int
3698 162 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
3699 : {
3700 162 : checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
3701 :
3702 144 : return date_cmp_timestamptz_internal(date1, tstz2);
3703 : }
3704 :
3705 : /*
3706 : * Compare timestamp to timestamptz.
3707 : */
3708 : static int
3709 252 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
3710 : {
3711 252 : checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
3712 :
3713 216 : return timestamp_cmp_timestamptz_internal(ts1, tstz2);
3714 : }
3715 :
3716 : /*
3717 : * Cross-type comparison of two datetime SQL/JSON items. If items are
3718 : * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
3719 : * If the cast requires timezone and it is not used, then explicit error is thrown.
3720 : */
3721 : static int
3722 3624 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
3723 : bool useTz, bool *cast_error)
3724 : {
3725 : PGFunction cmpfunc;
3726 :
3727 3624 : *cast_error = false;
3728 :
3729 3624 : switch (typid1)
3730 : {
3731 564 : case DATEOID:
3732 : switch (typid2)
3733 : {
3734 378 : case DATEOID:
3735 378 : cmpfunc = date_cmp;
3736 :
3737 378 : break;
3738 :
3739 78 : case TIMESTAMPOID:
3740 78 : return cmpDateToTimestamp(DatumGetDateADT(val1),
3741 : DatumGetTimestamp(val2),
3742 : useTz);
3743 :
3744 72 : case TIMESTAMPTZOID:
3745 72 : return cmpDateToTimestampTz(DatumGetDateADT(val1),
3746 : DatumGetTimestampTz(val2),
3747 : useTz);
3748 :
3749 36 : case TIMEOID:
3750 : case TIMETZOID:
3751 36 : *cast_error = true; /* uncomparable types */
3752 36 : return 0;
3753 :
3754 0 : default:
3755 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3756 : typid2);
3757 : }
3758 378 : break;
3759 :
3760 624 : case TIMEOID:
3761 : switch (typid2)
3762 : {
3763 426 : case TIMEOID:
3764 426 : cmpfunc = time_cmp;
3765 :
3766 426 : break;
3767 :
3768 126 : case TIMETZOID:
3769 126 : val1 = castTimeToTimeTz(val1, useTz);
3770 108 : cmpfunc = timetz_cmp;
3771 :
3772 108 : break;
3773 :
3774 72 : case DATEOID:
3775 : case TIMESTAMPOID:
3776 : case TIMESTAMPTZOID:
3777 72 : *cast_error = true; /* uncomparable types */
3778 72 : return 0;
3779 :
3780 0 : default:
3781 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3782 : typid2);
3783 : }
3784 534 : break;
3785 :
3786 804 : case TIMETZOID:
3787 : switch (typid2)
3788 : {
3789 126 : case TIMEOID:
3790 126 : val2 = castTimeToTimeTz(val2, useTz);
3791 108 : cmpfunc = timetz_cmp;
3792 :
3793 108 : break;
3794 :
3795 606 : case TIMETZOID:
3796 606 : cmpfunc = timetz_cmp;
3797 :
3798 606 : break;
3799 :
3800 72 : case DATEOID:
3801 : case TIMESTAMPOID:
3802 : case TIMESTAMPTZOID:
3803 72 : *cast_error = true; /* uncomparable types */
3804 72 : return 0;
3805 :
3806 0 : default:
3807 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3808 : typid2);
3809 : }
3810 714 : break;
3811 :
3812 714 : case TIMESTAMPOID:
3813 : switch (typid2)
3814 : {
3815 108 : case DATEOID:
3816 108 : return -cmpDateToTimestamp(DatumGetDateADT(val2),
3817 : DatumGetTimestamp(val1),
3818 : useTz);
3819 :
3820 426 : case TIMESTAMPOID:
3821 426 : cmpfunc = timestamp_cmp;
3822 :
3823 426 : break;
3824 :
3825 126 : case TIMESTAMPTZOID:
3826 126 : return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
3827 : DatumGetTimestampTz(val2),
3828 : useTz);
3829 :
3830 54 : case TIMEOID:
3831 : case TIMETZOID:
3832 54 : *cast_error = true; /* uncomparable types */
3833 54 : return 0;
3834 :
3835 0 : default:
3836 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3837 : typid2);
3838 : }
3839 426 : break;
3840 :
3841 918 : case TIMESTAMPTZOID:
3842 : switch (typid2)
3843 : {
3844 90 : case DATEOID:
3845 90 : return -cmpDateToTimestampTz(DatumGetDateADT(val2),
3846 : DatumGetTimestampTz(val1),
3847 : useTz);
3848 :
3849 126 : case TIMESTAMPOID:
3850 126 : return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
3851 : DatumGetTimestampTz(val1),
3852 : useTz);
3853 :
3854 630 : case TIMESTAMPTZOID:
3855 630 : cmpfunc = timestamp_cmp;
3856 :
3857 630 : break;
3858 :
3859 72 : case TIMEOID:
3860 : case TIMETZOID:
3861 72 : *cast_error = true; /* uncomparable types */
3862 72 : return 0;
3863 :
3864 0 : default:
3865 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3866 : typid2);
3867 : }
3868 630 : break;
3869 :
3870 0 : default:
3871 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
3872 : }
3873 :
3874 2682 : if (*cast_error)
3875 0 : return 0; /* cast error */
3876 :
3877 2682 : return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
3878 : }
3879 :
3880 : /*
3881 : * Executor-callable JSON_EXISTS implementation
3882 : *
3883 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
3884 : * *error to true.
3885 : */
3886 : bool
3887 582 : JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
3888 : {
3889 : JsonPathExecResult res;
3890 :
3891 582 : res = executeJsonPath(jp, vars,
3892 : GetJsonPathVar, CountJsonPathVars,
3893 : DatumGetJsonbP(jb), !error, NULL, true);
3894 :
3895 : Assert(error || !jperIsError(res));
3896 :
3897 576 : if (error && jperIsError(res))
3898 156 : *error = true;
3899 :
3900 576 : return res == jperOk;
3901 : }
3902 :
3903 : /*
3904 : * Executor-callable JSON_QUERY implementation
3905 : *
3906 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
3907 : * *error to true. *empty is set to true if no match is found.
3908 : */
3909 : Datum
3910 2460 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
3911 : bool *error, List *vars,
3912 : const char *column_name)
3913 : {
3914 : JsonbValue *singleton;
3915 : bool wrap;
3916 2460 : JsonValueList found = {0};
3917 : JsonPathExecResult res;
3918 : int count;
3919 :
3920 2460 : res = executeJsonPath(jp, vars,
3921 : GetJsonPathVar, CountJsonPathVars,
3922 : DatumGetJsonbP(jb), !error, &found, true);
3923 : Assert(error || !jperIsError(res));
3924 2442 : if (error && jperIsError(res))
3925 : {
3926 30 : *error = true;
3927 30 : *empty = false;
3928 30 : return (Datum) 0;
3929 : }
3930 :
3931 : /*
3932 : * Determine whether to wrap the result in a JSON array or not.
3933 : *
3934 : * First, count the number of SQL/JSON items in the returned
3935 : * JsonValueList. If the list is empty (singleton == NULL), no wrapping is
3936 : * necessary.
3937 : *
3938 : * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
3939 : * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
3940 : * default when no WRAPPER clause is specified.
3941 : *
3942 : * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
3943 : * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
3944 : * UNCONDITIONAL WRAPPER clause.
3945 : *
3946 : * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
3947 : * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
3948 : */
3949 2412 : count = JsonValueListLength(&found);
3950 2412 : singleton = count > 0 ? JsonValueListHead(&found) : NULL;
3951 2412 : if (singleton == NULL)
3952 210 : wrap = false;
3953 2202 : else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
3954 1710 : wrap = false;
3955 492 : else if (wrapper == JSW_UNCONDITIONAL)
3956 318 : wrap = true;
3957 174 : else if (wrapper == JSW_CONDITIONAL)
3958 174 : wrap = count > 1;
3959 : else
3960 : {
3961 0 : elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
3962 : wrap = false;
3963 : }
3964 :
3965 2412 : if (wrap)
3966 378 : return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
3967 :
3968 : /* No wrapping means only one item is expected. */
3969 2034 : if (count > 1)
3970 : {
3971 60 : if (error)
3972 : {
3973 48 : *error = true;
3974 48 : return (Datum) 0;
3975 : }
3976 :
3977 12 : if (column_name)
3978 6 : ereport(ERROR,
3979 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3980 : errmsg("JSON path expression for column \"%s\" should return single item without wrapper",
3981 : column_name),
3982 : errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
3983 : else
3984 6 : ereport(ERROR,
3985 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3986 : errmsg("JSON path expression in JSON_QUERY should return single item without wrapper"),
3987 : errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
3988 : }
3989 :
3990 1974 : if (singleton)
3991 1764 : return JsonbPGetDatum(JsonbValueToJsonb(singleton));
3992 :
3993 210 : *empty = true;
3994 210 : return PointerGetDatum(NULL);
3995 : }
3996 :
3997 : /*
3998 : * Executor-callable JSON_VALUE implementation
3999 : *
4000 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4001 : * *error to true. *empty is set to true if no match is found.
4002 : */
4003 : JsonbValue *
4004 2240 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
4005 : const char *column_name)
4006 : {
4007 : JsonbValue *res;
4008 2240 : JsonValueList found = {0};
4009 : JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
4010 : int count;
4011 :
4012 2240 : jper = executeJsonPath(jp, vars, GetJsonPathVar, CountJsonPathVars,
4013 : DatumGetJsonbP(jb),
4014 : !error, &found, true);
4015 :
4016 : Assert(error || !jperIsError(jper));
4017 :
4018 2228 : if (error && jperIsError(jper))
4019 : {
4020 18 : *error = true;
4021 18 : *empty = false;
4022 18 : return NULL;
4023 : }
4024 :
4025 2210 : count = JsonValueListLength(&found);
4026 :
4027 2210 : *empty = (count == 0);
4028 :
4029 2210 : if (*empty)
4030 330 : return NULL;
4031 :
4032 : /* JSON_VALUE expects to get only singletons. */
4033 1880 : if (count > 1)
4034 : {
4035 18 : if (error)
4036 : {
4037 12 : *error = true;
4038 12 : return NULL;
4039 : }
4040 :
4041 6 : if (column_name)
4042 0 : ereport(ERROR,
4043 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4044 : errmsg("JSON path expression for column \"%s\" should return single scalar item",
4045 : column_name)));
4046 : else
4047 6 : ereport(ERROR,
4048 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4049 : errmsg("JSON path expression in JSON_VALUE should return single scalar item")));
4050 : }
4051 :
4052 1862 : res = JsonValueListHead(&found);
4053 1862 : if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
4054 0 : JsonbExtractScalar(res->val.binary.data, res);
4055 :
4056 : /* JSON_VALUE expects to get only scalars. */
4057 1862 : if (!IsAJsonbScalar(res))
4058 : {
4059 96 : if (error)
4060 : {
4061 84 : *error = true;
4062 84 : return NULL;
4063 : }
4064 :
4065 12 : if (column_name)
4066 0 : ereport(ERROR,
4067 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4068 : errmsg("JSON path expression for column \"%s\" should return single scalar item",
4069 : column_name)));
4070 : else
4071 12 : ereport(ERROR,
4072 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4073 : errmsg("JSON path expression in JSON_VALUE should return single scalar item")));
4074 : }
4075 :
4076 1766 : if (res->type == jbvNull)
4077 72 : return NULL;
4078 :
4079 1694 : return res;
4080 : }
4081 :
4082 : /************************ JSON_TABLE functions ***************************/
4083 :
4084 : /*
4085 : * Sanity-checks and returns the opaque JsonTableExecContext from the
4086 : * given executor state struct.
4087 : */
4088 : static inline JsonTableExecContext *
4089 7300 : GetJsonTableExecContext(TableFuncScanState *state, const char *fname)
4090 : {
4091 : JsonTableExecContext *result;
4092 :
4093 7300 : if (!IsA(state, TableFuncScanState))
4094 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4095 7300 : result = (JsonTableExecContext *) state->opaque;
4096 7300 : if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
4097 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4098 :
4099 7300 : return result;
4100 : }
4101 :
4102 : /*
4103 : * JsonTableInitOpaque
4104 : * Fill in TableFuncScanState->opaque for processing JSON_TABLE
4105 : *
4106 : * This initializes the PASSING arguments and the JsonTablePlanState for
4107 : * JsonTablePlan given in TableFunc.
4108 : */
4109 : static void
4110 530 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
4111 : {
4112 : JsonTableExecContext *cxt;
4113 530 : PlanState *ps = &state->ss.ps;
4114 530 : TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
4115 530 : TableFunc *tf = tfs->tablefunc;
4116 530 : JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
4117 530 : JsonExpr *je = castNode(JsonExpr, tf->docexpr);
4118 530 : List *args = NIL;
4119 :
4120 530 : cxt = palloc0(sizeof(JsonTableExecContext));
4121 530 : cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC;
4122 :
4123 : /*
4124 : * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
4125 : * executor via JsonPathVariables.
4126 : */
4127 530 : if (state->passingvalexprs)
4128 : {
4129 : ListCell *exprlc;
4130 : ListCell *namelc;
4131 :
4132 : Assert(list_length(state->passingvalexprs) ==
4133 : list_length(je->passing_names));
4134 372 : forboth(exprlc, state->passingvalexprs,
4135 : namelc, je->passing_names)
4136 : {
4137 246 : ExprState *state = lfirst_node(ExprState, exprlc);
4138 246 : String *name = lfirst_node(String, namelc);
4139 246 : JsonPathVariable *var = palloc(sizeof(*var));
4140 :
4141 246 : var->name = pstrdup(name->sval);
4142 246 : var->namelen = strlen(var->name);
4143 246 : var->typid = exprType((Node *) state->expr);
4144 246 : var->typmod = exprTypmod((Node *) state->expr);
4145 :
4146 : /*
4147 : * Evaluate the expression and save the value to be returned by
4148 : * GetJsonPathVar().
4149 : */
4150 246 : var->value = ExecEvalExpr(state, ps->ps_ExprContext,
4151 : &var->isnull);
4152 :
4153 246 : args = lappend(args, var);
4154 : }
4155 : }
4156 :
4157 1060 : cxt->colplanstates = palloc(sizeof(JsonTablePlanState *) *
4158 530 : list_length(tf->colvalexprs));
4159 :
4160 : /*
4161 : * Initialize plan for the root path and, recursively, also any child
4162 : * plans that compute the NESTED paths.
4163 : */
4164 530 : cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
4165 : CurrentMemoryContext);
4166 :
4167 530 : state->opaque = cxt;
4168 530 : }
4169 :
4170 : /*
4171 : * JsonTableDestroyOpaque
4172 : * Resets state->opaque
4173 : */
4174 : static void
4175 530 : JsonTableDestroyOpaque(TableFuncScanState *state)
4176 : {
4177 : JsonTableExecContext *cxt =
4178 530 : GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
4179 :
4180 : /* not valid anymore */
4181 530 : cxt->magic = 0;
4182 :
4183 530 : state->opaque = NULL;
4184 530 : }
4185 :
4186 : /*
4187 : * JsonTableInitPlan
4188 : * Initialize information for evaluating jsonpath in the given
4189 : * JsonTablePlan and, recursively, in any child plans
4190 : */
4191 : static JsonTablePlanState *
4192 1036 : JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan,
4193 : JsonTablePlanState *parentstate,
4194 : List *args, MemoryContext mcxt)
4195 : {
4196 1036 : JsonTablePlanState *planstate = palloc0(sizeof(*planstate));
4197 :
4198 1036 : planstate->plan = plan;
4199 1036 : planstate->parent = parentstate;
4200 :
4201 1036 : if (IsA(plan, JsonTablePathScan))
4202 : {
4203 922 : JsonTablePathScan *scan = (JsonTablePathScan *) plan;
4204 : int i;
4205 :
4206 922 : planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
4207 922 : planstate->args = args;
4208 922 : planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
4209 : ALLOCSET_DEFAULT_SIZES);
4210 :
4211 : /* No row pattern evaluated yet. */
4212 922 : planstate->current.value = PointerGetDatum(NULL);
4213 922 : planstate->current.isnull = true;
4214 :
4215 2340 : for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
4216 1418 : cxt->colplanstates[i] = planstate;
4217 :
4218 922 : planstate->nested = scan->child ?
4219 922 : JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
4220 : }
4221 114 : else if (IsA(plan, JsonTableSiblingJoin))
4222 : {
4223 114 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
4224 :
4225 114 : planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
4226 : args, mcxt);
4227 114 : planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
4228 : args, mcxt);
4229 : }
4230 :
4231 1036 : return planstate;
4232 : }
4233 :
4234 : /*
4235 : * JsonTableSetDocument
4236 : * Install the input document and evaluate the row pattern
4237 : */
4238 : static void
4239 524 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
4240 : {
4241 : JsonTableExecContext *cxt =
4242 524 : GetJsonTableExecContext(state, "JsonTableSetDocument");
4243 :
4244 524 : JsonTableResetRowPattern(cxt->rootplanstate, value);
4245 518 : }
4246 :
4247 : /*
4248 : * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
4249 : * the given context item
4250 : */
4251 : static void
4252 1006 : JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item)
4253 : {
4254 1006 : JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
4255 : MemoryContext oldcxt;
4256 : JsonPathExecResult res;
4257 1006 : Jsonb *js = (Jsonb *) DatumGetJsonbP(item);
4258 :
4259 1006 : JsonValueListClear(&planstate->found);
4260 :
4261 1006 : MemoryContextResetOnly(planstate->mcxt);
4262 :
4263 1006 : oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4264 :
4265 1006 : res = executeJsonPath(planstate->path, planstate->args,
4266 : GetJsonPathVar, CountJsonPathVars,
4267 1006 : js, scan->errorOnError,
4268 : &planstate->found,
4269 : true);
4270 :
4271 1000 : MemoryContextSwitchTo(oldcxt);
4272 :
4273 1000 : if (jperIsError(res))
4274 : {
4275 : Assert(!scan->errorOnError);
4276 18 : JsonValueListClear(&planstate->found);
4277 : }
4278 :
4279 : /* Reset plan iterator to the beginning of the item list */
4280 1000 : JsonValueListInitIterator(&planstate->found, &planstate->iter);
4281 1000 : planstate->current.value = PointerGetDatum(NULL);
4282 1000 : planstate->current.isnull = true;
4283 1000 : planstate->ordinal = 0;
4284 1000 : }
4285 :
4286 : /*
4287 : * Fetch next row from a JsonTablePlan.
4288 : *
4289 : * Returns false if the plan has run out of rows, true otherwise.
4290 : */
4291 : static bool
4292 4208 : JsonTablePlanNextRow(JsonTablePlanState *planstate)
4293 : {
4294 4208 : if (IsA(planstate->plan, JsonTablePathScan))
4295 3290 : return JsonTablePlanScanNextRow(planstate);
4296 918 : else if (IsA(planstate->plan, JsonTableSiblingJoin))
4297 918 : return JsonTablePlanJoinNextRow(planstate);
4298 : else
4299 0 : elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
4300 :
4301 : Assert(false);
4302 : /* Appease compiler */
4303 : return false;
4304 : }
4305 :
4306 : /*
4307 : * Fetch next row from a JsonTablePlan's path evaluation result and from
4308 : * any child nested path(s).
4309 : *
4310 : * Returns true if any of the paths (this or the nested) has more rows to
4311 : * return.
4312 : *
4313 : * By fetching the nested path(s)'s rows based on the parent row at each
4314 : * level, this essentially joins the rows of different levels. If a nested
4315 : * path at a given level has no matching rows, the columns of that level will
4316 : * compute to NULL, making it an OUTER join.
4317 : */
4318 : static bool
4319 3290 : JsonTablePlanScanNextRow(JsonTablePlanState *planstate)
4320 : {
4321 : JsonbValue *jbv;
4322 : MemoryContext oldcxt;
4323 :
4324 : /*
4325 : * If planstate already has an active row and there is a nested plan,
4326 : * check if it has an active row to join with the former.
4327 : */
4328 3290 : if (!planstate->current.isnull)
4329 : {
4330 1840 : if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
4331 486 : return true;
4332 : }
4333 :
4334 : /* Fetch new row from the list of found values to set as active. */
4335 2804 : jbv = JsonValueListNext(&planstate->found, &planstate->iter);
4336 :
4337 : /* End of list? */
4338 2804 : if (jbv == NULL)
4339 : {
4340 1360 : planstate->current.value = PointerGetDatum(NULL);
4341 1360 : planstate->current.isnull = true;
4342 1360 : return false;
4343 : }
4344 :
4345 : /*
4346 : * Set current row item for subsequent JsonTableGetValue() calls for
4347 : * evaluating individual columns.
4348 : */
4349 1444 : oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4350 1444 : planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
4351 1444 : planstate->current.isnull = false;
4352 1444 : MemoryContextSwitchTo(oldcxt);
4353 :
4354 : /* Next row! */
4355 1444 : planstate->ordinal++;
4356 :
4357 : /* Process nested plan(s), if any. */
4358 1444 : if (planstate->nested)
4359 : {
4360 : /* Re-evaluate the nested path using the above parent row. */
4361 350 : JsonTableResetNestedPlan(planstate->nested);
4362 :
4363 : /*
4364 : * Now fetch the nested plan's current row to be joined against the
4365 : * parent row. Any further nested plans' paths will be re-evaluated
4366 : * recursively, level at a time, after setting each nested plan's
4367 : * current row.
4368 : */
4369 350 : (void) JsonTablePlanNextRow(planstate->nested);
4370 : }
4371 :
4372 : /* There are more rows. */
4373 1444 : return true;
4374 : }
4375 :
4376 : /*
4377 : * Re-evaluate the row pattern of a nested plan using the new parent row
4378 : * pattern.
4379 : */
4380 : static void
4381 614 : JsonTableResetNestedPlan(JsonTablePlanState *planstate)
4382 : {
4383 : /* This better be a child plan. */
4384 : Assert(planstate->parent != NULL);
4385 614 : if (IsA(planstate->plan, JsonTablePathScan))
4386 : {
4387 482 : JsonTablePlanState *parent = planstate->parent;
4388 :
4389 482 : if (!parent->current.isnull)
4390 482 : JsonTableResetRowPattern(planstate, parent->current.value);
4391 :
4392 : /*
4393 : * If this plan itself has a child nested plan, it will be reset when
4394 : * the caller calls JsonTablePlanNextRow() on this plan.
4395 : */
4396 : }
4397 132 : else if (IsA(planstate->plan, JsonTableSiblingJoin))
4398 : {
4399 132 : JsonTableResetNestedPlan(planstate->left);
4400 132 : JsonTableResetNestedPlan(planstate->right);
4401 : }
4402 614 : }
4403 :
4404 : /*
4405 : * Fetch the next row from a JsonTableSiblingJoin.
4406 : *
4407 : * This is essentially a UNION between the rows from left and right siblings.
4408 : */
4409 : static bool
4410 918 : JsonTablePlanJoinNextRow(JsonTablePlanState *planstate)
4411 : {
4412 :
4413 : /* Fetch row from left sibling. */
4414 918 : if (!JsonTablePlanNextRow(planstate->left))
4415 : {
4416 : /*
4417 : * Left sibling ran out of rows, so start fetching from the right
4418 : * sibling.
4419 : */
4420 546 : if (!JsonTablePlanNextRow(planstate->right))
4421 : {
4422 : /* Right sibling ran out of row, so there are more rows. */
4423 324 : return false;
4424 : }
4425 : }
4426 :
4427 594 : return true;
4428 : }
4429 :
4430 : /*
4431 : * JsonTableFetchRow
4432 : * Prepare the next "current" row for upcoming GetValue calls.
4433 : *
4434 : * Returns false if no more rows can be returned.
4435 : */
4436 : static bool
4437 1558 : JsonTableFetchRow(TableFuncScanState *state)
4438 : {
4439 : JsonTableExecContext *cxt =
4440 1558 : GetJsonTableExecContext(state, "JsonTableFetchRow");
4441 :
4442 1558 : return JsonTablePlanNextRow(cxt->rootplanstate);
4443 : }
4444 :
4445 : /*
4446 : * JsonTableGetValue
4447 : * Return the value for column number 'colnum' for the current row.
4448 : *
4449 : * This leaks memory, so be sure to reset often the context in which it's
4450 : * called.
4451 : */
4452 : static Datum
4453 4688 : JsonTableGetValue(TableFuncScanState *state, int colnum,
4454 : Oid typid, int32 typmod, bool *isnull)
4455 : {
4456 : JsonTableExecContext *cxt =
4457 4688 : GetJsonTableExecContext(state, "JsonTableGetValue");
4458 4688 : ExprContext *econtext = state->ss.ps.ps_ExprContext;
4459 4688 : ExprState *estate = list_nth(state->colvalexprs, colnum);
4460 4688 : JsonTablePlanState *planstate = cxt->colplanstates[colnum];
4461 4688 : JsonTablePlanRowSource *current = &planstate->current;
4462 : Datum result;
4463 :
4464 : /* Row pattern value is NULL */
4465 4688 : if (current->isnull)
4466 : {
4467 894 : result = (Datum) 0;
4468 894 : *isnull = true;
4469 : }
4470 : /* Evaluate JsonExpr. */
4471 3794 : else if (estate)
4472 : {
4473 3344 : Datum saved_caseValue = econtext->caseValue_datum;
4474 3344 : bool saved_caseIsNull = econtext->caseValue_isNull;
4475 :
4476 : /* Pass the row pattern value via CaseTestExpr. */
4477 3344 : econtext->caseValue_datum = current->value;
4478 3344 : econtext->caseValue_isNull = false;
4479 :
4480 3344 : result = ExecEvalExpr(estate, econtext, isnull);
4481 :
4482 3254 : econtext->caseValue_datum = saved_caseValue;
4483 3254 : econtext->caseValue_isNull = saved_caseIsNull;
4484 : }
4485 : /* ORDINAL column */
4486 : else
4487 : {
4488 450 : result = Int32GetDatum(planstate->ordinal);
4489 450 : *isnull = false;
4490 : }
4491 :
4492 4598 : return result;
4493 : }
|