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