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