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