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 5396 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
557 : {
558 : FuncCallContext *funcctx;
559 : JsonValueListIterator *iter;
560 : JsonbValue *v;
561 :
562 5396 : 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 3040 : funcctx = SRF_FIRSTCALL_INIT();
572 3040 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
573 :
574 3040 : jb = PG_GETARG_JSONB_P_COPY(0);
575 3040 : jp = PG_GETARG_JSONPATH_P_COPY(1);
576 3040 : vars = PG_GETARG_JSONB_P_COPY(2);
577 3040 : silent = PG_GETARG_BOOL(3);
578 :
579 3040 : found = palloc_object(JsonValueList);
580 3040 : JsonValueListInit(found);
581 :
582 3040 : (void) executeJsonPath(jp, vars, getJsonPathVariableFromJsonb,
583 : countVariablesFromJsonb,
584 3040 : jb, !silent, found, tz);
585 :
586 2012 : iter = palloc_object(JsonValueListIterator);
587 2012 : JsonValueListInitIterator(found, iter);
588 :
589 2012 : funcctx->user_fctx = iter;
590 :
591 2012 : MemoryContextSwitchTo(oldcontext);
592 : }
593 :
594 4368 : funcctx = SRF_PERCALL_SETUP();
595 4368 : iter = funcctx->user_fctx;
596 :
597 4368 : v = JsonValueListNext(iter);
598 :
599 4368 : if (v == NULL)
600 2012 : SRF_RETURN_DONE(funcctx);
601 :
602 2356 : SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
603 : }
604 :
605 : Datum
606 4392 : jsonb_path_query(PG_FUNCTION_ARGS)
607 : {
608 4392 : 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 133018 : 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 133018 : jspInit(&jsp, path);
726 :
727 133018 : if (!JsonbExtractScalar(&json->root, &jbv))
728 128964 : JsonbInitBinary(&jbv, json);
729 :
730 133018 : cxt.vars = vars;
731 133018 : cxt.getVar = getVar;
732 133018 : cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
733 133018 : cxt.ignoreStructuralErrors = cxt.laxMode;
734 133018 : cxt.root = &jbv;
735 133018 : cxt.current = &jbv;
736 133018 : cxt.baseObject.jbc = NULL;
737 133018 : cxt.baseObject.id = 0;
738 : /* 1 + number of base objects in vars */
739 133018 : cxt.lastGeneratedObjectId = 1 + countVars(vars);
740 133010 : cxt.innermostArraySize = -1;
741 133010 : cxt.throwErrors = throwErrors;
742 133010 : cxt.useTz = useTz;
743 :
744 133010 : 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 132845 : res = executeItem(&cxt, &jsp, &jbv, result);
767 :
768 : Assert(!throwErrors || !jperIsError(res));
769 :
770 131777 : return res;
771 : }
772 :
773 : /*
774 : * Execute jsonpath with automatic unwrapping of current item in lax mode.
775 : */
776 : static JsonPathExecResult
777 398387 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
778 : JsonbValue *jb, JsonValueList *found)
779 : {
780 398387 : 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 404091 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
790 : JsonbValue *jb, JsonValueList *found, bool unwrap)
791 : {
792 : JsonPathItem elem;
793 404091 : JsonPathExecResult res = jperNotFound;
794 : JsonBaseObjectInfo baseObject;
795 :
796 404091 : check_stack_depth();
797 404091 : CHECK_FOR_INTERRUPTS();
798 :
799 404091 : 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 141059 : case jpiRoot:
1096 141059 : jb = cxt->root;
1097 141059 : baseObject = setBaseObject(cxt, jb, 0);
1098 141059 : res = executeNextItem(cxt, jsp, NULL, jb, found);
1099 140043 : cxt->baseObject = baseObject;
1100 140043 : 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 510 : 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 510 : if (unwrap && JsonbType(jb) == jbvArray)
1695 40 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1696 :
1697 470 : 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 394697 : 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 294224 : executeNextItem(JsonPathExecContext *cxt,
1733 : JsonPathItem *cur, JsonPathItem *next,
1734 : JsonbValue *v, JsonValueList *found)
1735 : {
1736 : JsonPathItem elem;
1737 : bool hasNext;
1738 :
1739 294224 : if (!cur)
1740 0 : hasNext = next != NULL;
1741 294224 : else if (next)
1742 115365 : hasNext = jspHasNext(cur);
1743 : else
1744 : {
1745 178859 : next = &elem;
1746 178859 : hasNext = jspGetNext(cur, next);
1747 : }
1748 :
1749 294224 : if (hasNext)
1750 131689 : 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 470 : executeStringInternalMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
2906 : JsonbValue *jb, JsonValueList *found)
2907 : {
2908 : JsonbValue jbv;
2909 : bool hasNext;
2910 470 : 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 470 : 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 470 : 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 270 : 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 270 : 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 8 : case jpiStrSplitPart:
3018 : {
3019 : char *from_str;
3020 : Numeric n;
3021 :
3022 8 : jspGetLeftArg(jsp, &elem);
3023 8 : if (elem.type != jpiString)
3024 0 : elog(ERROR, "invalid jsonpath item type for .split_part()");
3025 :
3026 8 : from_str = jspGetString(&elem, NULL);
3027 :
3028 8 : jspGetRightArg(jsp, &elem);
3029 8 : if (elem.type != jpiNumeric)
3030 0 : elog(ERROR, "invalid jsonpath item type for .split_part()");
3031 :
3032 8 : n = jspGetNumeric(&elem);
3033 :
3034 8 : resStr = TextDatumGetCString(DirectFunctionCall3Coll(split_part,
3035 : DEFAULT_COLLATION_OID,
3036 : str,
3037 : CStringGetTextDatum(from_str),
3038 : DirectFunctionCall1(numeric_int4, NumericGetDatum(n))));
3039 8 : break;
3040 : }
3041 0 : default:
3042 0 : elog(ERROR, "unsupported jsonpath item type: %d", jsp->type);
3043 : }
3044 :
3045 270 : if (resStr)
3046 270 : res = jperOk;
3047 :
3048 270 : hasNext = jspGetNext(jsp, &elem);
3049 :
3050 270 : if (!hasNext && !found)
3051 0 : return res;
3052 :
3053 270 : jbv.type = jbvString;
3054 270 : jbv.val.string.val = resStr;
3055 270 : jbv.val.string.len = strlen(resStr);
3056 :
3057 270 : return executeNextItem(cxt, jsp, &elem, &jbv, found);
3058 : }
3059 :
3060 : /*
3061 : * Implementation of .keyvalue() method.
3062 : *
3063 : * .keyvalue() method returns a sequence of object's key-value pairs in the
3064 : * following format: '{ "key": key, "value": value, "id": id }'.
3065 : *
3066 : * "id" field is an object identifier which is constructed from the two parts:
3067 : * base object id and its binary offset in base object's jsonb:
3068 : * id = 10000000000 * base_object_id + obj_offset_in_base_object
3069 : *
3070 : * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
3071 : * (maximal offset in jsonb). Decimal multiplier is used here to improve the
3072 : * readability of identifiers.
3073 : *
3074 : * Base object is usually a root object of the path: context item '$' or path
3075 : * variable '$var', literals can't produce objects for now. But if the path
3076 : * contains generated objects (.keyvalue() itself, for example), then they
3077 : * become base object for the subsequent .keyvalue().
3078 : *
3079 : * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
3080 : * of variables (see getJsonPathVariable()). Ids for generated objects
3081 : * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
3082 : */
3083 : static JsonPathExecResult
3084 58 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
3085 : JsonbValue *jb, JsonValueList *found)
3086 : {
3087 58 : JsonPathExecResult res = jperNotFound;
3088 : JsonPathItem next;
3089 : JsonbContainer *jbc;
3090 : JsonbValue key;
3091 : JsonbValue val;
3092 : JsonbValue idval;
3093 : JsonbValue keystr;
3094 : JsonbValue valstr;
3095 : JsonbValue idstr;
3096 : JsonbIterator *it;
3097 : JsonbIteratorToken tok;
3098 : int64 id;
3099 : bool hasNext;
3100 :
3101 58 : if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
3102 16 : RETURN_ERROR(ereport(ERROR,
3103 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
3104 : errmsg("jsonpath item method .%s() can only be applied to an object",
3105 : jspOperationName(jsp->type)))));
3106 :
3107 42 : jbc = jb->val.binary.data;
3108 :
3109 42 : if (!JsonContainerSize(jbc))
3110 12 : return jperNotFound; /* no key-value pairs */
3111 :
3112 30 : hasNext = jspGetNext(jsp, &next);
3113 :
3114 30 : keystr.type = jbvString;
3115 30 : keystr.val.string.val = "key";
3116 30 : keystr.val.string.len = 3;
3117 :
3118 30 : valstr.type = jbvString;
3119 30 : valstr.val.string.val = "value";
3120 30 : valstr.val.string.len = 5;
3121 :
3122 30 : idstr.type = jbvString;
3123 30 : idstr.val.string.val = "id";
3124 30 : idstr.val.string.len = 2;
3125 :
3126 : /* construct object id from its base object and offset inside that */
3127 30 : id = jb->type != jbvBinary ? 0 :
3128 30 : (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
3129 30 : id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
3130 :
3131 30 : idval.type = jbvNumeric;
3132 30 : idval.val.numeric = int64_to_numeric(id);
3133 :
3134 30 : it = JsonbIteratorInit(jbc);
3135 :
3136 116 : while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
3137 : {
3138 : JsonBaseObjectInfo baseObject;
3139 : JsonbValue obj;
3140 : JsonbInState ps;
3141 : Jsonb *jsonb;
3142 :
3143 96 : if (tok != WJB_KEY)
3144 50 : continue;
3145 :
3146 46 : res = jperOk;
3147 :
3148 46 : if (!hasNext && !found)
3149 10 : break;
3150 :
3151 41 : tok = JsonbIteratorNext(&it, &val, true);
3152 : Assert(tok == WJB_VALUE);
3153 :
3154 41 : memset(&ps, 0, sizeof(ps));
3155 :
3156 41 : pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
3157 :
3158 41 : pushJsonbValue(&ps, WJB_KEY, &keystr);
3159 41 : pushJsonbValue(&ps, WJB_VALUE, &key);
3160 :
3161 41 : pushJsonbValue(&ps, WJB_KEY, &valstr);
3162 41 : pushJsonbValue(&ps, WJB_VALUE, &val);
3163 :
3164 41 : pushJsonbValue(&ps, WJB_KEY, &idstr);
3165 41 : pushJsonbValue(&ps, WJB_VALUE, &idval);
3166 :
3167 41 : pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
3168 :
3169 41 : jsonb = JsonbValueToJsonb(ps.result);
3170 :
3171 41 : JsonbInitBinary(&obj, jsonb);
3172 :
3173 41 : baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
3174 :
3175 41 : res = executeNextItem(cxt, jsp, &next, &obj, found);
3176 :
3177 41 : cxt->baseObject = baseObject;
3178 :
3179 41 : if (jperIsError(res))
3180 0 : return res;
3181 :
3182 41 : if (res == jperOk && !found)
3183 5 : break;
3184 : }
3185 :
3186 30 : return res;
3187 : }
3188 :
3189 : /*
3190 : * Convert boolean execution status 'res' to a boolean JSON item and execute
3191 : * next jsonpath.
3192 : */
3193 : static JsonPathExecResult
3194 68139 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
3195 : JsonValueList *found, JsonPathBool res)
3196 : {
3197 : JsonPathItem next;
3198 : JsonbValue jbv;
3199 :
3200 68139 : if (!jspGetNext(jsp, &next) && !found)
3201 13 : return jperOk; /* found singleton boolean value */
3202 :
3203 68126 : if (res == jpbUnknown)
3204 : {
3205 23 : jbv.type = jbvNull;
3206 : }
3207 : else
3208 : {
3209 68103 : jbv.type = jbvBool;
3210 68103 : jbv.val.boolean = res == jpbTrue;
3211 : }
3212 :
3213 68126 : return executeNextItem(cxt, jsp, &next, &jbv, found);
3214 : }
3215 :
3216 : /*
3217 : * Convert jsonpath's scalar or variable node to actual jsonb value.
3218 : *
3219 : * If node is a variable then its id returned, otherwise 0 returned.
3220 : */
3221 : static void
3222 40705 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
3223 : JsonbValue *value)
3224 : {
3225 40705 : switch (item->type)
3226 : {
3227 5036 : case jpiNull:
3228 5036 : value->type = jbvNull;
3229 5036 : break;
3230 950 : case jpiBool:
3231 950 : value->type = jbvBool;
3232 950 : value->val.boolean = jspGetBool(item);
3233 950 : break;
3234 13328 : case jpiNumeric:
3235 13328 : value->type = jbvNumeric;
3236 13328 : value->val.numeric = jspGetNumeric(item);
3237 13328 : break;
3238 16115 : case jpiString:
3239 16115 : value->type = jbvString;
3240 32230 : value->val.string.val = jspGetString(item,
3241 16115 : &value->val.string.len);
3242 16115 : break;
3243 5276 : case jpiVariable:
3244 5276 : getJsonPathVariable(cxt, item, value);
3245 5244 : return;
3246 0 : default:
3247 0 : elog(ERROR, "unexpected jsonpath item type");
3248 : }
3249 : }
3250 :
3251 : /*
3252 : * Returns the computed value of a JSON path variable with given name.
3253 : */
3254 : static JsonbValue *
3255 1740 : GetJsonPathVar(void *cxt, char *varName, int varNameLen,
3256 : JsonbValue *baseObject, int *baseObjectId)
3257 : {
3258 1740 : JsonPathVariable *var = NULL;
3259 1740 : List *vars = cxt;
3260 : ListCell *lc;
3261 : JsonbValue *result;
3262 1740 : int id = 1;
3263 :
3264 2504 : foreach(lc, vars)
3265 : {
3266 2492 : JsonPathVariable *curvar = lfirst(lc);
3267 :
3268 2492 : if (curvar->namelen == varNameLen &&
3269 2484 : strncmp(curvar->name, varName, varNameLen) == 0)
3270 : {
3271 1728 : var = curvar;
3272 1728 : break;
3273 : }
3274 :
3275 764 : id++;
3276 : }
3277 :
3278 1740 : if (var == NULL)
3279 : {
3280 12 : *baseObjectId = -1;
3281 12 : return NULL;
3282 : }
3283 :
3284 1728 : result = palloc_object(JsonbValue);
3285 1728 : if (var->isnull)
3286 : {
3287 0 : *baseObjectId = 0;
3288 0 : result->type = jbvNull;
3289 : }
3290 : else
3291 1728 : JsonItemFromDatum(var->value, var->typid, var->typmod, result);
3292 :
3293 1728 : *baseObject = *result;
3294 1728 : *baseObjectId = id;
3295 :
3296 1728 : return result;
3297 : }
3298 :
3299 : static int
3300 4204 : CountJsonPathVars(void *cxt)
3301 : {
3302 4204 : List *vars = (List *) cxt;
3303 :
3304 4204 : return list_length(vars);
3305 : }
3306 :
3307 :
3308 : /*
3309 : * Initialize JsonbValue to pass to jsonpath executor from given
3310 : * datum value of the specified type.
3311 : */
3312 : static void
3313 1728 : JsonItemFromDatum(Datum val, Oid typid, int32 typmod, JsonbValue *res)
3314 : {
3315 1728 : switch (typid)
3316 : {
3317 0 : case BOOLOID:
3318 0 : res->type = jbvBool;
3319 0 : res->val.boolean = DatumGetBool(val);
3320 0 : break;
3321 0 : case NUMERICOID:
3322 0 : JsonbValueInitNumericDatum(res, val);
3323 0 : break;
3324 0 : case INT2OID:
3325 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int2_numeric, val));
3326 0 : break;
3327 1660 : case INT4OID:
3328 1660 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int4_numeric, val));
3329 1660 : break;
3330 0 : case INT8OID:
3331 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(int8_numeric, val));
3332 0 : break;
3333 0 : case FLOAT4OID:
3334 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float4_numeric, val));
3335 0 : break;
3336 0 : case FLOAT8OID:
3337 0 : JsonbValueInitNumericDatum(res, DirectFunctionCall1(float8_numeric, val));
3338 0 : break;
3339 8 : case TEXTOID:
3340 : case VARCHAROID:
3341 8 : res->type = jbvString;
3342 8 : res->val.string.val = VARDATA_ANY(DatumGetPointer(val));
3343 8 : res->val.string.len = VARSIZE_ANY_EXHDR(DatumGetPointer(val));
3344 8 : break;
3345 48 : case DATEOID:
3346 : case TIMEOID:
3347 : case TIMETZOID:
3348 : case TIMESTAMPOID:
3349 : case TIMESTAMPTZOID:
3350 48 : res->type = jbvDatetime;
3351 48 : res->val.datetime.value = val;
3352 48 : res->val.datetime.typid = typid;
3353 48 : res->val.datetime.typmod = typmod;
3354 48 : res->val.datetime.tz = 0;
3355 48 : break;
3356 12 : case JSONBOID:
3357 : {
3358 12 : JsonbValue *jbv = res;
3359 12 : Jsonb *jb = DatumGetJsonbP(val);
3360 :
3361 12 : if (JsonContainerIsScalar(&jb->root))
3362 : {
3363 : bool result PG_USED_FOR_ASSERTS_ONLY;
3364 :
3365 12 : result = JsonbExtractScalar(&jb->root, jbv);
3366 : Assert(result);
3367 : }
3368 : else
3369 0 : JsonbInitBinary(jbv, jb);
3370 12 : break;
3371 : }
3372 0 : case JSONOID:
3373 : {
3374 0 : text *txt = DatumGetTextP(val);
3375 0 : char *str = text_to_cstring(txt);
3376 : Jsonb *jb;
3377 :
3378 0 : jb = DatumGetJsonbP(DirectFunctionCall1(jsonb_in,
3379 : CStringGetDatum(str)));
3380 0 : pfree(str);
3381 :
3382 0 : JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
3383 0 : break;
3384 : }
3385 0 : default:
3386 0 : ereport(ERROR,
3387 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3388 : errmsg("could not convert value of type %s to jsonpath",
3389 : format_type_be(typid)));
3390 : }
3391 1728 : }
3392 :
3393 : /* Initialize numeric value from the given datum */
3394 : static void
3395 1660 : JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num)
3396 : {
3397 1660 : jbv->type = jbvNumeric;
3398 1660 : jbv->val.numeric = DatumGetNumeric(num);
3399 1660 : }
3400 :
3401 : /*
3402 : * Get the value of variable passed to jsonpath executor
3403 : */
3404 : static void
3405 5276 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
3406 : JsonbValue *value)
3407 : {
3408 : char *varName;
3409 : int varNameLength;
3410 : JsonbValue baseObject;
3411 : int baseObjectId;
3412 : JsonbValue *v;
3413 :
3414 : Assert(variable->type == jpiVariable);
3415 5276 : varName = jspGetString(variable, &varNameLength);
3416 :
3417 10552 : if (cxt->vars == NULL ||
3418 5276 : (v = cxt->getVar(cxt->vars, varName, varNameLength,
3419 : &baseObject, &baseObjectId)) == NULL)
3420 32 : ereport(ERROR,
3421 : (errcode(ERRCODE_UNDEFINED_OBJECT),
3422 : errmsg("could not find jsonpath variable \"%s\"",
3423 : pnstrdup(varName, varNameLength))));
3424 :
3425 5244 : if (baseObjectId > 0)
3426 : {
3427 5244 : *value = *v;
3428 5244 : setBaseObject(cxt, &baseObject, baseObjectId);
3429 : }
3430 5244 : }
3431 :
3432 : /*
3433 : * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
3434 : * is specified as a jsonb value.
3435 : */
3436 : static JsonbValue *
3437 3536 : getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
3438 : JsonbValue *baseObject, int *baseObjectId)
3439 : {
3440 3536 : Jsonb *vars = varsJsonb;
3441 : JsonbValue tmp;
3442 : JsonbValue *result;
3443 :
3444 3536 : tmp.type = jbvString;
3445 3536 : tmp.val.string.val = varName;
3446 3536 : tmp.val.string.len = varNameLength;
3447 :
3448 3536 : result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
3449 :
3450 3536 : if (result == NULL)
3451 : {
3452 20 : *baseObjectId = -1;
3453 20 : return NULL;
3454 : }
3455 :
3456 3516 : *baseObjectId = 1;
3457 3516 : JsonbInitBinary(baseObject, vars);
3458 :
3459 3516 : return result;
3460 : }
3461 :
3462 : /*
3463 : * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
3464 : * is specified as a jsonb value.
3465 : */
3466 : static int
3467 128814 : countVariablesFromJsonb(void *varsJsonb)
3468 : {
3469 128814 : Jsonb *vars = varsJsonb;
3470 :
3471 128814 : if (vars && !JsonContainerIsObject(&vars->root))
3472 : {
3473 8 : ereport(ERROR,
3474 : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3475 : errmsg("\"vars\" argument is not an object"),
3476 : errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
3477 : }
3478 :
3479 : /* count of base objects */
3480 128806 : return vars != NULL ? 1 : 0;
3481 : }
3482 :
3483 : /**************** Support functions for JsonPath execution *****************/
3484 :
3485 : /*
3486 : * Returns the size of an array item, or -1 if item is not an array.
3487 : */
3488 : static int
3489 381 : JsonbArraySize(JsonbValue *jb)
3490 : {
3491 : Assert(jb->type != jbvArray);
3492 :
3493 381 : if (jb->type == jbvBinary)
3494 : {
3495 353 : JsonbContainer *jbc = jb->val.binary.data;
3496 :
3497 353 : if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
3498 345 : return JsonContainerSize(jbc);
3499 : }
3500 :
3501 36 : return -1;
3502 : }
3503 :
3504 : /* Comparison predicate callback. */
3505 : static JsonPathBool
3506 14593 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
3507 : {
3508 14593 : JsonPathExecContext *cxt = (JsonPathExecContext *) p;
3509 :
3510 14593 : return compareItems(cmp->type, lv, rv, cxt->useTz);
3511 : }
3512 :
3513 : /*
3514 : * Perform per-byte comparison of two strings.
3515 : */
3516 : static int
3517 2304 : binaryCompareStrings(const char *s1, int len1,
3518 : const char *s2, int len2)
3519 : {
3520 : int cmp;
3521 :
3522 2304 : cmp = memcmp(s1, s2, Min(len1, len2));
3523 :
3524 2304 : if (cmp != 0)
3525 1312 : return cmp;
3526 :
3527 992 : if (len1 == len2)
3528 192 : return 0;
3529 :
3530 800 : return len1 < len2 ? -1 : 1;
3531 : }
3532 :
3533 : /*
3534 : * Compare two strings in the current server encoding using Unicode codepoint
3535 : * collation.
3536 : */
3537 : static int
3538 2304 : compareStrings(const char *mbstr1, int mblen1,
3539 : const char *mbstr2, int mblen2)
3540 : {
3541 4608 : if (GetDatabaseEncoding() == PG_SQL_ASCII ||
3542 2304 : GetDatabaseEncoding() == PG_UTF8)
3543 : {
3544 : /*
3545 : * It's known property of UTF-8 strings that their per-byte comparison
3546 : * result matches codepoints comparison result. ASCII can be
3547 : * considered as special case of UTF-8.
3548 : */
3549 2304 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3550 : }
3551 : else
3552 : {
3553 : char *utf8str1,
3554 : *utf8str2;
3555 : int cmp,
3556 : utf8len1,
3557 : utf8len2;
3558 :
3559 : /*
3560 : * We have to convert other encodings to UTF-8 first, then compare.
3561 : * Input strings may be not null-terminated and pg_server_to_any() may
3562 : * return them "as is". So, use strlen() only if there is real
3563 : * conversion.
3564 : */
3565 0 : utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
3566 0 : utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
3567 0 : utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
3568 0 : utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
3569 :
3570 0 : cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
3571 :
3572 : /*
3573 : * If pg_server_to_any() did no real conversion, then we actually
3574 : * compared original strings. So, we already done.
3575 : */
3576 0 : if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
3577 0 : return cmp;
3578 :
3579 : /* Free memory if needed */
3580 0 : if (mbstr1 != utf8str1)
3581 0 : pfree(utf8str1);
3582 0 : if (mbstr2 != utf8str2)
3583 0 : pfree(utf8str2);
3584 :
3585 : /*
3586 : * When all Unicode codepoints are equal, return result of binary
3587 : * comparison. In some edge cases, same characters may have different
3588 : * representations in encoding. Then our behavior could diverge from
3589 : * standard. However, that allow us to do simple binary comparison
3590 : * for "==" operator, which is performance critical in typical cases.
3591 : * In future to implement strict standard conformance, we can do
3592 : * normalization of input JSON strings.
3593 : */
3594 0 : if (cmp == 0)
3595 0 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3596 : else
3597 0 : return cmp;
3598 : }
3599 : }
3600 :
3601 : /*
3602 : * Compare two SQL/JSON items using comparison operation 'op'.
3603 : */
3604 : static JsonPathBool
3605 14593 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
3606 : {
3607 : int cmp;
3608 : bool res;
3609 :
3610 14593 : if (jb1->type != jb2->type)
3611 : {
3612 2096 : if (jb1->type == jbvNull || jb2->type == jbvNull)
3613 :
3614 : /*
3615 : * Equality and order comparison of nulls to non-nulls returns
3616 : * always false, but inequality comparison returns true.
3617 : */
3618 1918 : return op == jpiNotEqual ? jpbTrue : jpbFalse;
3619 :
3620 : /* Non-null items of different types are not comparable. */
3621 178 : return jpbUnknown;
3622 : }
3623 :
3624 12497 : switch (jb1->type)
3625 : {
3626 124 : case jbvNull:
3627 124 : cmp = 0;
3628 124 : break;
3629 580 : case jbvBool:
3630 844 : cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
3631 264 : jb1->val.boolean ? 1 : -1;
3632 580 : break;
3633 2749 : case jbvNumeric:
3634 2749 : cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
3635 2749 : break;
3636 6620 : case jbvString:
3637 6620 : if (op == jpiEqual)
3638 4316 : return jb1->val.string.len != jb2->val.string.len ||
3639 2388 : memcmp(jb1->val.string.val,
3640 2388 : jb2->val.string.val,
3641 4316 : jb1->val.string.len) ? jpbFalse : jpbTrue;
3642 :
3643 2304 : cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
3644 2304 : jb2->val.string.val, jb2->val.string.len);
3645 2304 : break;
3646 2416 : case jbvDatetime:
3647 : {
3648 : bool cast_error;
3649 :
3650 2416 : cmp = compareDatetime(jb1->val.datetime.value,
3651 : jb1->val.datetime.typid,
3652 : jb2->val.datetime.value,
3653 : jb2->val.datetime.typid,
3654 : useTz,
3655 : &cast_error);
3656 :
3657 2356 : if (cast_error)
3658 204 : return jpbUnknown;
3659 : }
3660 2152 : break;
3661 :
3662 8 : case jbvBinary:
3663 : case jbvArray:
3664 : case jbvObject:
3665 8 : return jpbUnknown; /* non-scalars are not comparable */
3666 :
3667 0 : default:
3668 0 : elog(ERROR, "invalid jsonb value type %d", jb1->type);
3669 : }
3670 :
3671 7909 : switch (op)
3672 : {
3673 1818 : case jpiEqual:
3674 1818 : res = (cmp == 0);
3675 1818 : break;
3676 4 : case jpiNotEqual:
3677 4 : res = (cmp != 0);
3678 4 : break;
3679 1418 : case jpiLess:
3680 1418 : res = (cmp < 0);
3681 1418 : break;
3682 1105 : case jpiGreater:
3683 1105 : res = (cmp > 0);
3684 1105 : break;
3685 1361 : case jpiLessOrEqual:
3686 1361 : res = (cmp <= 0);
3687 1361 : break;
3688 2203 : case jpiGreaterOrEqual:
3689 2203 : res = (cmp >= 0);
3690 2203 : break;
3691 0 : default:
3692 0 : elog(ERROR, "unrecognized jsonpath operation: %d", op);
3693 : return jpbUnknown;
3694 : }
3695 :
3696 7909 : return res ? jpbTrue : jpbFalse;
3697 : }
3698 :
3699 : /* Compare two numerics */
3700 : static int
3701 2749 : compareNumeric(Numeric a, Numeric b)
3702 : {
3703 2749 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
3704 : NumericGetDatum(a),
3705 : NumericGetDatum(b)));
3706 : }
3707 :
3708 : static JsonbValue *
3709 1248 : copyJsonbValue(JsonbValue *src)
3710 : {
3711 1248 : JsonbValue *dst = palloc_object(JsonbValue);
3712 :
3713 1248 : *dst = *src;
3714 :
3715 1248 : return dst;
3716 : }
3717 :
3718 : /*
3719 : * Execute array subscript expression and convert resulting numeric item to
3720 : * the integer type with truncation.
3721 : */
3722 : static JsonPathExecResult
3723 362 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
3724 : int32 *index)
3725 : {
3726 : JsonbValue *jbv;
3727 : JsonValueList found;
3728 : JsonPathExecResult res;
3729 : Datum numeric_index;
3730 362 : ErrorSaveContext escontext = {T_ErrorSaveContext};
3731 :
3732 362 : JsonValueListInit(&found);
3733 :
3734 362 : res = executeItem(cxt, jsp, jb, &found);
3735 :
3736 358 : if (jperIsError(res))
3737 : {
3738 0 : JsonValueListClear(&found);
3739 0 : return res;
3740 : }
3741 :
3742 708 : if (!JsonValueListIsSingleton(&found) ||
3743 350 : !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
3744 : {
3745 16 : JsonValueListClear(&found);
3746 16 : RETURN_ERROR(ereport(ERROR,
3747 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3748 : errmsg("jsonpath array subscript is not a single numeric value"))));
3749 : }
3750 :
3751 342 : numeric_index = DirectFunctionCall2(numeric_trunc,
3752 : NumericGetDatum(jbv->val.numeric),
3753 : Int32GetDatum(0));
3754 :
3755 342 : *index = numeric_int4_safe(DatumGetNumeric(numeric_index),
3756 : (Node *) &escontext);
3757 :
3758 342 : JsonValueListClear(&found);
3759 :
3760 342 : if (escontext.error_occurred)
3761 18 : RETURN_ERROR(ereport(ERROR,
3762 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3763 : errmsg("jsonpath array subscript is out of integer range"))));
3764 :
3765 324 : return jperOk;
3766 : }
3767 :
3768 : /* Save base object and its id needed for the execution of .keyvalue(). */
3769 : static JsonBaseObjectInfo
3770 146344 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
3771 : {
3772 146344 : JsonBaseObjectInfo baseObject = cxt->baseObject;
3773 :
3774 146344 : cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
3775 : (JsonbContainer *) jbv->val.binary.data;
3776 146344 : cxt->baseObject.id = id;
3777 :
3778 146344 : return baseObject;
3779 : }
3780 :
3781 : /*
3782 : * JsonValueList support functions
3783 : */
3784 :
3785 : static void
3786 236205 : JsonValueListInit(JsonValueList *jvl)
3787 : {
3788 236205 : jvl->nitems = 0;
3789 236205 : jvl->maxitems = BASE_JVL_ITEMS;
3790 236205 : jvl->next = NULL;
3791 236205 : jvl->last = jvl;
3792 236205 : }
3793 :
3794 : static void
3795 161497 : JsonValueListClear(JsonValueList *jvl)
3796 : {
3797 : JsonValueList *nxt;
3798 :
3799 : /* Release any extra chunks */
3800 161953 : for (JsonValueList *chunk = jvl->next; chunk != NULL; chunk = nxt)
3801 : {
3802 456 : nxt = chunk->next;
3803 456 : pfree(chunk);
3804 : }
3805 : /* ... and reset to empty */
3806 161497 : jvl->nitems = 0;
3807 : Assert(jvl->maxitems == BASE_JVL_ITEMS);
3808 161497 : jvl->next = NULL;
3809 161497 : jvl->last = jvl;
3810 161497 : }
3811 :
3812 : static void
3813 185436 : JsonValueListAppend(JsonValueList *jvl, const JsonbValue *jbv)
3814 : {
3815 185436 : JsonValueList *last = jvl->last;
3816 :
3817 185436 : if (last->nitems < last->maxitems)
3818 : {
3819 : /* there's still room in the last existing chunk */
3820 184542 : last->items[last->nitems] = *jbv;
3821 184542 : last->nitems++;
3822 : }
3823 : else
3824 : {
3825 : /* need a new last chunk */
3826 : JsonValueList *nxt;
3827 : int nxtsize;
3828 :
3829 894 : nxtsize = last->maxitems * 2; /* double the size with each chunk */
3830 894 : nxtsize = Max(nxtsize, MIN_EXTRA_JVL_ITEMS); /* but at least this */
3831 894 : nxt = palloc(offsetof(JsonValueList, items) +
3832 894 : nxtsize * sizeof(JsonbValue));
3833 894 : nxt->nitems = 1;
3834 894 : nxt->maxitems = nxtsize;
3835 894 : nxt->next = NULL;
3836 894 : nxt->items[0] = *jbv;
3837 894 : last->next = nxt;
3838 894 : jvl->last = nxt;
3839 : }
3840 185436 : }
3841 :
3842 : static bool
3843 7518 : JsonValueListIsEmpty(const JsonValueList *jvl)
3844 : {
3845 : /* We need not examine extra chunks for this */
3846 7518 : return (jvl->nitems == 0);
3847 : }
3848 :
3849 : static bool
3850 66476 : JsonValueListIsSingleton(const JsonValueList *jvl)
3851 : {
3852 : #if BASE_JVL_ITEMS > 1
3853 : /* We need not examine extra chunks in this case */
3854 66476 : return (jvl->nitems == 1);
3855 : #else
3856 : return (jvl->nitems == 1 && jvl->next == NULL);
3857 : #endif
3858 : }
3859 :
3860 : static bool
3861 2732 : JsonValueListHasMultipleItems(const JsonValueList *jvl)
3862 : {
3863 : #if BASE_JVL_ITEMS > 1
3864 : /* We need not examine extra chunks in this case */
3865 2732 : return (jvl->nitems > 1);
3866 : #else
3867 : return (jvl->nitems == 1 && jvl->next != NULL);
3868 : #endif
3869 : }
3870 :
3871 : static JsonbValue *
3872 71706 : JsonValueListHead(JsonValueList *jvl)
3873 : {
3874 : Assert(jvl->nitems > 0);
3875 71706 : return &jvl->items[0];
3876 : }
3877 :
3878 : /*
3879 : * JsonValueListIterator functions
3880 : */
3881 :
3882 : static void
3883 140834 : JsonValueListInitIterator(JsonValueList *jvl, JsonValueListIterator *it)
3884 : {
3885 140834 : it->chunk = jvl;
3886 140834 : it->nextitem = 0;
3887 140834 : }
3888 :
3889 : /*
3890 : * Get the next item from the sequence advancing iterator.
3891 : * Returns NULL if no more items.
3892 : */
3893 : static JsonbValue *
3894 222845 : JsonValueListNext(JsonValueListIterator *it)
3895 : {
3896 222845 : if (it->chunk == NULL)
3897 300 : return NULL;
3898 222545 : if (it->nextitem >= it->chunk->nitems)
3899 : {
3900 131891 : it->chunk = it->chunk->next;
3901 131891 : if (it->chunk == NULL)
3902 131122 : return NULL;
3903 769 : it->nextitem = 0;
3904 : Assert(it->chunk->nitems > 0);
3905 : }
3906 91423 : return &it->chunk->items[it->nextitem++];
3907 : }
3908 :
3909 : /*
3910 : * Initialize a binary JsonbValue with the given jsonb container.
3911 : */
3912 : static JsonbValue *
3913 132521 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
3914 : {
3915 132521 : jbv->type = jbvBinary;
3916 132521 : jbv->val.binary.data = &jb->root;
3917 132521 : jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
3918 :
3919 132521 : return jbv;
3920 : }
3921 :
3922 : /*
3923 : * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
3924 : */
3925 : static int
3926 191169 : JsonbType(JsonbValue *jb)
3927 : {
3928 191169 : int type = jb->type;
3929 :
3930 191169 : if (jb->type == jbvBinary)
3931 : {
3932 122443 : JsonbContainer *jbc = jb->val.binary.data;
3933 :
3934 : /* Scalars should be always extracted during jsonpath execution. */
3935 : Assert(!JsonContainerIsScalar(jbc));
3936 :
3937 122443 : if (JsonContainerIsObject(jbc))
3938 119886 : type = jbvObject;
3939 2557 : else if (JsonContainerIsArray(jbc))
3940 2557 : type = jbvArray;
3941 : else
3942 0 : elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
3943 : }
3944 :
3945 191169 : return type;
3946 : }
3947 :
3948 : /* Get scalar of given type or NULL on type mismatch */
3949 : static JsonbValue *
3950 8051 : getScalar(JsonbValue *scalar, enum jbvType type)
3951 : {
3952 : /* Scalars should be always extracted during jsonpath execution. */
3953 : Assert(scalar->type != jbvBinary ||
3954 : !JsonContainerIsScalar(scalar->val.binary.data));
3955 :
3956 8051 : return scalar->type == type ? scalar : NULL;
3957 : }
3958 :
3959 : /* Construct a JSON array from the item list */
3960 : static JsonbValue *
3961 330 : wrapItemsInArray(JsonValueList *items)
3962 : {
3963 330 : JsonbInState ps = {0};
3964 : JsonValueListIterator it;
3965 : JsonbValue *jbv;
3966 :
3967 330 : pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
3968 :
3969 330 : JsonValueListInitIterator(items, &it);
3970 910 : while ((jbv = JsonValueListNext(&it)))
3971 580 : pushJsonbValue(&ps, WJB_ELEM, jbv);
3972 :
3973 330 : pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
3974 :
3975 330 : return ps.result;
3976 : }
3977 :
3978 : /* Check if the timezone required for casting from type1 to type2 is used */
3979 : static void
3980 900 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
3981 : {
3982 900 : if (!useTz)
3983 192 : ereport(ERROR,
3984 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3985 : errmsg("cannot convert value from %s to %s without time zone usage",
3986 : type1, type2),
3987 : errhint("Use *_tz() function for time zone support.")));
3988 708 : }
3989 :
3990 : /* Convert time datum to timetz datum */
3991 : static Datum
3992 168 : castTimeToTimeTz(Datum time, bool useTz)
3993 : {
3994 168 : checkTimezoneIsUsedForCast(useTz, "time", "timetz");
3995 :
3996 144 : return DirectFunctionCall1(time_timetz, time);
3997 : }
3998 :
3999 : /*
4000 : * Compare date to timestamp.
4001 : * Note that this doesn't involve any timezone considerations.
4002 : */
4003 : static int
4004 124 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
4005 : {
4006 124 : return date_cmp_timestamp_internal(date1, ts2);
4007 : }
4008 :
4009 : /*
4010 : * Compare date to timestamptz.
4011 : */
4012 : static int
4013 108 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
4014 : {
4015 108 : checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
4016 :
4017 96 : return date_cmp_timestamptz_internal(date1, tstz2);
4018 : }
4019 :
4020 : /*
4021 : * Compare timestamp to timestamptz.
4022 : */
4023 : static int
4024 168 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
4025 : {
4026 168 : checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
4027 :
4028 144 : return timestamp_cmp_timestamptz_internal(ts1, tstz2);
4029 : }
4030 :
4031 : /*
4032 : * Cross-type comparison of two datetime SQL/JSON items. If items are
4033 : * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
4034 : * If the cast requires timezone and it is not used, then explicit error is thrown.
4035 : */
4036 : static int
4037 2416 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
4038 : bool useTz, bool *cast_error)
4039 : {
4040 : PGFunction cmpfunc;
4041 :
4042 2416 : *cast_error = false;
4043 :
4044 2416 : switch (typid1)
4045 : {
4046 376 : case DATEOID:
4047 376 : switch (typid2)
4048 : {
4049 252 : case DATEOID:
4050 252 : cmpfunc = date_cmp;
4051 :
4052 252 : break;
4053 :
4054 52 : case TIMESTAMPOID:
4055 52 : return cmpDateToTimestamp(DatumGetDateADT(val1),
4056 : DatumGetTimestamp(val2),
4057 : useTz);
4058 :
4059 48 : case TIMESTAMPTZOID:
4060 48 : return cmpDateToTimestampTz(DatumGetDateADT(val1),
4061 : DatumGetTimestampTz(val2),
4062 : useTz);
4063 :
4064 24 : case TIMEOID:
4065 : case TIMETZOID:
4066 24 : *cast_error = true; /* uncomparable types */
4067 24 : return 0;
4068 :
4069 0 : default:
4070 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
4071 : typid2);
4072 : }
4073 252 : break;
4074 :
4075 416 : case TIMEOID:
4076 416 : switch (typid2)
4077 : {
4078 284 : case TIMEOID:
4079 284 : cmpfunc = time_cmp;
4080 :
4081 284 : break;
4082 :
4083 84 : case TIMETZOID:
4084 84 : val1 = castTimeToTimeTz(val1, useTz);
4085 72 : cmpfunc = timetz_cmp;
4086 :
4087 72 : break;
4088 :
4089 48 : case DATEOID:
4090 : case TIMESTAMPOID:
4091 : case TIMESTAMPTZOID:
4092 48 : *cast_error = true; /* uncomparable types */
4093 48 : return 0;
4094 :
4095 0 : default:
4096 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
4097 : typid2);
4098 : }
4099 356 : break;
4100 :
4101 536 : case TIMETZOID:
4102 536 : switch (typid2)
4103 : {
4104 84 : case TIMEOID:
4105 84 : val2 = castTimeToTimeTz(val2, useTz);
4106 72 : cmpfunc = timetz_cmp;
4107 :
4108 72 : break;
4109 :
4110 404 : case TIMETZOID:
4111 404 : cmpfunc = timetz_cmp;
4112 :
4113 404 : break;
4114 :
4115 48 : case DATEOID:
4116 : case TIMESTAMPOID:
4117 : case TIMESTAMPTZOID:
4118 48 : *cast_error = true; /* uncomparable types */
4119 48 : return 0;
4120 :
4121 0 : default:
4122 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
4123 : typid2);
4124 : }
4125 476 : break;
4126 :
4127 476 : case TIMESTAMPOID:
4128 476 : switch (typid2)
4129 : {
4130 72 : case DATEOID:
4131 72 : return -cmpDateToTimestamp(DatumGetDateADT(val2),
4132 : DatumGetTimestamp(val1),
4133 : useTz);
4134 :
4135 284 : case TIMESTAMPOID:
4136 284 : cmpfunc = timestamp_cmp;
4137 :
4138 284 : break;
4139 :
4140 84 : case TIMESTAMPTZOID:
4141 84 : return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
4142 : DatumGetTimestampTz(val2),
4143 : useTz);
4144 :
4145 36 : case TIMEOID:
4146 : case TIMETZOID:
4147 36 : *cast_error = true; /* uncomparable types */
4148 36 : return 0;
4149 :
4150 0 : default:
4151 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
4152 : typid2);
4153 : }
4154 284 : break;
4155 :
4156 612 : case TIMESTAMPTZOID:
4157 612 : switch (typid2)
4158 : {
4159 60 : case DATEOID:
4160 60 : return -cmpDateToTimestampTz(DatumGetDateADT(val2),
4161 : DatumGetTimestampTz(val1),
4162 : useTz);
4163 :
4164 84 : case TIMESTAMPOID:
4165 84 : return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
4166 : DatumGetTimestampTz(val1),
4167 : useTz);
4168 :
4169 420 : case TIMESTAMPTZOID:
4170 420 : cmpfunc = timestamp_cmp;
4171 :
4172 420 : break;
4173 :
4174 48 : case TIMEOID:
4175 : case TIMETZOID:
4176 48 : *cast_error = true; /* uncomparable types */
4177 48 : return 0;
4178 :
4179 0 : default:
4180 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
4181 : typid2);
4182 : }
4183 420 : break;
4184 :
4185 0 : default:
4186 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
4187 : }
4188 :
4189 1788 : if (*cast_error)
4190 0 : return 0; /* cast error */
4191 :
4192 1788 : return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
4193 : }
4194 :
4195 : /*
4196 : * Executor-callable JSON_EXISTS implementation
4197 : *
4198 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4199 : * *error to true.
4200 : */
4201 : bool
4202 388 : JsonPathExists(Datum jb, JsonPath *jp, bool *error, List *vars)
4203 : {
4204 : JsonPathExecResult res;
4205 :
4206 388 : res = executeJsonPath(jp, vars,
4207 : GetJsonPathVar, CountJsonPathVars,
4208 : DatumGetJsonbP(jb), !error, NULL, true);
4209 :
4210 : Assert(error || !jperIsError(res));
4211 :
4212 384 : if (error && jperIsError(res))
4213 104 : *error = true;
4214 :
4215 384 : return res == jperOk;
4216 : }
4217 :
4218 : /*
4219 : * Executor-callable JSON_QUERY implementation
4220 : *
4221 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4222 : * *error to true. *empty is set to true if no match is found.
4223 : */
4224 : Datum
4225 1640 : JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
4226 : bool *error, List *vars,
4227 : const char *column_name)
4228 : {
4229 : bool wrap;
4230 : JsonValueList found;
4231 : JsonPathExecResult res;
4232 :
4233 1640 : JsonValueListInit(&found);
4234 :
4235 1640 : res = executeJsonPath(jp, vars,
4236 : GetJsonPathVar, CountJsonPathVars,
4237 : DatumGetJsonbP(jb), !error, &found, true);
4238 : Assert(error || !jperIsError(res));
4239 1628 : if (error && jperIsError(res))
4240 : {
4241 20 : *error = true;
4242 20 : *empty = false;
4243 20 : return (Datum) 0;
4244 : }
4245 :
4246 : /*
4247 : * Determine whether to wrap the result in a JSON array or not.
4248 : *
4249 : * If the returned JsonValueList is empty, no wrapping is necessary.
4250 : *
4251 : * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
4252 : * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
4253 : * default when no WRAPPER clause is specified.
4254 : *
4255 : * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
4256 : * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
4257 : * UNCONDITIONAL WRAPPER clause.
4258 : *
4259 : * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
4260 : * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
4261 : */
4262 1608 : if (JsonValueListIsEmpty(&found))
4263 140 : wrap = false;
4264 1468 : else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
4265 1140 : wrap = false;
4266 328 : else if (wrapper == JSW_UNCONDITIONAL)
4267 212 : wrap = true;
4268 116 : else if (wrapper == JSW_CONDITIONAL)
4269 116 : wrap = JsonValueListHasMultipleItems(&found);
4270 : else
4271 : {
4272 0 : elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
4273 : wrap = false;
4274 : }
4275 :
4276 1608 : if (wrap)
4277 252 : return JsonbPGetDatum(JsonbValueToJsonb(wrapItemsInArray(&found)));
4278 :
4279 : /* No wrapping means at most one item is expected. */
4280 1356 : if (JsonValueListHasMultipleItems(&found))
4281 : {
4282 40 : if (error)
4283 : {
4284 32 : *error = true;
4285 32 : return (Datum) 0;
4286 : }
4287 :
4288 8 : if (column_name)
4289 4 : ereport(ERROR,
4290 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4291 : errmsg("JSON path expression for column \"%s\" must return single item when no wrapper is requested",
4292 : column_name),
4293 : errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
4294 : else
4295 4 : ereport(ERROR,
4296 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4297 : errmsg("JSON path expression in JSON_QUERY must return single item when no wrapper is requested"),
4298 : errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
4299 : }
4300 :
4301 1316 : if (!JsonValueListIsEmpty(&found))
4302 1176 : return JsonbPGetDatum(JsonbValueToJsonb(JsonValueListHead(&found)));
4303 :
4304 140 : *empty = true;
4305 140 : return PointerGetDatum(NULL);
4306 : }
4307 :
4308 : /*
4309 : * Executor-callable JSON_VALUE implementation
4310 : *
4311 : * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4312 : * *error to true. *empty is set to true if no match is found.
4313 : */
4314 : JsonbValue *
4315 1508 : JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
4316 : const char *column_name)
4317 : {
4318 : JsonbValue *res;
4319 : JsonValueList found;
4320 : JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY;
4321 :
4322 1508 : JsonValueListInit(&found);
4323 :
4324 1508 : jper = executeJsonPath(jp, vars, GetJsonPathVar, CountJsonPathVars,
4325 : DatumGetJsonbP(jb),
4326 : !error, &found, true);
4327 :
4328 : Assert(error || !jperIsError(jper));
4329 :
4330 1500 : if (error && jperIsError(jper))
4331 : {
4332 12 : *error = true;
4333 12 : *empty = false;
4334 12 : return NULL;
4335 : }
4336 :
4337 1488 : *empty = JsonValueListIsEmpty(&found);
4338 :
4339 1488 : if (*empty)
4340 228 : return NULL;
4341 :
4342 : /* JSON_VALUE expects to get only singletons. */
4343 1260 : if (JsonValueListHasMultipleItems(&found))
4344 : {
4345 12 : if (error)
4346 : {
4347 8 : *error = true;
4348 8 : return NULL;
4349 : }
4350 :
4351 4 : if (column_name)
4352 0 : ereport(ERROR,
4353 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4354 : errmsg("JSON path expression for column \"%s\" must return single scalar item",
4355 : column_name)));
4356 : else
4357 4 : ereport(ERROR,
4358 : (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4359 : errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
4360 : }
4361 :
4362 1248 : res = copyJsonbValue(JsonValueListHead(&found));
4363 1248 : if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
4364 0 : JsonbExtractScalar(res->val.binary.data, res);
4365 :
4366 : /* JSON_VALUE expects to get only scalars. */
4367 1248 : if (!IsAJsonbScalar(res))
4368 : {
4369 64 : if (error)
4370 : {
4371 56 : *error = true;
4372 56 : return NULL;
4373 : }
4374 :
4375 8 : if (column_name)
4376 0 : ereport(ERROR,
4377 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4378 : errmsg("JSON path expression for column \"%s\" must return single scalar item",
4379 : column_name)));
4380 : else
4381 8 : ereport(ERROR,
4382 : (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4383 : errmsg("JSON path expression in JSON_VALUE must return single scalar item")));
4384 : }
4385 :
4386 1184 : if (res->type == jbvNull)
4387 48 : return NULL;
4388 :
4389 1136 : return res;
4390 : }
4391 :
4392 : /************************ JSON_TABLE functions ***************************/
4393 :
4394 : /*
4395 : * Sanity-checks and returns the opaque JsonTableExecContext from the
4396 : * given executor state struct.
4397 : */
4398 : static inline JsonTableExecContext *
4399 4860 : GetJsonTableExecContext(TableFuncScanState *state, const char *fname)
4400 : {
4401 : JsonTableExecContext *result;
4402 :
4403 4860 : if (!IsA(state, TableFuncScanState))
4404 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4405 4860 : result = (JsonTableExecContext *) state->opaque;
4406 4860 : if (result->magic != JSON_TABLE_EXEC_CONTEXT_MAGIC)
4407 0 : elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4408 :
4409 4860 : return result;
4410 : }
4411 :
4412 : /*
4413 : * JsonTableInitOpaque
4414 : * Fill in TableFuncScanState->opaque for processing JSON_TABLE
4415 : *
4416 : * This initializes the PASSING arguments and the JsonTablePlanState for
4417 : * JsonTablePlan given in TableFunc.
4418 : */
4419 : static void
4420 352 : JsonTableInitOpaque(TableFuncScanState *state, int natts)
4421 : {
4422 : JsonTableExecContext *cxt;
4423 352 : PlanState *ps = &state->ss.ps;
4424 352 : TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
4425 352 : TableFunc *tf = tfs->tablefunc;
4426 352 : JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
4427 352 : JsonExpr *je = castNode(JsonExpr, tf->docexpr);
4428 352 : List *args = NIL;
4429 :
4430 352 : cxt = palloc0_object(JsonTableExecContext);
4431 352 : cxt->magic = JSON_TABLE_EXEC_CONTEXT_MAGIC;
4432 :
4433 : /*
4434 : * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
4435 : * executor via JsonPathVariables.
4436 : */
4437 352 : if (state->passingvalexprs)
4438 : {
4439 : ListCell *exprlc;
4440 : ListCell *namelc;
4441 :
4442 : Assert(list_length(state->passingvalexprs) ==
4443 : list_length(je->passing_names));
4444 248 : forboth(exprlc, state->passingvalexprs,
4445 : namelc, je->passing_names)
4446 : {
4447 164 : ExprState *state = lfirst_node(ExprState, exprlc);
4448 164 : String *name = lfirst_node(String, namelc);
4449 164 : JsonPathVariable *var = palloc_object(JsonPathVariable);
4450 :
4451 164 : var->name = pstrdup(name->sval);
4452 164 : var->namelen = strlen(var->name);
4453 164 : var->typid = exprType((Node *) state->expr);
4454 164 : var->typmod = exprTypmod((Node *) state->expr);
4455 :
4456 : /*
4457 : * Evaluate the expression and save the value to be returned by
4458 : * GetJsonPathVar().
4459 : */
4460 164 : var->value = ExecEvalExpr(state, ps->ps_ExprContext,
4461 : &var->isnull);
4462 :
4463 164 : args = lappend(args, var);
4464 : }
4465 : }
4466 :
4467 352 : cxt->colplanstates = palloc_array(JsonTablePlanState *, list_length(tf->colvalexprs));
4468 :
4469 : /*
4470 : * Initialize plan for the root path and, recursively, also any child
4471 : * plans that compute the NESTED paths.
4472 : */
4473 352 : cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
4474 : CurrentMemoryContext);
4475 :
4476 352 : state->opaque = cxt;
4477 352 : }
4478 :
4479 : /*
4480 : * JsonTableDestroyOpaque
4481 : * Resets state->opaque
4482 : */
4483 : static void
4484 352 : JsonTableDestroyOpaque(TableFuncScanState *state)
4485 : {
4486 : JsonTableExecContext *cxt =
4487 352 : GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
4488 :
4489 : /* not valid anymore */
4490 352 : cxt->magic = 0;
4491 :
4492 352 : state->opaque = NULL;
4493 352 : }
4494 :
4495 : /*
4496 : * JsonTableInitPlan
4497 : * Initialize information for evaluating jsonpath in the given
4498 : * JsonTablePlan and, recursively, in any child plans
4499 : */
4500 : static JsonTablePlanState *
4501 688 : JsonTableInitPlan(JsonTableExecContext *cxt, JsonTablePlan *plan,
4502 : JsonTablePlanState *parentstate,
4503 : List *args, MemoryContext mcxt)
4504 : {
4505 688 : JsonTablePlanState *planstate = palloc0_object(JsonTablePlanState);
4506 :
4507 688 : planstate->plan = plan;
4508 688 : planstate->parent = parentstate;
4509 688 : JsonValueListInit(&planstate->found);
4510 :
4511 688 : if (IsA(plan, JsonTablePathScan))
4512 : {
4513 612 : JsonTablePathScan *scan = (JsonTablePathScan *) plan;
4514 : int i;
4515 :
4516 612 : planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
4517 612 : planstate->args = args;
4518 612 : planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
4519 : ALLOCSET_DEFAULT_SIZES);
4520 :
4521 : /* No row pattern evaluated yet. */
4522 612 : planstate->current.value = PointerGetDatum(NULL);
4523 612 : planstate->current.isnull = true;
4524 :
4525 1556 : for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
4526 944 : cxt->colplanstates[i] = planstate;
4527 :
4528 612 : planstate->nested = scan->child ?
4529 612 : JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
4530 : }
4531 76 : else if (IsA(plan, JsonTableSiblingJoin))
4532 : {
4533 76 : JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
4534 :
4535 76 : planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
4536 : args, mcxt);
4537 76 : planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
4538 : args, mcxt);
4539 : }
4540 :
4541 688 : return planstate;
4542 : }
4543 :
4544 : /*
4545 : * JsonTableSetDocument
4546 : * Install the input document and evaluate the row pattern
4547 : */
4548 : static void
4549 348 : JsonTableSetDocument(TableFuncScanState *state, Datum value)
4550 : {
4551 : JsonTableExecContext *cxt =
4552 348 : GetJsonTableExecContext(state, "JsonTableSetDocument");
4553 :
4554 348 : JsonTableResetRowPattern(cxt->rootplanstate, value);
4555 344 : }
4556 :
4557 : /*
4558 : * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
4559 : * the given context item
4560 : */
4561 : static void
4562 668 : JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item)
4563 : {
4564 668 : JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
4565 : MemoryContext oldcxt;
4566 : JsonPathExecResult res;
4567 668 : Jsonb *js = (Jsonb *) DatumGetJsonbP(item);
4568 :
4569 668 : JsonValueListClear(&planstate->found);
4570 :
4571 668 : MemoryContextResetOnly(planstate->mcxt);
4572 :
4573 668 : oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4574 :
4575 668 : res = executeJsonPath(planstate->path, planstate->args,
4576 : GetJsonPathVar, CountJsonPathVars,
4577 668 : js, scan->errorOnError,
4578 : &planstate->found,
4579 : true);
4580 :
4581 664 : MemoryContextSwitchTo(oldcxt);
4582 :
4583 664 : if (jperIsError(res))
4584 : {
4585 : Assert(!scan->errorOnError);
4586 12 : JsonValueListClear(&planstate->found);
4587 : }
4588 :
4589 : /* Reset plan iterator to the beginning of the item list */
4590 664 : JsonValueListInitIterator(&planstate->found, &planstate->iter);
4591 664 : planstate->current.value = PointerGetDatum(NULL);
4592 664 : planstate->current.isnull = true;
4593 664 : planstate->ordinal = 0;
4594 664 : }
4595 :
4596 : /*
4597 : * Fetch next row from a JsonTablePlan.
4598 : *
4599 : * Returns false if the plan has run out of rows, true otherwise.
4600 : */
4601 : static bool
4602 2800 : JsonTablePlanNextRow(JsonTablePlanState *planstate)
4603 : {
4604 2800 : if (IsA(planstate->plan, JsonTablePathScan))
4605 2188 : return JsonTablePlanScanNextRow(planstate);
4606 612 : else if (IsA(planstate->plan, JsonTableSiblingJoin))
4607 612 : return JsonTablePlanJoinNextRow(planstate);
4608 : else
4609 0 : elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
4610 :
4611 : Assert(false);
4612 : /* Appease compiler */
4613 : return false;
4614 : }
4615 :
4616 : /*
4617 : * Fetch next row from a JsonTablePlan's path evaluation result and from
4618 : * any child nested path(s).
4619 : *
4620 : * Returns true if any of the paths (this or the nested) has more rows to
4621 : * return.
4622 : *
4623 : * By fetching the nested path(s)'s rows based on the parent row at each
4624 : * level, this essentially joins the rows of different levels. If a nested
4625 : * path at a given level has no matching rows, the columns of that level will
4626 : * compute to NULL, making it an OUTER join.
4627 : */
4628 : static bool
4629 2188 : JsonTablePlanScanNextRow(JsonTablePlanState *planstate)
4630 : {
4631 : JsonbValue *jbv;
4632 : MemoryContext oldcxt;
4633 :
4634 : /*
4635 : * If planstate already has an active row and there is a nested plan,
4636 : * check if it has an active row to join with the former.
4637 : */
4638 2188 : if (!planstate->current.isnull)
4639 : {
4640 1224 : if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
4641 324 : return true;
4642 : }
4643 :
4644 : /* Fetch new row from the list of found values to set as active. */
4645 1864 : jbv = JsonValueListNext(&planstate->iter);
4646 :
4647 : /* End of list? */
4648 1864 : if (jbv == NULL)
4649 : {
4650 904 : planstate->current.value = PointerGetDatum(NULL);
4651 904 : planstate->current.isnull = true;
4652 904 : return false;
4653 : }
4654 :
4655 : /*
4656 : * Set current row item for subsequent JsonTableGetValue() calls for
4657 : * evaluating individual columns.
4658 : */
4659 960 : oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4660 960 : planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
4661 960 : planstate->current.isnull = false;
4662 960 : MemoryContextSwitchTo(oldcxt);
4663 :
4664 : /* Next row! */
4665 960 : planstate->ordinal++;
4666 :
4667 : /* Process nested plan(s), if any. */
4668 960 : if (planstate->nested)
4669 : {
4670 : /* Re-evaluate the nested path using the above parent row. */
4671 232 : JsonTableResetNestedPlan(planstate->nested);
4672 :
4673 : /*
4674 : * Now fetch the nested plan's current row to be joined against the
4675 : * parent row. Any further nested plans' paths will be re-evaluated
4676 : * recursively, level at a time, after setting each nested plan's
4677 : * current row.
4678 : */
4679 232 : (void) JsonTablePlanNextRow(planstate->nested);
4680 : }
4681 :
4682 : /* There are more rows. */
4683 960 : return true;
4684 : }
4685 :
4686 : /*
4687 : * Re-evaluate the row pattern of a nested plan using the new parent row
4688 : * pattern.
4689 : */
4690 : static void
4691 408 : JsonTableResetNestedPlan(JsonTablePlanState *planstate)
4692 : {
4693 : /* This better be a child plan. */
4694 : Assert(planstate->parent != NULL);
4695 408 : if (IsA(planstate->plan, JsonTablePathScan))
4696 : {
4697 320 : JsonTablePlanState *parent = planstate->parent;
4698 :
4699 320 : if (!parent->current.isnull)
4700 320 : JsonTableResetRowPattern(planstate, parent->current.value);
4701 :
4702 : /*
4703 : * If this plan itself has a child nested plan, it will be reset when
4704 : * the caller calls JsonTablePlanNextRow() on this plan.
4705 : */
4706 : }
4707 88 : else if (IsA(planstate->plan, JsonTableSiblingJoin))
4708 : {
4709 88 : JsonTableResetNestedPlan(planstate->left);
4710 88 : JsonTableResetNestedPlan(planstate->right);
4711 : }
4712 408 : }
4713 :
4714 : /*
4715 : * Fetch the next row from a JsonTableSiblingJoin.
4716 : *
4717 : * This is essentially a UNION between the rows from left and right siblings.
4718 : */
4719 : static bool
4720 612 : JsonTablePlanJoinNextRow(JsonTablePlanState *planstate)
4721 : {
4722 :
4723 : /* Fetch row from left sibling. */
4724 612 : if (!JsonTablePlanNextRow(planstate->left))
4725 : {
4726 : /*
4727 : * Left sibling ran out of rows, so start fetching from the right
4728 : * sibling.
4729 : */
4730 364 : if (!JsonTablePlanNextRow(planstate->right))
4731 : {
4732 : /* Right sibling ran out of row, so there are more rows. */
4733 216 : return false;
4734 : }
4735 : }
4736 :
4737 396 : return true;
4738 : }
4739 :
4740 : /*
4741 : * JsonTableFetchRow
4742 : * Prepare the next "current" row for upcoming GetValue calls.
4743 : *
4744 : * Returns false if no more rows can be returned.
4745 : */
4746 : static bool
4747 1036 : JsonTableFetchRow(TableFuncScanState *state)
4748 : {
4749 : JsonTableExecContext *cxt =
4750 1036 : GetJsonTableExecContext(state, "JsonTableFetchRow");
4751 :
4752 1036 : return JsonTablePlanNextRow(cxt->rootplanstate);
4753 : }
4754 :
4755 : /*
4756 : * JsonTableGetValue
4757 : * Return the value for column number 'colnum' for the current row.
4758 : *
4759 : * This leaks memory, so be sure to reset often the context in which it's
4760 : * called.
4761 : */
4762 : static Datum
4763 3124 : JsonTableGetValue(TableFuncScanState *state, int colnum,
4764 : Oid typid, int32 typmod, bool *isnull)
4765 : {
4766 : JsonTableExecContext *cxt =
4767 3124 : GetJsonTableExecContext(state, "JsonTableGetValue");
4768 3124 : ExprContext *econtext = state->ss.ps.ps_ExprContext;
4769 3124 : ExprState *estate = list_nth(state->colvalexprs, colnum);
4770 3124 : JsonTablePlanState *planstate = cxt->colplanstates[colnum];
4771 3124 : JsonTablePlanRowSource *current = &planstate->current;
4772 : Datum result;
4773 :
4774 : /* Row pattern value is NULL */
4775 3124 : if (current->isnull)
4776 : {
4777 596 : result = (Datum) 0;
4778 596 : *isnull = true;
4779 : }
4780 : /* Evaluate JsonExpr. */
4781 2528 : else if (estate)
4782 : {
4783 2228 : Datum saved_caseValue = econtext->caseValue_datum;
4784 2228 : bool saved_caseIsNull = econtext->caseValue_isNull;
4785 :
4786 : /* Pass the row pattern value via CaseTestExpr. */
4787 2228 : econtext->caseValue_datum = current->value;
4788 2228 : econtext->caseValue_isNull = false;
4789 :
4790 2228 : result = ExecEvalExpr(estate, econtext, isnull);
4791 :
4792 2168 : econtext->caseValue_datum = saved_caseValue;
4793 2168 : econtext->caseValue_isNull = saved_caseIsNull;
4794 : }
4795 : /* ORDINAL column */
4796 : else
4797 : {
4798 300 : result = Int32GetDatum(planstate->ordinal);
4799 300 : *isnull = false;
4800 : }
4801 :
4802 3064 : return result;
4803 : }
|