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