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