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-2023, 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 "lib/stringinfo.h"
66 : #include "miscadmin.h"
67 : #include "nodes/miscnodes.h"
68 : #include "regex/regex.h"
69 : #include "utils/builtins.h"
70 : #include "utils/date.h"
71 : #include "utils/datetime.h"
72 : #include "utils/datum.h"
73 : #include "utils/float.h"
74 : #include "utils/formatting.h"
75 : #include "utils/guc.h"
76 : #include "utils/json.h"
77 : #include "utils/jsonpath.h"
78 : #include "utils/timestamp.h"
79 : #include "utils/varlena.h"
80 :
81 : /*
82 : * Represents "base object" and it's "id" for .keyvalue() evaluation.
83 : */
84 : typedef struct JsonBaseObjectInfo
85 : {
86 : JsonbContainer *jbc;
87 : int id;
88 : } JsonBaseObjectInfo;
89 :
90 : /*
91 : * Context of jsonpath execution.
92 : */
93 : typedef struct JsonPathExecContext
94 : {
95 : Jsonb *vars; /* variables to substitute into jsonpath */
96 : JsonbValue *root; /* for $ evaluation */
97 : JsonbValue *current; /* for @ evaluation */
98 : JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
99 : * evaluation */
100 : int lastGeneratedObjectId; /* "id" counter for .keyvalue()
101 : * evaluation */
102 : int innermostArraySize; /* for LAST array index evaluation */
103 : bool laxMode; /* true for "lax" mode, false for "strict"
104 : * mode */
105 : bool ignoreStructuralErrors; /* with "true" structural errors such
106 : * as absence of required json item or
107 : * unexpected json item type are
108 : * ignored */
109 : bool throwErrors; /* with "false" all suppressible errors are
110 : * suppressed */
111 : bool useTz;
112 : } JsonPathExecContext;
113 :
114 : /* Context for LIKE_REGEX execution. */
115 : typedef struct JsonLikeRegexContext
116 : {
117 : text *regex;
118 : int cflags;
119 : } JsonLikeRegexContext;
120 :
121 : /* Result of jsonpath predicate evaluation */
122 : typedef enum JsonPathBool
123 : {
124 : jpbFalse = 0,
125 : jpbTrue = 1,
126 : jpbUnknown = 2
127 : } JsonPathBool;
128 :
129 : /* Result of jsonpath expression evaluation */
130 : typedef enum JsonPathExecResult
131 : {
132 : jperOk = 0,
133 : jperNotFound = 1,
134 : jperError = 2
135 : } JsonPathExecResult;
136 :
137 : #define jperIsError(jper) ((jper) == jperError)
138 :
139 : /*
140 : * List of jsonb values with shortcut for single-value list.
141 : */
142 : typedef struct JsonValueList
143 : {
144 : JsonbValue *singleton;
145 : List *list;
146 : } JsonValueList;
147 :
148 : typedef struct JsonValueListIterator
149 : {
150 : JsonbValue *value;
151 : List *list;
152 : ListCell *next;
153 : } JsonValueListIterator;
154 :
155 : /* strict/lax flags is decomposed into four [un]wrap/error flags */
156 : #define jspStrictAbsenseOfErrors(cxt) (!(cxt)->laxMode)
157 : #define jspAutoUnwrap(cxt) ((cxt)->laxMode)
158 : #define jspAutoWrap(cxt) ((cxt)->laxMode)
159 : #define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
160 : #define jspThrowErrors(cxt) ((cxt)->throwErrors)
161 :
162 : /* Convenience macro: return or throw error depending on context */
163 : #define RETURN_ERROR(throw_error) \
164 : do { \
165 : if (jspThrowErrors(cxt)) \
166 : throw_error; \
167 : else \
168 : return jperError; \
169 : } while (0)
170 :
171 : typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
172 : JsonbValue *larg,
173 : JsonbValue *rarg,
174 : void *param);
175 : typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
176 :
177 : static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
178 : Jsonb *json, bool throwErrors,
179 : JsonValueList *result, bool useTz);
180 : static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
181 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
182 : static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
183 : JsonPathItem *jsp, JsonbValue *jb,
184 : JsonValueList *found, bool unwrap);
185 : static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
186 : JsonPathItem *jsp, JsonbValue *jb,
187 : JsonValueList *found, bool unwrapElements);
188 : static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
189 : JsonPathItem *cur, JsonPathItem *next,
190 : JsonbValue *v, JsonValueList *found, bool copy);
191 : static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
192 : bool unwrap, JsonValueList *found);
193 : static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
194 : JsonbValue *jb, bool unwrap, JsonValueList *found);
195 : static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
196 : JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
197 : static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
198 : JsonPathItem *jsp, JsonbValue *jb);
199 : static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
200 : JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
201 : uint32 level, uint32 first, uint32 last,
202 : bool ignoreStructuralErrors, bool unwrapNext);
203 : static JsonPathBool executePredicate(JsonPathExecContext *cxt,
204 : JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
205 : JsonbValue *jb, bool unwrapRightArg,
206 : JsonPathPredicateCallback exec, void *param);
207 : static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
208 : JsonPathItem *jsp, JsonbValue *jb,
209 : BinaryArithmFunc func, JsonValueList *found);
210 : static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
211 : JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
212 : JsonValueList *found);
213 : static JsonPathBool executeStartsWith(JsonPathItem *jsp,
214 : JsonbValue *whole, JsonbValue *initial, void *param);
215 : static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
216 : JsonbValue *rarg, void *param);
217 : static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
218 : JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
219 : JsonValueList *found);
220 : static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
221 : JsonbValue *jb, JsonValueList *found);
222 : static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
223 : JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
224 : static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
225 : JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
226 : static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
227 : JsonbValue *value);
228 : static void getJsonPathVariable(JsonPathExecContext *cxt,
229 : JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
230 : static int JsonbArraySize(JsonbValue *jb);
231 : static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
232 : JsonbValue *rv, void *p);
233 : static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
234 : bool useTz);
235 : static int compareNumeric(Numeric a, Numeric b);
236 : static JsonbValue *copyJsonbValue(JsonbValue *src);
237 : static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
238 : JsonPathItem *jsp, JsonbValue *jb, int32 *index);
239 : static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
240 : JsonbValue *jbv, int32 id);
241 : static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
242 : static int JsonValueListLength(const JsonValueList *jvl);
243 : static bool JsonValueListIsEmpty(JsonValueList *jvl);
244 : static JsonbValue *JsonValueListHead(JsonValueList *jvl);
245 : static List *JsonValueListGetList(JsonValueList *jvl);
246 : static void JsonValueListInitIterator(const JsonValueList *jvl,
247 : JsonValueListIterator *it);
248 : static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
249 : JsonValueListIterator *it);
250 : static int JsonbType(JsonbValue *jb);
251 : static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
252 : static int JsonbType(JsonbValue *jb);
253 : static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
254 : static JsonbValue *wrapItemsInArray(const JsonValueList *items);
255 : static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
256 : bool useTz, bool *cast_error);
257 :
258 : /****************** User interface to JsonPath executor ********************/
259 :
260 : /*
261 : * jsonb_path_exists
262 : * Returns true if jsonpath returns at least one item for the specified
263 : * jsonb value. This function and jsonb_path_match() are used to
264 : * implement @? and @@ operators, which in turn are intended to have an
265 : * index support. Thus, it's desirable to make it easier to achieve
266 : * consistency between index scan results and sequential scan results.
267 : * So, we throw as few errors as possible. Regarding this function,
268 : * such behavior also matches behavior of JSON_EXISTS() clause of
269 : * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
270 : * an analogy in SQL/JSON, so we define its behavior on our own.
271 : */
272 : static Datum
273 86010 : jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
274 : {
275 86010 : Jsonb *jb = PG_GETARG_JSONB_P(0);
276 86010 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
277 : JsonPathExecResult res;
278 86010 : Jsonb *vars = NULL;
279 86010 : bool silent = true;
280 :
281 86010 : if (PG_NARGS() == 4)
282 : {
283 54 : vars = PG_GETARG_JSONB_P(2);
284 54 : silent = PG_GETARG_BOOL(3);
285 : }
286 :
287 86010 : res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
288 :
289 85998 : PG_FREE_IF_COPY(jb, 0);
290 85998 : PG_FREE_IF_COPY(jp, 1);
291 :
292 85998 : if (jperIsError(res))
293 54 : PG_RETURN_NULL();
294 :
295 85944 : PG_RETURN_BOOL(res == jperOk);
296 : }
297 :
298 : Datum
299 54 : jsonb_path_exists(PG_FUNCTION_ARGS)
300 : {
301 54 : return jsonb_path_exists_internal(fcinfo, false);
302 : }
303 :
304 : Datum
305 0 : jsonb_path_exists_tz(PG_FUNCTION_ARGS)
306 : {
307 0 : return jsonb_path_exists_internal(fcinfo, true);
308 : }
309 :
310 : /*
311 : * jsonb_path_exists_opr
312 : * Implementation of operator "jsonb @? jsonpath" (2-argument version of
313 : * jsonb_path_exists()).
314 : */
315 : Datum
316 85956 : jsonb_path_exists_opr(PG_FUNCTION_ARGS)
317 : {
318 : /* just call the other one -- it can handle both cases */
319 85956 : return jsonb_path_exists_internal(fcinfo, false);
320 : }
321 :
322 : /*
323 : * jsonb_path_match
324 : * Returns jsonpath predicate result item for the specified jsonb value.
325 : * See jsonb_path_exists() comment for details regarding error handling.
326 : */
327 : static Datum
328 97914 : jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
329 : {
330 97914 : Jsonb *jb = PG_GETARG_JSONB_P(0);
331 97914 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
332 97914 : JsonValueList found = {0};
333 97914 : Jsonb *vars = NULL;
334 97914 : bool silent = true;
335 :
336 97914 : if (PG_NARGS() == 4)
337 : {
338 126 : vars = PG_GETARG_JSONB_P(2);
339 126 : silent = PG_GETARG_BOOL(3);
340 : }
341 :
342 97914 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
343 :
344 97902 : PG_FREE_IF_COPY(jb, 0);
345 97902 : PG_FREE_IF_COPY(jp, 1);
346 :
347 97902 : if (JsonValueListLength(&found) == 1)
348 : {
349 97872 : JsonbValue *jbv = JsonValueListHead(&found);
350 :
351 97872 : if (jbv->type == jbvBool)
352 97800 : PG_RETURN_BOOL(jbv->val.boolean);
353 :
354 72 : if (jbv->type == jbvNull)
355 24 : PG_RETURN_NULL();
356 : }
357 :
358 78 : if (!silent)
359 36 : ereport(ERROR,
360 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
361 : errmsg("single boolean result is expected")));
362 :
363 42 : PG_RETURN_NULL();
364 : }
365 :
366 : Datum
367 126 : jsonb_path_match(PG_FUNCTION_ARGS)
368 : {
369 126 : return jsonb_path_match_internal(fcinfo, false);
370 : }
371 :
372 : Datum
373 0 : jsonb_path_match_tz(PG_FUNCTION_ARGS)
374 : {
375 0 : return jsonb_path_match_internal(fcinfo, true);
376 : }
377 :
378 : /*
379 : * jsonb_path_match_opr
380 : * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
381 : * jsonb_path_match()).
382 : */
383 : Datum
384 97788 : jsonb_path_match_opr(PG_FUNCTION_ARGS)
385 : {
386 : /* just call the other one -- it can handle both cases */
387 97788 : return jsonb_path_match_internal(fcinfo, false);
388 : }
389 :
390 : /*
391 : * jsonb_path_query
392 : * Executes jsonpath for given jsonb document and returns result as
393 : * rowset.
394 : */
395 : static Datum
396 3618 : jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
397 : {
398 : FuncCallContext *funcctx;
399 : List *found;
400 : JsonbValue *v;
401 : ListCell *c;
402 :
403 3618 : if (SRF_IS_FIRSTCALL())
404 : {
405 : JsonPath *jp;
406 : Jsonb *jb;
407 : MemoryContext oldcontext;
408 : Jsonb *vars;
409 : bool silent;
410 1872 : JsonValueList found = {0};
411 :
412 1872 : funcctx = SRF_FIRSTCALL_INIT();
413 1872 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
414 :
415 1872 : jb = PG_GETARG_JSONB_P_COPY(0);
416 1872 : jp = PG_GETARG_JSONPATH_P_COPY(1);
417 1872 : vars = PG_GETARG_JSONB_P_COPY(2);
418 1872 : silent = PG_GETARG_BOOL(3);
419 :
420 1872 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
421 :
422 1416 : funcctx->user_fctx = JsonValueListGetList(&found);
423 :
424 1416 : MemoryContextSwitchTo(oldcontext);
425 : }
426 :
427 3162 : funcctx = SRF_PERCALL_SETUP();
428 3162 : found = funcctx->user_fctx;
429 :
430 3162 : c = list_head(found);
431 :
432 3162 : if (c == NULL)
433 1416 : SRF_RETURN_DONE(funcctx);
434 :
435 1746 : v = lfirst(c);
436 1746 : funcctx->user_fctx = list_delete_first(found);
437 :
438 1746 : SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
439 : }
440 :
441 : Datum
442 3234 : jsonb_path_query(PG_FUNCTION_ARGS)
443 : {
444 3234 : return jsonb_path_query_internal(fcinfo, false);
445 : }
446 :
447 : Datum
448 384 : jsonb_path_query_tz(PG_FUNCTION_ARGS)
449 : {
450 384 : return jsonb_path_query_internal(fcinfo, true);
451 : }
452 :
453 : /*
454 : * jsonb_path_query_array
455 : * Executes jsonpath for given jsonb document and returns result as
456 : * jsonb array.
457 : */
458 : static Datum
459 48 : jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
460 : {
461 48 : Jsonb *jb = PG_GETARG_JSONB_P(0);
462 48 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
463 48 : JsonValueList found = {0};
464 48 : Jsonb *vars = PG_GETARG_JSONB_P(2);
465 48 : bool silent = PG_GETARG_BOOL(3);
466 :
467 48 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
468 :
469 42 : PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
470 : }
471 :
472 : Datum
473 48 : jsonb_path_query_array(PG_FUNCTION_ARGS)
474 : {
475 48 : return jsonb_path_query_array_internal(fcinfo, false);
476 : }
477 :
478 : Datum
479 0 : jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
480 : {
481 0 : return jsonb_path_query_array_internal(fcinfo, true);
482 : }
483 :
484 : /*
485 : * jsonb_path_query_first
486 : * Executes jsonpath for given jsonb document and returns first result
487 : * item. If there are no items, NULL returned.
488 : */
489 : static Datum
490 4374 : jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
491 : {
492 4374 : Jsonb *jb = PG_GETARG_JSONB_P(0);
493 4374 : JsonPath *jp = PG_GETARG_JSONPATH_P(1);
494 4374 : JsonValueList found = {0};
495 4374 : Jsonb *vars = PG_GETARG_JSONB_P(2);
496 4374 : bool silent = PG_GETARG_BOOL(3);
497 :
498 4374 : (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
499 :
500 4362 : if (JsonValueListLength(&found) >= 1)
501 4350 : PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
502 : else
503 12 : PG_RETURN_NULL();
504 : }
505 :
506 : Datum
507 4374 : jsonb_path_query_first(PG_FUNCTION_ARGS)
508 : {
509 4374 : return jsonb_path_query_first_internal(fcinfo, false);
510 : }
511 :
512 : Datum
513 0 : jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
514 : {
515 0 : return jsonb_path_query_first_internal(fcinfo, true);
516 : }
517 :
518 : /********************Execute functions for JsonPath**************************/
519 :
520 : /*
521 : * Interface to jsonpath executor
522 : *
523 : * 'path' - jsonpath to be executed
524 : * 'vars' - variables to be substituted to jsonpath
525 : * 'json' - target document for jsonpath evaluation
526 : * 'throwErrors' - whether we should throw suppressible errors
527 : * 'result' - list to store result items into
528 : *
529 : * Returns an error if a recoverable error happens during processing, or NULL
530 : * on no error.
531 : *
532 : * Note, jsonb and jsonpath values should be available and untoasted during
533 : * work because JsonPathItem, JsonbValue and result item could have pointers
534 : * into input values. If caller needs to just check if document matches
535 : * jsonpath, then it doesn't provide a result arg. In this case executor
536 : * works till first positive result and does not check the rest if possible.
537 : * In other case it tries to find all the satisfied result items.
538 : */
539 : static JsonPathExecResult
540 190218 : executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
541 : JsonValueList *result, bool useTz)
542 : {
543 : JsonPathExecContext cxt;
544 : JsonPathExecResult res;
545 : JsonPathItem jsp;
546 : JsonbValue jbv;
547 :
548 190218 : jspInit(&jsp, path);
549 :
550 190218 : if (!JsonbExtractScalar(&json->root, &jbv))
551 189522 : JsonbInitBinary(&jbv, json);
552 :
553 190218 : if (vars && !JsonContainerIsObject(&vars->root))
554 : {
555 12 : ereport(ERROR,
556 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
557 : errmsg("\"vars\" argument is not an object"),
558 : errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
559 : }
560 :
561 190206 : cxt.vars = vars;
562 190206 : cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
563 190206 : cxt.ignoreStructuralErrors = cxt.laxMode;
564 190206 : cxt.root = &jbv;
565 190206 : cxt.current = &jbv;
566 190206 : cxt.baseObject.jbc = NULL;
567 190206 : cxt.baseObject.id = 0;
568 190206 : cxt.lastGeneratedObjectId = vars ? 2 : 1;
569 190206 : cxt.innermostArraySize = -1;
570 190206 : cxt.throwErrors = throwErrors;
571 190206 : cxt.useTz = useTz;
572 :
573 190206 : if (jspStrictAbsenseOfErrors(&cxt) && !result)
574 : {
575 : /*
576 : * In strict mode we must get a complete list of values to check that
577 : * there are no errors at all.
578 : */
579 54 : JsonValueList vals = {0};
580 :
581 54 : res = executeItem(&cxt, &jsp, &jbv, &vals);
582 :
583 48 : if (jperIsError(res))
584 42 : return res;
585 :
586 6 : return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
587 : }
588 :
589 190152 : res = executeItem(&cxt, &jsp, &jbv, result);
590 :
591 : Assert(!throwErrors || !jperIsError(res));
592 :
593 189672 : return res;
594 : }
595 :
596 : /*
597 : * Execute jsonpath with automatic unwrapping of current item in lax mode.
598 : */
599 : static JsonPathExecResult
600 565866 : executeItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
601 : JsonbValue *jb, JsonValueList *found)
602 : {
603 565866 : return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
604 : }
605 :
606 : /*
607 : * Main jsonpath executor function: walks on jsonpath structure, finds
608 : * relevant parts of jsonb and evaluates expressions over them.
609 : * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
610 : */
611 : static JsonPathExecResult
612 569850 : executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
613 : JsonbValue *jb, JsonValueList *found, bool unwrap)
614 : {
615 : JsonPathItem elem;
616 569850 : JsonPathExecResult res = jperNotFound;
617 : JsonBaseObjectInfo baseObject;
618 :
619 569850 : check_stack_depth();
620 569850 : CHECK_FOR_INTERRUPTS();
621 :
622 569850 : switch (jsp->type)
623 : {
624 : /* all boolean item types: */
625 102162 : case jpiAnd:
626 : case jpiOr:
627 : case jpiNot:
628 : case jpiIsUnknown:
629 : case jpiEqual:
630 : case jpiNotEqual:
631 : case jpiLess:
632 : case jpiGreater:
633 : case jpiLessOrEqual:
634 : case jpiGreaterOrEqual:
635 : case jpiExists:
636 : case jpiStartsWith:
637 : case jpiLikeRegex:
638 : {
639 102162 : JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
640 :
641 102162 : res = appendBoolResult(cxt, jsp, found, st);
642 102162 : break;
643 : }
644 :
645 165228 : case jpiKey:
646 165228 : if (JsonbType(jb) == jbvObject)
647 : {
648 : JsonbValue *v;
649 : JsonbValue key;
650 :
651 164976 : key.type = jbvString;
652 164976 : key.val.string.val = jspGetString(jsp, &key.val.string.len);
653 :
654 164976 : v = findJsonbValueFromContainer(jb->val.binary.data,
655 : JB_FOBJECT, &key);
656 :
657 164976 : if (v != NULL)
658 : {
659 26148 : res = executeNextItem(cxt, jsp, NULL,
660 : v, found, false);
661 :
662 : /* free value if it was not added to found list */
663 26148 : if (jspHasNext(jsp) || !found)
664 16104 : pfree(v);
665 : }
666 138828 : else if (!jspIgnoreStructuralErrors(cxt))
667 : {
668 : Assert(found);
669 :
670 72 : if (!jspThrowErrors(cxt))
671 48 : return jperError;
672 :
673 24 : ereport(ERROR,
674 : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
675 : errmsg("JSON object does not contain key \"%s\"",
676 : pnstrdup(key.val.string.val,
677 : key.val.string.len))));
678 : }
679 : }
680 252 : else if (unwrap && JsonbType(jb) == jbvArray)
681 6 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
682 246 : else if (!jspIgnoreStructuralErrors(cxt))
683 : {
684 : Assert(found);
685 66 : RETURN_ERROR(ereport(ERROR,
686 : (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
687 : errmsg("jsonpath member accessor can only be applied to an object"))));
688 : }
689 165084 : break;
690 :
691 202410 : case jpiRoot:
692 202410 : jb = cxt->root;
693 202410 : baseObject = setBaseObject(cxt, jb, 0);
694 202410 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
695 201996 : cxt->baseObject = baseObject;
696 201996 : break;
697 :
698 19356 : case jpiCurrent:
699 19356 : res = executeNextItem(cxt, jsp, NULL, cxt->current,
700 : found, true);
701 19356 : break;
702 :
703 1326 : case jpiAnyArray:
704 1326 : if (JsonbType(jb) == jbvArray)
705 : {
706 1140 : bool hasNext = jspGetNext(jsp, &elem);
707 :
708 1140 : res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
709 1140 : jb, found, jspAutoUnwrap(cxt));
710 : }
711 186 : else if (jspAutoWrap(cxt))
712 174 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
713 12 : else if (!jspIgnoreStructuralErrors(cxt))
714 12 : RETURN_ERROR(ereport(ERROR,
715 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
716 : errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
717 1188 : break;
718 :
719 288 : case jpiIndexArray:
720 288 : if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
721 174 : {
722 276 : int innermostArraySize = cxt->innermostArraySize;
723 : int i;
724 276 : int size = JsonbArraySize(jb);
725 276 : bool singleton = size < 0;
726 276 : bool hasNext = jspGetNext(jsp, &elem);
727 :
728 276 : if (singleton)
729 6 : size = 1;
730 :
731 276 : cxt->innermostArraySize = size; /* for LAST evaluation */
732 :
733 432 : for (i = 0; i < jsp->content.array.nelems; i++)
734 : {
735 : JsonPathItem from;
736 : JsonPathItem to;
737 : int32 index;
738 : int32 index_from;
739 : int32 index_to;
740 288 : bool range = jspGetArraySubscript(jsp, &from,
741 : &to, i);
742 :
743 288 : res = getArrayIndex(cxt, &from, jb, &index_from);
744 :
745 264 : if (jperIsError(res))
746 30 : break;
747 :
748 240 : if (range)
749 : {
750 30 : res = getArrayIndex(cxt, &to, jb, &index_to);
751 :
752 24 : if (jperIsError(res))
753 0 : break;
754 : }
755 : else
756 210 : index_to = index_from;
757 :
758 234 : if (!jspIgnoreStructuralErrors(cxt) &&
759 84 : (index_from < 0 ||
760 72 : index_from > index_to ||
761 72 : index_to >= size))
762 72 : RETURN_ERROR(ereport(ERROR,
763 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
764 : errmsg("jsonpath array subscript is out of bounds"))));
765 :
766 186 : if (index_from < 0)
767 12 : index_from = 0;
768 :
769 186 : if (index_to >= size)
770 18 : index_to = size - 1;
771 :
772 186 : res = jperNotFound;
773 :
774 360 : for (index = index_from; index <= index_to; index++)
775 : {
776 : JsonbValue *v;
777 : bool copy;
778 :
779 204 : if (singleton)
780 : {
781 6 : v = jb;
782 6 : copy = true;
783 : }
784 : else
785 : {
786 198 : v = getIthJsonbValueFromContainer(jb->val.binary.data,
787 : (uint32) index);
788 :
789 198 : if (v == NULL)
790 0 : continue;
791 :
792 198 : copy = false;
793 : }
794 :
795 204 : if (!hasNext && !found)
796 24 : return jperOk;
797 :
798 180 : res = executeNextItem(cxt, jsp, &elem, v, found,
799 : copy);
800 :
801 180 : if (jperIsError(res))
802 0 : break;
803 :
804 180 : if (res == jperOk && !found)
805 6 : break;
806 : }
807 :
808 162 : if (jperIsError(res))
809 0 : break;
810 :
811 162 : if (res == jperOk && !found)
812 6 : break;
813 : }
814 :
815 174 : cxt->innermostArraySize = innermostArraySize;
816 : }
817 12 : else if (!jspIgnoreStructuralErrors(cxt))
818 : {
819 12 : RETURN_ERROR(ereport(ERROR,
820 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
821 : errmsg("jsonpath array accessor can only be applied to an array"))));
822 : }
823 174 : break;
824 :
825 66 : case jpiLast:
826 : {
827 : JsonbValue tmpjbv;
828 : JsonbValue *lastjbv;
829 : int last;
830 66 : bool hasNext = jspGetNext(jsp, &elem);
831 :
832 66 : if (cxt->innermostArraySize < 0)
833 0 : elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
834 :
835 66 : if (!hasNext && !found)
836 : {
837 6 : res = jperOk;
838 6 : break;
839 : }
840 :
841 60 : last = cxt->innermostArraySize - 1;
842 :
843 60 : lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
844 :
845 60 : lastjbv->type = jbvNumeric;
846 60 : lastjbv->val.numeric = int64_to_numeric(last);
847 :
848 60 : res = executeNextItem(cxt, jsp, &elem,
849 : lastjbv, found, hasNext);
850 : }
851 60 : break;
852 :
853 84 : case jpiAnyKey:
854 84 : if (JsonbType(jb) == jbvObject)
855 : {
856 66 : bool hasNext = jspGetNext(jsp, &elem);
857 :
858 66 : if (jb->type != jbvBinary)
859 0 : elog(ERROR, "invalid jsonb object type: %d", jb->type);
860 :
861 66 : return executeAnyItem
862 : (cxt, hasNext ? &elem : NULL,
863 : jb->val.binary.data, found, 1, 1, 1,
864 66 : false, jspAutoUnwrap(cxt));
865 : }
866 18 : else if (unwrap && JsonbType(jb) == jbvArray)
867 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
868 18 : else if (!jspIgnoreStructuralErrors(cxt))
869 : {
870 : Assert(found);
871 12 : RETURN_ERROR(ereport(ERROR,
872 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
873 : errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
874 : }
875 6 : break;
876 :
877 168 : case jpiAdd:
878 168 : return executeBinaryArithmExpr(cxt, jsp, jb,
879 : numeric_add_opt_error, found);
880 :
881 60 : case jpiSub:
882 60 : return executeBinaryArithmExpr(cxt, jsp, jb,
883 : numeric_sub_opt_error, found);
884 :
885 36 : case jpiMul:
886 36 : return executeBinaryArithmExpr(cxt, jsp, jb,
887 : numeric_mul_opt_error, found);
888 :
889 66 : case jpiDiv:
890 66 : return executeBinaryArithmExpr(cxt, jsp, jb,
891 : numeric_div_opt_error, found);
892 :
893 12 : case jpiMod:
894 12 : return executeBinaryArithmExpr(cxt, jsp, jb,
895 : numeric_mod_opt_error, found);
896 :
897 60 : case jpiPlus:
898 60 : return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
899 :
900 126 : case jpiMinus:
901 126 : return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
902 : found);
903 :
904 18582 : case jpiFilter:
905 : {
906 : JsonPathBool st;
907 :
908 18582 : if (unwrap && JsonbType(jb) == jbvArray)
909 126 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
910 : false);
911 :
912 18456 : jspGetArg(jsp, &elem);
913 18456 : st = executeNestedBoolItem(cxt, &elem, jb);
914 18360 : if (st != jpbTrue)
915 16482 : res = jperNotFound;
916 : else
917 1878 : res = executeNextItem(cxt, jsp, NULL,
918 : jb, found, true);
919 18360 : break;
920 : }
921 :
922 306 : case jpiAny:
923 : {
924 306 : bool hasNext = jspGetNext(jsp, &elem);
925 :
926 : /* first try without any intermediate steps */
927 306 : if (jsp->content.anybounds.first == 0)
928 : {
929 : bool savedIgnoreStructuralErrors;
930 :
931 168 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
932 168 : cxt->ignoreStructuralErrors = true;
933 168 : res = executeNextItem(cxt, jsp, &elem,
934 : jb, found, true);
935 168 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
936 :
937 168 : if (res == jperOk && !found)
938 6 : break;
939 : }
940 :
941 300 : if (jb->type == jbvBinary)
942 300 : res = executeAnyItem
943 : (cxt, hasNext ? &elem : NULL,
944 : jb->val.binary.data, found,
945 : 1,
946 : jsp->content.anybounds.first,
947 : jsp->content.anybounds.last,
948 300 : true, jspAutoUnwrap(cxt));
949 300 : break;
950 : }
951 :
952 55548 : case jpiNull:
953 : case jpiBool:
954 : case jpiNumeric:
955 : case jpiString:
956 : case jpiVariable:
957 : {
958 : JsonbValue vbuf;
959 : JsonbValue *v;
960 55548 : bool hasNext = jspGetNext(jsp, &elem);
961 :
962 55548 : if (!hasNext && !found && jsp->type != jpiVariable)
963 : {
964 : /*
965 : * Skip evaluation, but not for variables. We must
966 : * trigger an error for the missing variable.
967 : */
968 12 : res = jperOk;
969 12 : break;
970 : }
971 :
972 55536 : v = hasNext ? &vbuf : palloc(sizeof(*v));
973 :
974 55536 : baseObject = cxt->baseObject;
975 55536 : getJsonPathItem(cxt, jsp, v);
976 :
977 55506 : res = executeNextItem(cxt, jsp, &elem,
978 : v, found, hasNext);
979 55506 : cxt->baseObject = baseObject;
980 : }
981 55506 : break;
982 :
983 210 : case jpiType:
984 : {
985 210 : JsonbValue *jbv = palloc(sizeof(*jbv));
986 :
987 210 : jbv->type = jbvString;
988 210 : jbv->val.string.val = pstrdup(JsonbTypeName(jb));
989 210 : jbv->val.string.len = strlen(jbv->val.string.val);
990 :
991 210 : res = executeNextItem(cxt, jsp, NULL, jbv,
992 : found, false);
993 : }
994 210 : break;
995 :
996 72 : case jpiSize:
997 : {
998 72 : int size = JsonbArraySize(jb);
999 :
1000 72 : if (size < 0)
1001 : {
1002 48 : if (!jspAutoWrap(cxt))
1003 : {
1004 12 : if (!jspIgnoreStructuralErrors(cxt))
1005 12 : RETURN_ERROR(ereport(ERROR,
1006 : (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1007 : errmsg("jsonpath item method .%s() can only be applied to an array",
1008 : jspOperationName(jsp->type)))));
1009 0 : break;
1010 : }
1011 :
1012 36 : size = 1;
1013 : }
1014 :
1015 60 : jb = palloc(sizeof(*jb));
1016 :
1017 60 : jb->type = jbvNumeric;
1018 60 : jb->val.numeric = int64_to_numeric(size);
1019 :
1020 60 : res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1021 : }
1022 60 : break;
1023 :
1024 108 : case jpiAbs:
1025 108 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1026 : found);
1027 :
1028 48 : case jpiFloor:
1029 48 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1030 : found);
1031 :
1032 102 : case jpiCeiling:
1033 102 : return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1034 : found);
1035 :
1036 114 : case jpiDouble:
1037 : {
1038 : JsonbValue jbv;
1039 :
1040 114 : if (unwrap && JsonbType(jb) == jbvArray)
1041 42 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1042 : false);
1043 :
1044 108 : if (jb->type == jbvNumeric)
1045 : {
1046 12 : char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1047 : NumericGetDatum(jb->val.numeric)));
1048 : double val;
1049 12 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1050 :
1051 12 : val = float8in_internal(tmp,
1052 : NULL,
1053 : "double precision",
1054 : tmp,
1055 : (Node *) &escontext);
1056 :
1057 12 : if (escontext.error_occurred || isinf(val) || isnan(val))
1058 6 : RETURN_ERROR(ereport(ERROR,
1059 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1060 : errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
1061 : jspOperationName(jsp->type)))));
1062 6 : res = jperOk;
1063 : }
1064 96 : else if (jb->type == jbvString)
1065 : {
1066 : /* cast string as double */
1067 : double val;
1068 48 : char *tmp = pnstrdup(jb->val.string.val,
1069 48 : jb->val.string.len);
1070 48 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1071 :
1072 48 : val = float8in_internal(tmp,
1073 : NULL,
1074 : "double precision",
1075 : tmp,
1076 : (Node *) &escontext);
1077 :
1078 48 : if (escontext.error_occurred || isinf(val) || isnan(val))
1079 42 : RETURN_ERROR(ereport(ERROR,
1080 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1081 : errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
1082 : jspOperationName(jsp->type)))));
1083 :
1084 6 : jb = &jbv;
1085 6 : jb->type = jbvNumeric;
1086 6 : jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1087 : Float8GetDatum(val)));
1088 6 : res = jperOk;
1089 : }
1090 :
1091 60 : if (res == jperNotFound)
1092 48 : RETURN_ERROR(ereport(ERROR,
1093 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1094 : errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1095 : jspOperationName(jsp->type)))));
1096 :
1097 12 : res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1098 : }
1099 12 : break;
1100 :
1101 3222 : case jpiDatetime:
1102 3222 : if (unwrap && JsonbType(jb) == jbvArray)
1103 6 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1104 :
1105 3216 : return executeDateTimeMethod(cxt, jsp, jb, found);
1106 :
1107 90 : case jpiKeyValue:
1108 90 : if (unwrap && JsonbType(jb) == jbvArray)
1109 6 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1110 :
1111 84 : return executeKeyValueMethod(cxt, jsp, jb, found);
1112 :
1113 0 : default:
1114 0 : elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1115 : }
1116 :
1117 564498 : return res;
1118 : }
1119 :
1120 : /*
1121 : * Unwrap current array item and execute jsonpath for each of its elements.
1122 : */
1123 : static JsonPathExecResult
1124 1344 : executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1125 : JsonbValue *jb, JsonValueList *found,
1126 : bool unwrapElements)
1127 : {
1128 1344 : if (jb->type != jbvBinary)
1129 : {
1130 : Assert(jb->type != jbvArray);
1131 0 : elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1132 : }
1133 :
1134 1344 : return executeAnyItem
1135 : (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1136 : false, unwrapElements);
1137 : }
1138 :
1139 : /*
1140 : * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1141 : * list if provided.
1142 : */
1143 : static JsonPathExecResult
1144 412014 : executeNextItem(JsonPathExecContext *cxt,
1145 : JsonPathItem *cur, JsonPathItem *next,
1146 : JsonbValue *v, JsonValueList *found, bool copy)
1147 : {
1148 : JsonPathItem elem;
1149 : bool hasNext;
1150 :
1151 412014 : if (!cur)
1152 0 : hasNext = next != NULL;
1153 412014 : else if (next)
1154 161766 : hasNext = jspHasNext(cur);
1155 : else
1156 : {
1157 250248 : next = &elem;
1158 250248 : hasNext = jspGetNext(cur, next);
1159 : }
1160 :
1161 412014 : if (hasNext)
1162 185796 : return executeItem(cxt, next, v, found);
1163 :
1164 226218 : if (found)
1165 176178 : JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1166 :
1167 226218 : return jperOk;
1168 : }
1169 :
1170 : /*
1171 : * Same as executeItem(), but when "unwrap == true" automatically unwraps
1172 : * each array item from the resulting sequence in lax mode.
1173 : */
1174 : static JsonPathExecResult
1175 189546 : executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1176 : JsonbValue *jb, bool unwrap,
1177 : JsonValueList *found)
1178 : {
1179 189546 : if (unwrap && jspAutoUnwrap(cxt))
1180 : {
1181 110160 : JsonValueList seq = {0};
1182 : JsonValueListIterator it;
1183 110160 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1184 : JsonbValue *item;
1185 :
1186 110136 : if (jperIsError(res))
1187 66 : return res;
1188 :
1189 110070 : JsonValueListInitIterator(&seq, &it);
1190 182298 : while ((item = JsonValueListNext(&seq, &it)))
1191 : {
1192 : Assert(item->type != jbvArray);
1193 :
1194 72228 : if (JsonbType(item) == jbvArray)
1195 54 : executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1196 : else
1197 72174 : JsonValueListAppend(found, item);
1198 : }
1199 :
1200 110070 : return jperOk;
1201 : }
1202 :
1203 79386 : return executeItem(cxt, jsp, jb, found);
1204 : }
1205 :
1206 : /*
1207 : * Same as executeItemOptUnwrapResult(), but with error suppression.
1208 : */
1209 : static JsonPathExecResult
1210 188682 : executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1211 : JsonPathItem *jsp,
1212 : JsonbValue *jb, bool unwrap,
1213 : JsonValueList *found)
1214 : {
1215 : JsonPathExecResult res;
1216 188682 : bool throwErrors = cxt->throwErrors;
1217 :
1218 188682 : cxt->throwErrors = false;
1219 188682 : res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1220 188676 : cxt->throwErrors = throwErrors;
1221 :
1222 188676 : return res;
1223 : }
1224 :
1225 : /* Execute boolean-valued jsonpath expression. */
1226 : static JsonPathBool
1227 172944 : executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1228 : JsonbValue *jb, bool canHaveNext)
1229 : {
1230 : JsonPathItem larg;
1231 : JsonPathItem rarg;
1232 : JsonPathBool res;
1233 : JsonPathBool res2;
1234 :
1235 172944 : if (!canHaveNext && jspHasNext(jsp))
1236 0 : elog(ERROR, "boolean jsonpath item cannot have next item");
1237 :
1238 172944 : switch (jsp->type)
1239 : {
1240 25530 : case jpiAnd:
1241 25530 : jspGetLeftArg(jsp, &larg);
1242 25530 : res = executeBoolItem(cxt, &larg, jb, false);
1243 :
1244 25530 : if (res == jpbFalse)
1245 22470 : return jpbFalse;
1246 :
1247 : /*
1248 : * SQL/JSON says that we should check second arg in case of
1249 : * jperError
1250 : */
1251 :
1252 3060 : jspGetRightArg(jsp, &rarg);
1253 3060 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1254 :
1255 3060 : return res2 == jpbTrue ? res : res2;
1256 :
1257 12954 : case jpiOr:
1258 12954 : jspGetLeftArg(jsp, &larg);
1259 12954 : res = executeBoolItem(cxt, &larg, jb, false);
1260 :
1261 12954 : if (res == jpbTrue)
1262 2490 : return jpbTrue;
1263 :
1264 10464 : jspGetRightArg(jsp, &rarg);
1265 10464 : res2 = executeBoolItem(cxt, &rarg, jb, false);
1266 :
1267 10464 : return res2 == jpbFalse ? res : res2;
1268 :
1269 108 : case jpiNot:
1270 108 : jspGetArg(jsp, &larg);
1271 :
1272 108 : res = executeBoolItem(cxt, &larg, jb, false);
1273 :
1274 108 : if (res == jpbUnknown)
1275 36 : return jpbUnknown;
1276 :
1277 72 : return res == jpbTrue ? jpbFalse : jpbTrue;
1278 :
1279 210 : case jpiIsUnknown:
1280 210 : jspGetArg(jsp, &larg);
1281 210 : res = executeBoolItem(cxt, &larg, jb, false);
1282 210 : return res == jpbUnknown ? jpbTrue : jpbFalse;
1283 :
1284 54474 : case jpiEqual:
1285 : case jpiNotEqual:
1286 : case jpiLess:
1287 : case jpiGreater:
1288 : case jpiLessOrEqual:
1289 : case jpiGreaterOrEqual:
1290 54474 : jspGetLeftArg(jsp, &larg);
1291 54474 : jspGetRightArg(jsp, &rarg);
1292 54474 : return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1293 : executeComparison, cxt);
1294 :
1295 84 : case jpiStartsWith: /* 'whole STARTS WITH initial' */
1296 84 : jspGetLeftArg(jsp, &larg); /* 'whole' */
1297 84 : jspGetRightArg(jsp, &rarg); /* 'initial' */
1298 84 : return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1299 : executeStartsWith, NULL);
1300 :
1301 396 : case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1302 : {
1303 : /*
1304 : * 'expr' is a sequence-returning expression. 'pattern' is a
1305 : * regex string literal. SQL/JSON standard requires XQuery
1306 : * regexes, but we use Postgres regexes here. 'flags' is a
1307 : * string literal converted to integer flags at compile-time.
1308 : */
1309 396 : JsonLikeRegexContext lrcxt = {0};
1310 :
1311 396 : jspInitByBuffer(&larg, jsp->base,
1312 : jsp->content.like_regex.expr);
1313 :
1314 396 : return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1315 : executeLikeRegex, &lrcxt);
1316 : }
1317 :
1318 79188 : case jpiExists:
1319 79188 : jspGetArg(jsp, &larg);
1320 :
1321 79188 : if (jspStrictAbsenseOfErrors(cxt))
1322 : {
1323 : /*
1324 : * In strict mode we must get a complete list of values to
1325 : * check that there are no errors at all.
1326 : */
1327 48 : JsonValueList vals = {0};
1328 : JsonPathExecResult res =
1329 48 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1330 : false, &vals);
1331 :
1332 48 : if (jperIsError(res))
1333 36 : return jpbUnknown;
1334 :
1335 12 : return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1336 : }
1337 : else
1338 : {
1339 : JsonPathExecResult res =
1340 79140 : executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1341 : false, NULL);
1342 :
1343 79140 : if (jperIsError(res))
1344 24 : return jpbUnknown;
1345 :
1346 79116 : return res == jperOk ? jpbTrue : jpbFalse;
1347 : }
1348 :
1349 0 : default:
1350 0 : elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1351 : return jpbUnknown;
1352 : }
1353 : }
1354 :
1355 : /*
1356 : * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1357 : * item onto the stack.
1358 : */
1359 : static JsonPathBool
1360 18456 : executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1361 : JsonbValue *jb)
1362 : {
1363 : JsonbValue *prev;
1364 : JsonPathBool res;
1365 :
1366 18456 : prev = cxt->current;
1367 18456 : cxt->current = jb;
1368 18456 : res = executeBoolItem(cxt, jsp, jb, false);
1369 18360 : cxt->current = prev;
1370 :
1371 18360 : return res;
1372 : }
1373 :
1374 : /*
1375 : * Implementation of several jsonpath nodes:
1376 : * - jpiAny (.** accessor),
1377 : * - jpiAnyKey (.* accessor),
1378 : * - jpiAnyArray ([*] accessor)
1379 : */
1380 : static JsonPathExecResult
1381 1896 : executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1382 : JsonValueList *found, uint32 level, uint32 first, uint32 last,
1383 : bool ignoreStructuralErrors, bool unwrapNext)
1384 : {
1385 1896 : JsonPathExecResult res = jperNotFound;
1386 : JsonbIterator *it;
1387 : int32 r;
1388 : JsonbValue v;
1389 :
1390 1896 : check_stack_depth();
1391 :
1392 1896 : if (level > last)
1393 30 : return res;
1394 :
1395 1866 : it = JsonbIteratorInit(jbc);
1396 :
1397 : /*
1398 : * Recursively iterate over jsonb objects/arrays
1399 : */
1400 9630 : while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1401 : {
1402 8340 : if (r == WJB_KEY)
1403 : {
1404 552 : r = JsonbIteratorNext(&it, &v, true);
1405 : Assert(r == WJB_VALUE);
1406 : }
1407 :
1408 8340 : if (r == WJB_VALUE || r == WJB_ELEM)
1409 : {
1410 :
1411 5184 : if (level >= first ||
1412 12 : (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1413 12 : v.type != jbvBinary)) /* leaves only requested */
1414 : {
1415 : /* check expression */
1416 5124 : if (jsp)
1417 : {
1418 3984 : if (ignoreStructuralErrors)
1419 : {
1420 : bool savedIgnoreStructuralErrors;
1421 :
1422 348 : savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1423 348 : cxt->ignoreStructuralErrors = true;
1424 348 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1425 348 : cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1426 : }
1427 : else
1428 3636 : res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1429 :
1430 3858 : if (jperIsError(res))
1431 66 : break;
1432 :
1433 3792 : if (res == jperOk && !found)
1434 324 : break;
1435 : }
1436 1140 : else if (found)
1437 1116 : JsonValueListAppend(found, copyJsonbValue(&v));
1438 : else
1439 24 : return jperOk;
1440 : }
1441 :
1442 4644 : if (level < last && v.type == jbvBinary)
1443 : {
1444 186 : res = executeAnyItem
1445 : (cxt, jsp, v.val.binary.data, found,
1446 : level + 1, first, last,
1447 : ignoreStructuralErrors, unwrapNext);
1448 :
1449 186 : if (jperIsError(res))
1450 0 : break;
1451 :
1452 186 : if (res == jperOk && found == NULL)
1453 36 : break;
1454 : }
1455 : }
1456 : }
1457 :
1458 1716 : return res;
1459 : }
1460 :
1461 : /*
1462 : * Execute unary or binary predicate.
1463 : *
1464 : * Predicates have existence semantics, because their operands are item
1465 : * sequences. Pairs of items from the left and right operand's sequences are
1466 : * checked. TRUE returned only if any pair satisfying the condition is found.
1467 : * In strict mode, even if the desired pair has already been found, all pairs
1468 : * still need to be examined to check the absence of errors. If any error
1469 : * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1470 : */
1471 : static JsonPathBool
1472 54954 : executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1473 : JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1474 : bool unwrapRightArg, JsonPathPredicateCallback exec,
1475 : void *param)
1476 : {
1477 : JsonPathExecResult res;
1478 : JsonValueListIterator lseqit;
1479 54954 : JsonValueList lseq = {0};
1480 54954 : JsonValueList rseq = {0};
1481 : JsonbValue *lval;
1482 54954 : bool error = false;
1483 54954 : bool found = false;
1484 :
1485 : /* Left argument is always auto-unwrapped. */
1486 54954 : res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1487 54954 : if (jperIsError(res))
1488 18 : return jpbUnknown;
1489 :
1490 54936 : if (rarg)
1491 : {
1492 : /* Right argument is conditionally auto-unwrapped. */
1493 54540 : res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1494 : unwrapRightArg, &rseq);
1495 54534 : if (jperIsError(res))
1496 54 : return jpbUnknown;
1497 : }
1498 :
1499 54876 : JsonValueListInitIterator(&lseq, &lseqit);
1500 72042 : while ((lval = JsonValueListNext(&lseq, &lseqit)))
1501 : {
1502 : JsonValueListIterator rseqit;
1503 : JsonbValue *rval;
1504 22074 : bool first = true;
1505 :
1506 22074 : JsonValueListInitIterator(&rseq, &rseqit);
1507 22074 : if (rarg)
1508 21678 : rval = JsonValueListNext(&rseq, &rseqit);
1509 : else
1510 396 : rval = NULL;
1511 :
1512 : /* Loop over right arg sequence or do single pass otherwise */
1513 34296 : while (rarg ? (rval != NULL) : first)
1514 : {
1515 17130 : JsonPathBool res = exec(pred, lval, rval, param);
1516 :
1517 17040 : if (res == jpbUnknown)
1518 : {
1519 678 : if (jspStrictAbsenseOfErrors(cxt))
1520 4818 : return jpbUnknown;
1521 :
1522 654 : error = true;
1523 : }
1524 16362 : else if (res == jpbTrue)
1525 : {
1526 4842 : if (!jspStrictAbsenseOfErrors(cxt))
1527 4794 : return jpbTrue;
1528 :
1529 48 : found = true;
1530 : }
1531 :
1532 12222 : first = false;
1533 12222 : if (rarg)
1534 11928 : rval = JsonValueListNext(&rseq, &rseqit);
1535 : }
1536 : }
1537 :
1538 49968 : if (found) /* possible only in strict mode */
1539 18 : return jpbTrue;
1540 :
1541 49950 : if (error) /* possible only in lax mode */
1542 624 : return jpbUnknown;
1543 :
1544 49326 : return jpbFalse;
1545 : }
1546 :
1547 : /*
1548 : * Execute binary arithmetic expression on singleton numeric operands.
1549 : * Array operands are automatically unwrapped in lax mode.
1550 : */
1551 : static JsonPathExecResult
1552 342 : executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1553 : JsonbValue *jb, BinaryArithmFunc func,
1554 : JsonValueList *found)
1555 : {
1556 : JsonPathExecResult jper;
1557 : JsonPathItem elem;
1558 342 : JsonValueList lseq = {0};
1559 342 : JsonValueList rseq = {0};
1560 : JsonbValue *lval;
1561 : JsonbValue *rval;
1562 : Numeric res;
1563 :
1564 342 : jspGetLeftArg(jsp, &elem);
1565 :
1566 : /*
1567 : * XXX: By standard only operands of multiplicative expressions are
1568 : * unwrapped. We extend it to other binary arithmetic expressions too.
1569 : */
1570 342 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1571 336 : if (jperIsError(jper))
1572 0 : return jper;
1573 :
1574 336 : jspGetRightArg(jsp, &elem);
1575 :
1576 336 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1577 330 : if (jperIsError(jper))
1578 0 : return jper;
1579 :
1580 594 : if (JsonValueListLength(&lseq) != 1 ||
1581 264 : !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1582 66 : RETURN_ERROR(ereport(ERROR,
1583 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1584 : errmsg("left operand of jsonpath operator %s is not a single numeric value",
1585 : jspOperationName(jsp->type)))));
1586 :
1587 498 : if (JsonValueListLength(&rseq) != 1 ||
1588 234 : !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1589 54 : RETURN_ERROR(ereport(ERROR,
1590 : (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1591 : errmsg("right operand of jsonpath operator %s is not a single numeric value",
1592 : jspOperationName(jsp->type)))));
1593 :
1594 210 : if (jspThrowErrors(cxt))
1595 : {
1596 78 : res = func(lval->val.numeric, rval->val.numeric, NULL);
1597 : }
1598 : else
1599 : {
1600 132 : bool error = false;
1601 :
1602 132 : res = func(lval->val.numeric, rval->val.numeric, &error);
1603 :
1604 132 : if (error)
1605 12 : return jperError;
1606 : }
1607 :
1608 174 : if (!jspGetNext(jsp, &elem) && !found)
1609 6 : return jperOk;
1610 :
1611 168 : lval = palloc(sizeof(*lval));
1612 168 : lval->type = jbvNumeric;
1613 168 : lval->val.numeric = res;
1614 :
1615 168 : return executeNextItem(cxt, jsp, &elem, lval, found, false);
1616 : }
1617 :
1618 : /*
1619 : * Execute unary arithmetic expression for each numeric item in its operand's
1620 : * sequence. Array operand is automatically unwrapped in lax mode.
1621 : */
1622 : static JsonPathExecResult
1623 186 : executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1624 : JsonbValue *jb, PGFunction func, JsonValueList *found)
1625 : {
1626 : JsonPathExecResult jper;
1627 : JsonPathExecResult jper2;
1628 : JsonPathItem elem;
1629 186 : JsonValueList seq = {0};
1630 : JsonValueListIterator it;
1631 : JsonbValue *val;
1632 : bool hasNext;
1633 :
1634 186 : jspGetArg(jsp, &elem);
1635 186 : jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1636 :
1637 180 : if (jperIsError(jper))
1638 0 : return jper;
1639 :
1640 180 : jper = jperNotFound;
1641 :
1642 180 : hasNext = jspGetNext(jsp, &elem);
1643 :
1644 180 : JsonValueListInitIterator(&seq, &it);
1645 324 : while ((val = JsonValueListNext(&seq, &it)))
1646 : {
1647 192 : if ((val = getScalar(val, jbvNumeric)))
1648 : {
1649 150 : if (!found && !hasNext)
1650 12 : return jperOk;
1651 : }
1652 : else
1653 : {
1654 42 : if (!found && !hasNext)
1655 6 : continue; /* skip non-numerics processing */
1656 :
1657 36 : RETURN_ERROR(ereport(ERROR,
1658 : (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1659 : errmsg("operand of unary jsonpath operator %s is not a numeric value",
1660 : jspOperationName(jsp->type)))));
1661 : }
1662 :
1663 138 : if (func)
1664 84 : val->val.numeric =
1665 84 : DatumGetNumeric(DirectFunctionCall1(func,
1666 : NumericGetDatum(val->val.numeric)));
1667 :
1668 138 : jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1669 :
1670 138 : if (jperIsError(jper2))
1671 0 : return jper2;
1672 :
1673 138 : if (jper2 == jperOk)
1674 : {
1675 138 : if (!found)
1676 0 : return jperOk;
1677 138 : jper = jperOk;
1678 : }
1679 : }
1680 :
1681 132 : return jper;
1682 : }
1683 :
1684 : /*
1685 : * STARTS_WITH predicate callback.
1686 : *
1687 : * Check if the 'whole' string starts from 'initial' string.
1688 : */
1689 : static JsonPathBool
1690 174 : executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1691 : void *param)
1692 : {
1693 174 : if (!(whole = getScalar(whole, jbvString)))
1694 48 : return jpbUnknown; /* error */
1695 :
1696 126 : if (!(initial = getScalar(initial, jbvString)))
1697 0 : return jpbUnknown; /* error */
1698 :
1699 126 : if (whole->val.string.len >= initial->val.string.len &&
1700 90 : !memcmp(whole->val.string.val,
1701 90 : initial->val.string.val,
1702 90 : initial->val.string.len))
1703 54 : return jpbTrue;
1704 :
1705 72 : return jpbFalse;
1706 : }
1707 :
1708 : /*
1709 : * LIKE_REGEX predicate callback.
1710 : *
1711 : * Check if the string matches regex pattern.
1712 : */
1713 : static JsonPathBool
1714 396 : executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1715 : void *param)
1716 : {
1717 396 : JsonLikeRegexContext *cxt = param;
1718 :
1719 396 : if (!(str = getScalar(str, jbvString)))
1720 120 : return jpbUnknown;
1721 :
1722 : /* Cache regex text and converted flags. */
1723 276 : if (!cxt->regex)
1724 : {
1725 276 : cxt->regex =
1726 276 : cstring_to_text_with_len(jsp->content.like_regex.pattern,
1727 : jsp->content.like_regex.patternlen);
1728 276 : (void) jspConvertRegexFlags(jsp->content.like_regex.flags,
1729 : &(cxt->cflags), NULL);
1730 : }
1731 :
1732 276 : if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1733 : str->val.string.len,
1734 : cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1735 102 : return jpbTrue;
1736 :
1737 174 : return jpbFalse;
1738 : }
1739 :
1740 : /*
1741 : * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1742 : * user function 'func'.
1743 : */
1744 : static JsonPathExecResult
1745 258 : executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1746 : JsonbValue *jb, bool unwrap, PGFunction func,
1747 : JsonValueList *found)
1748 : {
1749 : JsonPathItem next;
1750 : Datum datum;
1751 :
1752 258 : if (unwrap && JsonbType(jb) == jbvArray)
1753 0 : return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1754 :
1755 258 : if (!(jb = getScalar(jb, jbvNumeric)))
1756 36 : RETURN_ERROR(ereport(ERROR,
1757 : (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1758 : errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1759 : jspOperationName(jsp->type)))));
1760 :
1761 222 : datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1762 :
1763 222 : if (!jspGetNext(jsp, &next) && !found)
1764 0 : return jperOk;
1765 :
1766 222 : jb = palloc(sizeof(*jb));
1767 222 : jb->type = jbvNumeric;
1768 222 : jb->val.numeric = DatumGetNumeric(datum);
1769 :
1770 222 : return executeNextItem(cxt, jsp, &next, jb, found, false);
1771 : }
1772 :
1773 : /*
1774 : * Implementation of the .datetime() method.
1775 : *
1776 : * Converts a string into a date/time value. The actual type is determined at run time.
1777 : * If an argument is provided, this argument is used as a template string.
1778 : * Otherwise, the first fitting ISO format is selected.
1779 : */
1780 : static JsonPathExecResult
1781 3216 : executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1782 : JsonbValue *jb, JsonValueList *found)
1783 : {
1784 : JsonbValue jbvbuf;
1785 : Datum value;
1786 : text *datetime;
1787 : Oid collid;
1788 : Oid typid;
1789 3216 : int32 typmod = -1;
1790 3216 : int tz = 0;
1791 : bool hasNext;
1792 3216 : JsonPathExecResult res = jperNotFound;
1793 : JsonPathItem elem;
1794 :
1795 3216 : if (!(jb = getScalar(jb, jbvString)))
1796 30 : RETURN_ERROR(ereport(ERROR,
1797 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1798 : errmsg("jsonpath item method .%s() can only be applied to a string",
1799 : jspOperationName(jsp->type)))));
1800 :
1801 3186 : datetime = cstring_to_text_with_len(jb->val.string.val,
1802 : jb->val.string.len);
1803 :
1804 : /*
1805 : * At some point we might wish to have callers supply the collation to
1806 : * use, but right now it's unclear that they'd be able to do better than
1807 : * DEFAULT_COLLATION_OID anyway.
1808 : */
1809 3186 : collid = DEFAULT_COLLATION_OID;
1810 :
1811 3186 : if (jsp->content.arg)
1812 : {
1813 : text *template;
1814 : char *template_str;
1815 : int template_len;
1816 1650 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1817 :
1818 1650 : jspGetArg(jsp, &elem);
1819 :
1820 1650 : if (elem.type != jpiString)
1821 0 : elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1822 :
1823 1650 : template_str = jspGetString(&elem, &template_len);
1824 :
1825 1650 : template = cstring_to_text_with_len(template_str,
1826 : template_len);
1827 :
1828 1650 : value = parse_datetime(datetime, template, collid, true,
1829 : &typid, &typmod, &tz,
1830 1650 : jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
1831 :
1832 1590 : if (escontext.error_occurred)
1833 0 : res = jperError;
1834 : else
1835 1590 : res = jperOk;
1836 : }
1837 : else
1838 : {
1839 : /*
1840 : * According to SQL/JSON standard enumerate ISO formats for: date,
1841 : * timetz, time, timestamptz, timestamp.
1842 : *
1843 : * We also support ISO 8601 for timestamps, because to_json[b]()
1844 : * functions use this format.
1845 : */
1846 : static const char *fmt_str[] =
1847 : {
1848 : "yyyy-mm-dd",
1849 : "HH24:MI:SSTZH:TZM",
1850 : "HH24:MI:SSTZH",
1851 : "HH24:MI:SS",
1852 : "yyyy-mm-dd HH24:MI:SSTZH:TZM",
1853 : "yyyy-mm-dd HH24:MI:SSTZH",
1854 : "yyyy-mm-dd HH24:MI:SS",
1855 : "yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
1856 : "yyyy-mm-dd\"T\"HH24:MI:SSTZH",
1857 : "yyyy-mm-dd\"T\"HH24:MI:SS"
1858 : };
1859 :
1860 : /* cache for format texts */
1861 : static text *fmt_txt[lengthof(fmt_str)] = {0};
1862 : int i;
1863 :
1864 : /* loop until datetime format fits */
1865 6828 : for (i = 0; i < lengthof(fmt_str); i++)
1866 : {
1867 6816 : ErrorSaveContext escontext = {T_ErrorSaveContext};
1868 :
1869 6816 : if (!fmt_txt[i])
1870 : {
1871 : MemoryContext oldcxt =
1872 60 : MemoryContextSwitchTo(TopMemoryContext);
1873 :
1874 60 : fmt_txt[i] = cstring_to_text(fmt_str[i]);
1875 60 : MemoryContextSwitchTo(oldcxt);
1876 : }
1877 :
1878 6816 : value = parse_datetime(datetime, fmt_txt[i], collid, true,
1879 : &typid, &typmod, &tz,
1880 : (Node *) &escontext);
1881 :
1882 6816 : if (!escontext.error_occurred)
1883 : {
1884 1524 : res = jperOk;
1885 1524 : break;
1886 : }
1887 : }
1888 :
1889 1536 : if (res == jperNotFound)
1890 12 : RETURN_ERROR(ereport(ERROR,
1891 : (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1892 : errmsg("datetime format is not recognized: \"%s\"",
1893 : text_to_cstring(datetime)),
1894 : errhint("Use a datetime template argument to specify the input data format."))));
1895 : }
1896 :
1897 3114 : pfree(datetime);
1898 :
1899 3114 : if (jperIsError(res))
1900 0 : return res;
1901 :
1902 3114 : hasNext = jspGetNext(jsp, &elem);
1903 :
1904 3114 : if (!hasNext && !found)
1905 6 : return res;
1906 :
1907 3108 : jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1908 :
1909 3108 : jb->type = jbvDatetime;
1910 3108 : jb->val.datetime.value = value;
1911 3108 : jb->val.datetime.typid = typid;
1912 3108 : jb->val.datetime.typmod = typmod;
1913 3108 : jb->val.datetime.tz = tz;
1914 :
1915 3108 : return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1916 : }
1917 :
1918 : /*
1919 : * Implementation of .keyvalue() method.
1920 : *
1921 : * .keyvalue() method returns a sequence of object's key-value pairs in the
1922 : * following format: '{ "key": key, "value": value, "id": id }'.
1923 : *
1924 : * "id" field is an object identifier which is constructed from the two parts:
1925 : * base object id and its binary offset in base object's jsonb:
1926 : * id = 10000000000 * base_object_id + obj_offset_in_base_object
1927 : *
1928 : * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1929 : * (maximal offset in jsonb). Decimal multiplier is used here to improve the
1930 : * readability of identifiers.
1931 : *
1932 : * Base object is usually a root object of the path: context item '$' or path
1933 : * variable '$var', literals can't produce objects for now. But if the path
1934 : * contains generated objects (.keyvalue() itself, for example), then they
1935 : * become base object for the subsequent .keyvalue().
1936 : *
1937 : * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1938 : * of variables (see getJsonPathVariable()). Ids for generated objects
1939 : * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1940 : */
1941 : static JsonPathExecResult
1942 84 : executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1943 : JsonbValue *jb, JsonValueList *found)
1944 : {
1945 84 : JsonPathExecResult res = jperNotFound;
1946 : JsonPathItem next;
1947 : JsonbContainer *jbc;
1948 : JsonbValue key;
1949 : JsonbValue val;
1950 : JsonbValue idval;
1951 : JsonbValue keystr;
1952 : JsonbValue valstr;
1953 : JsonbValue idstr;
1954 : JsonbIterator *it;
1955 : JsonbIteratorToken tok;
1956 : int64 id;
1957 : bool hasNext;
1958 :
1959 84 : if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1960 24 : RETURN_ERROR(ereport(ERROR,
1961 : (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1962 : errmsg("jsonpath item method .%s() can only be applied to an object",
1963 : jspOperationName(jsp->type)))));
1964 :
1965 60 : jbc = jb->val.binary.data;
1966 :
1967 60 : if (!JsonContainerSize(jbc))
1968 18 : return jperNotFound; /* no key-value pairs */
1969 :
1970 42 : hasNext = jspGetNext(jsp, &next);
1971 :
1972 42 : keystr.type = jbvString;
1973 42 : keystr.val.string.val = "key";
1974 42 : keystr.val.string.len = 3;
1975 :
1976 42 : valstr.type = jbvString;
1977 42 : valstr.val.string.val = "value";
1978 42 : valstr.val.string.len = 5;
1979 :
1980 42 : idstr.type = jbvString;
1981 42 : idstr.val.string.val = "id";
1982 42 : idstr.val.string.len = 2;
1983 :
1984 : /* construct object id from its base object and offset inside that */
1985 42 : id = jb->type != jbvBinary ? 0 :
1986 42 : (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1987 42 : id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1988 :
1989 42 : idval.type = jbvNumeric;
1990 42 : idval.val.numeric = int64_to_numeric(id);
1991 :
1992 42 : it = JsonbIteratorInit(jbc);
1993 :
1994 168 : while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1995 : {
1996 : JsonBaseObjectInfo baseObject;
1997 : JsonbValue obj;
1998 : JsonbParseState *ps;
1999 : JsonbValue *keyval;
2000 : Jsonb *jsonb;
2001 :
2002 138 : if (tok != WJB_KEY)
2003 72 : continue;
2004 :
2005 66 : res = jperOk;
2006 :
2007 66 : if (!hasNext && !found)
2008 12 : break;
2009 :
2010 60 : tok = JsonbIteratorNext(&it, &val, true);
2011 : Assert(tok == WJB_VALUE);
2012 :
2013 60 : ps = NULL;
2014 60 : pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2015 :
2016 60 : pushJsonbValue(&ps, WJB_KEY, &keystr);
2017 60 : pushJsonbValue(&ps, WJB_VALUE, &key);
2018 :
2019 60 : pushJsonbValue(&ps, WJB_KEY, &valstr);
2020 60 : pushJsonbValue(&ps, WJB_VALUE, &val);
2021 :
2022 60 : pushJsonbValue(&ps, WJB_KEY, &idstr);
2023 60 : pushJsonbValue(&ps, WJB_VALUE, &idval);
2024 :
2025 60 : keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2026 :
2027 60 : jsonb = JsonbValueToJsonb(keyval);
2028 :
2029 60 : JsonbInitBinary(&obj, jsonb);
2030 :
2031 60 : baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2032 :
2033 60 : res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2034 :
2035 60 : cxt->baseObject = baseObject;
2036 :
2037 60 : if (jperIsError(res))
2038 0 : return res;
2039 :
2040 60 : if (res == jperOk && !found)
2041 6 : break;
2042 : }
2043 :
2044 42 : return res;
2045 : }
2046 :
2047 : /*
2048 : * Convert boolean execution status 'res' to a boolean JSON item and execute
2049 : * next jsonpath.
2050 : */
2051 : static JsonPathExecResult
2052 102162 : appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2053 : JsonValueList *found, JsonPathBool res)
2054 : {
2055 : JsonPathItem next;
2056 : JsonbValue jbv;
2057 :
2058 102162 : if (!jspGetNext(jsp, &next) && !found)
2059 6 : return jperOk; /* found singleton boolean value */
2060 :
2061 102156 : if (res == jpbUnknown)
2062 : {
2063 30 : jbv.type = jbvNull;
2064 : }
2065 : else
2066 : {
2067 102126 : jbv.type = jbvBool;
2068 102126 : jbv.val.boolean = res == jpbTrue;
2069 : }
2070 :
2071 102156 : return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2072 : }
2073 :
2074 : /*
2075 : * Convert jsonpath's scalar or variable node to actual jsonb value.
2076 : *
2077 : * If node is a variable then its id returned, otherwise 0 returned.
2078 : */
2079 : static void
2080 55536 : getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2081 : JsonbValue *value)
2082 : {
2083 55536 : switch (item->type)
2084 : {
2085 7554 : case jpiNull:
2086 7554 : value->type = jbvNull;
2087 7554 : break;
2088 1422 : case jpiBool:
2089 1422 : value->type = jbvBool;
2090 1422 : value->val.boolean = jspGetBool(item);
2091 1422 : break;
2092 19356 : case jpiNumeric:
2093 19356 : value->type = jbvNumeric;
2094 19356 : value->val.numeric = jspGetNumeric(item);
2095 19356 : break;
2096 21954 : case jpiString:
2097 21954 : value->type = jbvString;
2098 43908 : value->val.string.val = jspGetString(item,
2099 21954 : &value->val.string.len);
2100 21954 : break;
2101 5250 : case jpiVariable:
2102 5250 : getJsonPathVariable(cxt, item, cxt->vars, value);
2103 5220 : return;
2104 0 : default:
2105 0 : elog(ERROR, "unexpected jsonpath item type");
2106 : }
2107 : }
2108 :
2109 : /*
2110 : * Get the value of variable passed to jsonpath executor
2111 : */
2112 : static void
2113 5250 : getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2114 : Jsonb *vars, JsonbValue *value)
2115 : {
2116 : char *varName;
2117 : int varNameLength;
2118 : JsonbValue tmp;
2119 : JsonbValue *v;
2120 :
2121 5250 : if (!vars)
2122 : {
2123 0 : value->type = jbvNull;
2124 0 : return;
2125 : }
2126 :
2127 : Assert(variable->type == jpiVariable);
2128 5250 : varName = jspGetString(variable, &varNameLength);
2129 5250 : tmp.type = jbvString;
2130 5250 : tmp.val.string.val = varName;
2131 5250 : tmp.val.string.len = varNameLength;
2132 :
2133 5250 : v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2134 :
2135 5250 : if (v)
2136 : {
2137 5220 : *value = *v;
2138 5220 : pfree(v);
2139 : }
2140 : else
2141 : {
2142 30 : ereport(ERROR,
2143 : (errcode(ERRCODE_UNDEFINED_OBJECT),
2144 : errmsg("could not find jsonpath variable \"%s\"",
2145 : pnstrdup(varName, varNameLength))));
2146 : }
2147 :
2148 5220 : JsonbInitBinary(&tmp, vars);
2149 5220 : setBaseObject(cxt, &tmp, 1);
2150 : }
2151 :
2152 : /**************** Support functions for JsonPath execution *****************/
2153 :
2154 : /*
2155 : * Returns the size of an array item, or -1 if item is not an array.
2156 : */
2157 : static int
2158 348 : JsonbArraySize(JsonbValue *jb)
2159 : {
2160 : Assert(jb->type != jbvArray);
2161 :
2162 348 : if (jb->type == jbvBinary)
2163 : {
2164 306 : JsonbContainer *jbc = jb->val.binary.data;
2165 :
2166 306 : if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2167 294 : return JsonContainerSize(jbc);
2168 : }
2169 :
2170 54 : return -1;
2171 : }
2172 :
2173 : /* Comparison predicate callback. */
2174 : static JsonPathBool
2175 16560 : executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2176 : {
2177 16560 : JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2178 :
2179 16560 : return compareItems(cmp->type, lv, rv, cxt->useTz);
2180 : }
2181 :
2182 : /*
2183 : * Perform per-byte comparison of two strings.
2184 : */
2185 : static int
2186 3456 : binaryCompareStrings(const char *s1, int len1,
2187 : const char *s2, int len2)
2188 : {
2189 : int cmp;
2190 :
2191 3456 : cmp = memcmp(s1, s2, Min(len1, len2));
2192 :
2193 3456 : if (cmp != 0)
2194 1968 : return cmp;
2195 :
2196 1488 : if (len1 == len2)
2197 288 : return 0;
2198 :
2199 1200 : return len1 < len2 ? -1 : 1;
2200 : }
2201 :
2202 : /*
2203 : * Compare two strings in the current server encoding using Unicode codepoint
2204 : * collation.
2205 : */
2206 : static int
2207 3456 : compareStrings(const char *mbstr1, int mblen1,
2208 : const char *mbstr2, int mblen2)
2209 : {
2210 6912 : if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2211 3456 : GetDatabaseEncoding() == PG_UTF8)
2212 : {
2213 : /*
2214 : * It's known property of UTF-8 strings that their per-byte comparison
2215 : * result matches codepoints comparison result. ASCII can be
2216 : * considered as special case of UTF-8.
2217 : */
2218 3456 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2219 : }
2220 : else
2221 : {
2222 : char *utf8str1,
2223 : *utf8str2;
2224 : int cmp,
2225 : utf8len1,
2226 : utf8len2;
2227 :
2228 : /*
2229 : * We have to convert other encodings to UTF-8 first, then compare.
2230 : * Input strings may be not null-terminated and pg_server_to_any() may
2231 : * return them "as is". So, use strlen() only if there is real
2232 : * conversion.
2233 : */
2234 0 : utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2235 0 : utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2236 0 : utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2237 0 : utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2238 :
2239 0 : cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2240 :
2241 : /*
2242 : * If pg_server_to_any() did no real conversion, then we actually
2243 : * compared original strings. So, we already done.
2244 : */
2245 0 : if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2246 0 : return cmp;
2247 :
2248 : /* Free memory if needed */
2249 0 : if (mbstr1 != utf8str1)
2250 0 : pfree(utf8str1);
2251 0 : if (mbstr2 != utf8str2)
2252 0 : pfree(utf8str2);
2253 :
2254 : /*
2255 : * When all Unicode codepoints are equal, return result of binary
2256 : * comparison. In some edge cases, same characters may have different
2257 : * representations in encoding. Then our behavior could diverge from
2258 : * standard. However, that allow us to do simple binary comparison
2259 : * for "==" operator, which is performance critical in typical cases.
2260 : * In future to implement strict standard conformance, we can do
2261 : * normalization of input JSON strings.
2262 : */
2263 0 : if (cmp == 0)
2264 0 : return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2265 : else
2266 0 : return cmp;
2267 : }
2268 : }
2269 :
2270 : /*
2271 : * Compare two SQL/JSON items using comparison operation 'op'.
2272 : */
2273 : static JsonPathBool
2274 16560 : compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2275 : {
2276 : int cmp;
2277 : bool res;
2278 :
2279 16560 : if (jb1->type != jb2->type)
2280 : {
2281 2970 : if (jb1->type == jbvNull || jb2->type == jbvNull)
2282 :
2283 : /*
2284 : * Equality and order comparison of nulls to non-nulls returns
2285 : * always false, but inequality comparison returns true.
2286 : */
2287 2724 : return op == jpiNotEqual ? jpbTrue : jpbFalse;
2288 :
2289 : /* Non-null items of different types are not comparable. */
2290 246 : return jpbUnknown;
2291 : }
2292 :
2293 13590 : switch (jb1->type)
2294 : {
2295 186 : case jbvNull:
2296 186 : cmp = 0;
2297 186 : break;
2298 870 : case jbvBool:
2299 1266 : cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2300 396 : jb1->val.boolean ? 1 : -1;
2301 870 : break;
2302 1164 : case jbvNumeric:
2303 1164 : cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2304 1164 : break;
2305 9930 : case jbvString:
2306 9930 : if (op == jpiEqual)
2307 6474 : return jb1->val.string.len != jb2->val.string.len ||
2308 3582 : memcmp(jb1->val.string.val,
2309 3582 : jb2->val.string.val,
2310 6474 : jb1->val.string.len) ? jpbFalse : jpbTrue;
2311 :
2312 3456 : cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2313 3456 : jb2->val.string.val, jb2->val.string.len);
2314 3456 : break;
2315 1428 : case jbvDatetime:
2316 : {
2317 : bool cast_error;
2318 :
2319 1428 : cmp = compareDatetime(jb1->val.datetime.value,
2320 : jb1->val.datetime.typid,
2321 : jb2->val.datetime.value,
2322 : jb2->val.datetime.typid,
2323 : useTz,
2324 : &cast_error);
2325 :
2326 1338 : if (cast_error)
2327 252 : return jpbUnknown;
2328 : }
2329 1086 : break;
2330 :
2331 12 : case jbvBinary:
2332 : case jbvArray:
2333 : case jbvObject:
2334 12 : return jpbUnknown; /* non-scalars are not comparable */
2335 :
2336 0 : default:
2337 0 : elog(ERROR, "invalid jsonb value type %d", jb1->type);
2338 : }
2339 :
2340 6762 : switch (op)
2341 : {
2342 1686 : case jpiEqual:
2343 1686 : res = (cmp == 0);
2344 1686 : break;
2345 6 : case jpiNotEqual:
2346 6 : res = (cmp != 0);
2347 6 : break;
2348 1416 : case jpiLess:
2349 1416 : res = (cmp < 0);
2350 1416 : break;
2351 1434 : case jpiGreater:
2352 1434 : res = (cmp > 0);
2353 1434 : break;
2354 876 : case jpiLessOrEqual:
2355 876 : res = (cmp <= 0);
2356 876 : break;
2357 1344 : case jpiGreaterOrEqual:
2358 1344 : res = (cmp >= 0);
2359 1344 : break;
2360 0 : default:
2361 0 : elog(ERROR, "unrecognized jsonpath operation: %d", op);
2362 : return jpbUnknown;
2363 : }
2364 :
2365 6762 : return res ? jpbTrue : jpbFalse;
2366 : }
2367 :
2368 : /* Compare two numerics */
2369 : static int
2370 1164 : compareNumeric(Numeric a, Numeric b)
2371 : {
2372 1164 : return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2373 : NumericGetDatum(a),
2374 : NumericGetDatum(b)));
2375 : }
2376 :
2377 : static JsonbValue *
2378 110778 : copyJsonbValue(JsonbValue *src)
2379 : {
2380 110778 : JsonbValue *dst = palloc(sizeof(*dst));
2381 :
2382 110778 : *dst = *src;
2383 :
2384 110778 : return dst;
2385 : }
2386 :
2387 : /*
2388 : * Execute array subscript expression and convert resulting numeric item to
2389 : * the integer type with truncation.
2390 : */
2391 : static JsonPathExecResult
2392 318 : getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2393 : int32 *index)
2394 : {
2395 : JsonbValue *jbv;
2396 318 : JsonValueList found = {0};
2397 318 : JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2398 : Datum numeric_index;
2399 312 : bool have_error = false;
2400 :
2401 312 : if (jperIsError(res))
2402 0 : return res;
2403 :
2404 612 : if (JsonValueListLength(&found) != 1 ||
2405 300 : !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2406 24 : RETURN_ERROR(ereport(ERROR,
2407 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2408 : errmsg("jsonpath array subscript is not a single numeric value"))));
2409 :
2410 288 : numeric_index = DirectFunctionCall2(numeric_trunc,
2411 : NumericGetDatum(jbv->val.numeric),
2412 : Int32GetDatum(0));
2413 :
2414 288 : *index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2415 : &have_error);
2416 :
2417 288 : if (have_error)
2418 24 : RETURN_ERROR(ereport(ERROR,
2419 : (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2420 : errmsg("jsonpath array subscript is out of integer range"))));
2421 :
2422 264 : return jperOk;
2423 : }
2424 :
2425 : /* Save base object and its id needed for the execution of .keyvalue(). */
2426 : static JsonBaseObjectInfo
2427 207690 : setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2428 : {
2429 207690 : JsonBaseObjectInfo baseObject = cxt->baseObject;
2430 :
2431 207690 : cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2432 : (JsonbContainer *) jbv->val.binary.data;
2433 207690 : cxt->baseObject.id = id;
2434 :
2435 207690 : return baseObject;
2436 : }
2437 :
2438 : static void
2439 249468 : JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2440 : {
2441 249468 : if (jvl->singleton)
2442 : {
2443 984 : jvl->list = list_make2(jvl->singleton, jbv);
2444 984 : jvl->singleton = NULL;
2445 : }
2446 248484 : else if (!jvl->list)
2447 247584 : jvl->singleton = jbv;
2448 : else
2449 900 : jvl->list = lappend(jvl->list, jbv);
2450 249468 : }
2451 :
2452 : static int
2453 103170 : JsonValueListLength(const JsonValueList *jvl)
2454 : {
2455 103170 : return jvl->singleton ? 1 : list_length(jvl->list);
2456 : }
2457 :
2458 : static bool
2459 18 : JsonValueListIsEmpty(JsonValueList *jvl)
2460 : {
2461 18 : return !jvl->singleton && (jvl->list == NIL);
2462 : }
2463 :
2464 : static JsonbValue *
2465 103020 : JsonValueListHead(JsonValueList *jvl)
2466 : {
2467 103020 : return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2468 : }
2469 :
2470 : static List *
2471 1416 : JsonValueListGetList(JsonValueList *jvl)
2472 : {
2473 1416 : if (jvl->singleton)
2474 834 : return list_make1(jvl->singleton);
2475 :
2476 582 : return jvl->list;
2477 : }
2478 :
2479 : static void
2480 187242 : JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2481 : {
2482 187242 : if (jvl->singleton)
2483 : {
2484 109782 : it->value = jvl->singleton;
2485 109782 : it->list = NIL;
2486 109782 : it->next = NULL;
2487 : }
2488 77460 : else if (jvl->list != NIL)
2489 : {
2490 618 : it->value = (JsonbValue *) linitial(jvl->list);
2491 618 : it->list = jvl->list;
2492 618 : it->next = list_second_cell(jvl->list);
2493 : }
2494 : else
2495 : {
2496 76842 : it->value = NULL;
2497 76842 : it->list = NIL;
2498 76842 : it->next = NULL;
2499 : }
2500 187242 : }
2501 :
2502 : /*
2503 : * Get the next item from the sequence advancing iterator.
2504 : */
2505 : static JsonbValue *
2506 288354 : JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2507 : {
2508 288354 : JsonbValue *result = it->value;
2509 :
2510 288354 : if (it->next)
2511 : {
2512 1068 : it->value = lfirst(it->next);
2513 1068 : it->next = lnext(it->list, it->next);
2514 : }
2515 : else
2516 : {
2517 287286 : it->value = NULL;
2518 : }
2519 :
2520 288354 : return result;
2521 : }
2522 :
2523 : /*
2524 : * Initialize a binary JsonbValue with the given jsonb container.
2525 : */
2526 : static JsonbValue *
2527 194802 : JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2528 : {
2529 194802 : jbv->type = jbvBinary;
2530 194802 : jbv->val.binary.data = &jb->root;
2531 194802 : jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2532 :
2533 194802 : return jbv;
2534 : }
2535 :
2536 : /*
2537 : * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
2538 : */
2539 : static int
2540 261342 : JsonbType(JsonbValue *jb)
2541 : {
2542 261342 : int type = jb->type;
2543 :
2544 261342 : if (jb->type == jbvBinary)
2545 : {
2546 179448 : JsonbContainer *jbc = (void *) jb->val.binary.data;
2547 :
2548 : /* Scalars should be always extracted during jsonpath execution. */
2549 : Assert(!JsonContainerIsScalar(jbc));
2550 :
2551 179448 : if (JsonContainerIsObject(jbc))
2552 177804 : type = jbvObject;
2553 1644 : else if (JsonContainerIsArray(jbc))
2554 1644 : type = jbvArray;
2555 : else
2556 0 : elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2557 : }
2558 :
2559 261342 : return type;
2560 : }
2561 :
2562 : /* Get scalar of given type or NULL on type mismatch */
2563 : static JsonbValue *
2564 5160 : getScalar(JsonbValue *scalar, enum jbvType type)
2565 : {
2566 : /* Scalars should be always extracted during jsonpath execution. */
2567 : Assert(scalar->type != jbvBinary ||
2568 : !JsonContainerIsScalar(scalar->val.binary.data));
2569 :
2570 5160 : return scalar->type == type ? scalar : NULL;
2571 : }
2572 :
2573 : /* Construct a JSON array from the item list */
2574 : static JsonbValue *
2575 42 : wrapItemsInArray(const JsonValueList *items)
2576 : {
2577 42 : JsonbParseState *ps = NULL;
2578 : JsonValueListIterator it;
2579 : JsonbValue *jbv;
2580 :
2581 42 : pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2582 :
2583 42 : JsonValueListInitIterator(items, &it);
2584 84 : while ((jbv = JsonValueListNext(items, &it)))
2585 42 : pushJsonbValue(&ps, WJB_ELEM, jbv);
2586 :
2587 42 : return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2588 : }
2589 :
2590 : /* Check if the timezone required for casting from type1 to type2 is used */
2591 : static void
2592 378 : checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
2593 : {
2594 378 : if (!useTz)
2595 90 : ereport(ERROR,
2596 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2597 : errmsg("cannot convert value from %s to %s without time zone usage",
2598 : type1, type2),
2599 : errhint("Use *_tz() function for time zone support.")));
2600 288 : }
2601 :
2602 : /* Convert time datum to timetz datum */
2603 : static Datum
2604 144 : castTimeToTimeTz(Datum time, bool useTz)
2605 : {
2606 144 : checkTimezoneIsUsedForCast(useTz, "time", "timetz");
2607 :
2608 108 : return DirectFunctionCall1(time_timetz, time);
2609 : }
2610 :
2611 : /*
2612 : * Compare date to timestamp.
2613 : * Note that this doesn't involve any timezone considerations.
2614 : */
2615 : static int
2616 114 : cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
2617 : {
2618 114 : return date_cmp_timestamp_internal(date1, ts2);
2619 : }
2620 :
2621 : /*
2622 : * Compare date to timestamptz.
2623 : */
2624 : static int
2625 90 : cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
2626 : {
2627 90 : checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
2628 :
2629 72 : return date_cmp_timestamptz_internal(date1, tstz2);
2630 : }
2631 :
2632 : /*
2633 : * Compare timestamp to timestamptz.
2634 : */
2635 : static int
2636 144 : cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
2637 : {
2638 144 : checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
2639 :
2640 108 : return timestamp_cmp_timestamptz_internal(ts1, tstz2);
2641 : }
2642 :
2643 : /*
2644 : * Cross-type comparison of two datetime SQL/JSON items. If items are
2645 : * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
2646 : * If the cast requires timezone and it is not used, then explicit error is thrown.
2647 : */
2648 : static int
2649 1428 : compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2650 : bool useTz, bool *cast_error)
2651 : {
2652 : PGFunction cmpfunc;
2653 :
2654 1428 : *cast_error = false;
2655 :
2656 1428 : switch (typid1)
2657 : {
2658 222 : case DATEOID:
2659 : switch (typid2)
2660 : {
2661 108 : case DATEOID:
2662 108 : cmpfunc = date_cmp;
2663 :
2664 108 : break;
2665 :
2666 42 : case TIMESTAMPOID:
2667 42 : return cmpDateToTimestamp(DatumGetDateADT(val1),
2668 : DatumGetTimestamp(val2),
2669 : useTz);
2670 :
2671 36 : case TIMESTAMPTZOID:
2672 36 : return cmpDateToTimestampTz(DatumGetDateADT(val1),
2673 : DatumGetTimestampTz(val2),
2674 : useTz);
2675 :
2676 36 : case TIMEOID:
2677 : case TIMETZOID:
2678 36 : *cast_error = true; /* uncomparable types */
2679 36 : return 0;
2680 :
2681 0 : default:
2682 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2683 : typid2);
2684 : }
2685 108 : break;
2686 :
2687 252 : case TIMEOID:
2688 : switch (typid2)
2689 : {
2690 108 : case TIMEOID:
2691 108 : cmpfunc = time_cmp;
2692 :
2693 108 : break;
2694 :
2695 72 : case TIMETZOID:
2696 72 : val1 = castTimeToTimeTz(val1, useTz);
2697 54 : cmpfunc = timetz_cmp;
2698 :
2699 54 : break;
2700 :
2701 72 : case DATEOID:
2702 : case TIMESTAMPOID:
2703 : case TIMESTAMPTZOID:
2704 72 : *cast_error = true; /* uncomparable types */
2705 72 : return 0;
2706 :
2707 0 : default:
2708 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2709 : typid2);
2710 : }
2711 162 : break;
2712 :
2713 324 : case TIMETZOID:
2714 : switch (typid2)
2715 : {
2716 72 : case TIMEOID:
2717 72 : val2 = castTimeToTimeTz(val2, useTz);
2718 54 : cmpfunc = timetz_cmp;
2719 :
2720 54 : break;
2721 :
2722 180 : case TIMETZOID:
2723 180 : cmpfunc = timetz_cmp;
2724 :
2725 180 : break;
2726 :
2727 72 : case DATEOID:
2728 : case TIMESTAMPOID:
2729 : case TIMESTAMPTZOID:
2730 72 : *cast_error = true; /* uncomparable types */
2731 72 : return 0;
2732 :
2733 0 : default:
2734 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2735 : typid2);
2736 : }
2737 234 : break;
2738 :
2739 288 : case TIMESTAMPOID:
2740 : switch (typid2)
2741 : {
2742 72 : case DATEOID:
2743 72 : return -cmpDateToTimestamp(DatumGetDateADT(val2),
2744 : DatumGetTimestamp(val1),
2745 : useTz);
2746 :
2747 108 : case TIMESTAMPOID:
2748 108 : cmpfunc = timestamp_cmp;
2749 :
2750 108 : break;
2751 :
2752 72 : case TIMESTAMPTZOID:
2753 72 : return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
2754 : DatumGetTimestampTz(val2),
2755 : useTz);
2756 :
2757 36 : case TIMEOID:
2758 : case TIMETZOID:
2759 36 : *cast_error = true; /* uncomparable types */
2760 36 : return 0;
2761 :
2762 0 : default:
2763 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2764 : typid2);
2765 : }
2766 108 : break;
2767 :
2768 342 : case TIMESTAMPTZOID:
2769 : switch (typid2)
2770 : {
2771 54 : case DATEOID:
2772 54 : return -cmpDateToTimestampTz(DatumGetDateADT(val2),
2773 : DatumGetTimestampTz(val1),
2774 : useTz);
2775 :
2776 72 : case TIMESTAMPOID:
2777 72 : return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
2778 : DatumGetTimestampTz(val1),
2779 : useTz);
2780 :
2781 180 : case TIMESTAMPTZOID:
2782 180 : cmpfunc = timestamp_cmp;
2783 :
2784 180 : break;
2785 :
2786 36 : case TIMEOID:
2787 : case TIMETZOID:
2788 36 : *cast_error = true; /* uncomparable types */
2789 36 : return 0;
2790 :
2791 0 : default:
2792 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2793 : typid2);
2794 : }
2795 180 : break;
2796 :
2797 0 : default:
2798 0 : elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
2799 : }
2800 :
2801 792 : if (*cast_error)
2802 0 : return 0; /* cast error */
2803 :
2804 792 : return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
2805 : }
|