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