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