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