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