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