PostgreSQL Source Code git master
jsonpath_exec.c
Go to the documentation of this file.
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 avoid 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-2025, PostgreSQL Global Development Group
53 *
54 * IDENTIFICATION
55 * src/backend/utils/adt/jsonpath_exec.c
56 *
57 *-------------------------------------------------------------------------
58 */
59
60#include "postgres.h"
61
63#include "catalog/pg_type.h"
64#include "funcapi.h"
65#include "miscadmin.h"
66#include "nodes/miscnodes.h"
67#include "nodes/nodeFuncs.h"
68#include "regex/regex.h"
69#include "utils/builtins.h"
70#include "utils/date.h"
71#include "utils/datetime.h"
72#include "utils/float.h"
73#include "utils/formatting.h"
74#include "utils/json.h"
75#include "utils/jsonpath.h"
76#include "utils/memutils.h"
77#include "utils/timestamp.h"
78
79/*
80 * Represents "base object" and it's "id" for .keyvalue() evaluation.
81 */
82typedef struct JsonBaseObjectInfo
83{
85 int id;
87
88/* Callbacks for executeJsonPath() */
89typedef JsonbValue *(*JsonPathGetVarCallback) (void *vars, char *varName, int varNameLen,
90 JsonbValue *baseObject, int *baseObjectId);
91typedef int (*JsonPathCountVarsCallback) (void *vars);
92
93/*
94 * Context of jsonpath execution.
95 */
96typedef struct JsonPathExecContext
97{
98 void *vars; /* variables to substitute into jsonpath */
99 JsonPathGetVarCallback getVar; /* callback to extract a given variable
100 * from 'vars' */
101 JsonbValue *root; /* for $ evaluation */
102 JsonbValue *current; /* for @ evaluation */
103 JsonBaseObjectInfo baseObject; /* "base object" for .keyvalue()
104 * evaluation */
105 int lastGeneratedObjectId; /* "id" counter for .keyvalue()
106 * evaluation */
107 int innermostArraySize; /* for LAST array index evaluation */
108 bool laxMode; /* true for "lax" mode, false for "strict"
109 * mode */
110 bool ignoreStructuralErrors; /* with "true" structural errors such
111 * as absence of required json item or
112 * unexpected json item type are
113 * ignored */
114 bool throwErrors; /* with "false" all suppressible errors are
115 * suppressed */
116 bool useTz;
118
119/* Context for LIKE_REGEX execution. */
121{
125
126/* Result of jsonpath predicate evaluation */
127typedef enum JsonPathBool
128{
131 jpbUnknown = 2
133
134/* Result of jsonpath expression evaluation */
136{
139 jperError = 2
141
142#define jperIsError(jper) ((jper) == jperError)
143
144/*
145 * List of jsonb values with shortcut for single-value list.
146 */
147typedef struct JsonValueList
148{
152
154{
159
160/* Structures for JSON_TABLE execution */
161
162/*
163 * Struct holding the result of jsonpath evaluation, to be used as source row
164 * for JsonTableGetValue() which in turn computes the values of individual
165 * JSON_TABLE columns.
166 */
168{
170 bool isnull;
172
173/*
174 * State of evaluation of row pattern derived by applying jsonpath given in
175 * a JsonTablePlan to an input document given in the parent TableFunc.
176 */
177typedef struct JsonTablePlanState
178{
179 /* Original plan */
181
182 /* The following fields are only valid for JsonTablePathScan plans */
183
184 /* jsonpath to evaluate against the input doc to get the row pattern */
186
187 /*
188 * Memory context to use when evaluating the row pattern from the jsonpath
189 */
191
192 /* PASSING arguments passed to jsonpath executor */
194
195 /* List and iterator of jsonpath result values */
198
199 /* Currently selected row for JsonTableGetValue() to use */
201
202 /* Counter for ORDINAL columns */
204
205 /* Nested plan, if any */
207
208 /* Left sibling, if any */
210
211 /* Right sibling, if any */
213
214 /* Parent plan, if this is a nested plan */
217
218/* Random number to identify JsonTableExecContext for sanity checking */
219#define JSON_TABLE_EXEC_CONTEXT_MAGIC 418352867
220
222{
223 int magic;
224
225 /* State of the plan providing a row evaluated from "root" jsonpath */
227
228 /*
229 * Per-column JsonTablePlanStates for all columns including the nested
230 * ones.
231 */
234
235/* strict/lax flags is decomposed into four [un]wrap/error flags */
236#define jspStrictAbsenceOfErrors(cxt) (!(cxt)->laxMode)
237#define jspAutoUnwrap(cxt) ((cxt)->laxMode)
238#define jspAutoWrap(cxt) ((cxt)->laxMode)
239#define jspIgnoreStructuralErrors(cxt) ((cxt)->ignoreStructuralErrors)
240#define jspThrowErrors(cxt) ((cxt)->throwErrors)
241
242/* Convenience macro: return or throw error depending on context */
243#define RETURN_ERROR(throw_error) \
244do { \
245 if (jspThrowErrors(cxt)) \
246 throw_error; \
247 else \
248 return jperError; \
249} while (0)
250
252 JsonbValue *larg,
253 JsonbValue *rarg,
254 void *param);
255typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
256
260 Jsonb *json, bool throwErrors,
261 JsonValueList *result, bool useTz);
263 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
265 JsonPathItem *jsp, JsonbValue *jb,
266 JsonValueList *found, bool unwrap);
268 JsonPathItem *jsp, JsonbValue *jb,
269 JsonValueList *found, bool unwrapElements);
272 JsonbValue *v, JsonValueList *found, bool copy);
274 bool unwrap, JsonValueList *found);
276 JsonbValue *jb, bool unwrap, JsonValueList *found);
278 JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
280 JsonPathItem *jsp, JsonbValue *jb);
282 JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
283 uint32 level, uint32 first, uint32 last,
284 bool ignoreStructuralErrors, bool unwrapNext);
286 JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
287 JsonbValue *jb, bool unwrapRightArg,
288 JsonPathPredicateCallback exec, void *param);
290 JsonPathItem *jsp, JsonbValue *jb,
291 BinaryArithmFunc func, JsonValueList *found);
293 JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
294 JsonValueList *found);
296 JsonbValue *whole, JsonbValue *initial, void *param);
298 JsonbValue *rarg, void *param);
300 JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
301 JsonValueList *found);
303 JsonbValue *jb, JsonValueList *found);
305 JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
308static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
310static JsonbValue *GetJsonPathVar(void *cxt, char *varName, int varNameLen,
311 JsonbValue *baseObject, int *baseObjectId);
312static int CountJsonPathVars(void *cxt);
313static void JsonItemFromDatum(Datum val, Oid typid, int32 typmod,
314 JsonbValue *res);
315static void JsonbValueInitNumericDatum(JsonbValue *jbv, Datum num);
318static int countVariablesFromJsonb(void *varsJsonb);
319static JsonbValue *getJsonPathVariableFromJsonb(void *varsJsonb, char *varName,
320 int varNameLength,
321 JsonbValue *baseObject,
322 int *baseObjectId);
323static int JsonbArraySize(JsonbValue *jb);
325 JsonbValue *rv, void *p);
327 bool useTz);
328static int compareNumeric(Numeric a, Numeric b);
331 JsonPathItem *jsp, JsonbValue *jb, int32 *index);
333 JsonbValue *jbv, int32 id);
334static void JsonValueListClear(JsonValueList *jvl);
335static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
336static int JsonValueListLength(const JsonValueList *jvl);
337static bool JsonValueListIsEmpty(JsonValueList *jvl);
340static void JsonValueListInitIterator(const JsonValueList *jvl,
344static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
345static int JsonbType(JsonbValue *jb);
346static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
348static int compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
349 bool useTz, bool *cast_error);
350static void checkTimezoneIsUsedForCast(bool useTz, const char *type1,
351 const char *type2);
352
353static void JsonTableInitOpaque(TableFuncScanState *state, int natts);
356 JsonTablePlanState *parentstate,
357 List *args,
358 MemoryContext mcxt);
360static void JsonTableResetRowPattern(JsonTablePlanState *planstate, Datum item);
363 Oid typid, int32 typmod, bool *isnull);
365static bool JsonTablePlanScanNextRow(JsonTablePlanState *planstate);
366static void JsonTableResetNestedPlan(JsonTablePlanState *planstate);
367static bool JsonTablePlanJoinNextRow(JsonTablePlanState *planstate);
368static bool JsonTablePlanNextRow(JsonTablePlanState *planstate);
369
371{
373 .SetDocument = JsonTableSetDocument,
374 .SetNamespace = NULL,
375 .SetRowFilter = NULL,
376 .SetColumnFilter = NULL,
377 .FetchRow = JsonTableFetchRow,
378 .GetValue = JsonTableGetValue,
379 .DestroyOpaque = JsonTableDestroyOpaque
380};
381
382/****************** User interface to JsonPath executor ********************/
383
384/*
385 * jsonb_path_exists
386 * Returns true if jsonpath returns at least one item for the specified
387 * jsonb value. This function and jsonb_path_match() are used to
388 * implement @? and @@ operators, which in turn are intended to have an
389 * index support. Thus, it's desirable to make it easier to achieve
390 * consistency between index scan results and sequential scan results.
391 * So, we throw as few errors as possible. Regarding this function,
392 * such behavior also matches behavior of JSON_EXISTS() clause of
393 * SQL/JSON. Regarding jsonb_path_match(), this function doesn't have
394 * an analogy in SQL/JSON, so we define its behavior on our own.
395 */
396static Datum
398{
399 Jsonb *jb = PG_GETARG_JSONB_P(0);
402 Jsonb *vars = NULL;
403 bool silent = true;
404
405 if (PG_NARGS() == 4)
406 {
408 silent = PG_GETARG_BOOL(3);
409 }
410
413 jb, !silent, NULL, tz);
414
415 PG_FREE_IF_COPY(jb, 0);
416 PG_FREE_IF_COPY(jp, 1);
417
418 if (jperIsError(res))
420
422}
423
424Datum
426{
427 return jsonb_path_exists_internal(fcinfo, false);
428}
429
430Datum
432{
433 return jsonb_path_exists_internal(fcinfo, true);
434}
435
436/*
437 * jsonb_path_exists_opr
438 * Implementation of operator "jsonb @? jsonpath" (2-argument version of
439 * jsonb_path_exists()).
440 */
441Datum
443{
444 /* just call the other one -- it can handle both cases */
445 return jsonb_path_exists_internal(fcinfo, false);
446}
447
448/*
449 * jsonb_path_match
450 * Returns jsonpath predicate result item for the specified jsonb value.
451 * See jsonb_path_exists() comment for details regarding error handling.
452 */
453static Datum
455{
456 Jsonb *jb = PG_GETARG_JSONB_P(0);
458 JsonValueList found = {0};
459 Jsonb *vars = NULL;
460 bool silent = true;
461
462 if (PG_NARGS() == 4)
463 {
465 silent = PG_GETARG_BOOL(3);
466 }
467
470 jb, !silent, &found, tz);
471
472 PG_FREE_IF_COPY(jb, 0);
473 PG_FREE_IF_COPY(jp, 1);
474
475 if (JsonValueListLength(&found) == 1)
476 {
477 JsonbValue *jbv = JsonValueListHead(&found);
478
479 if (jbv->type == jbvBool)
480 PG_RETURN_BOOL(jbv->val.boolean);
481
482 if (jbv->type == jbvNull)
484 }
485
486 if (!silent)
488 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
489 errmsg("single boolean result is expected")));
490
492}
493
494Datum
496{
497 return jsonb_path_match_internal(fcinfo, false);
498}
499
500Datum
502{
503 return jsonb_path_match_internal(fcinfo, true);
504}
505
506/*
507 * jsonb_path_match_opr
508 * Implementation of operator "jsonb @@ jsonpath" (2-argument version of
509 * jsonb_path_match()).
510 */
511Datum
513{
514 /* just call the other one -- it can handle both cases */
515 return jsonb_path_match_internal(fcinfo, false);
516}
517
518/*
519 * jsonb_path_query
520 * Executes jsonpath for given jsonb document and returns result as
521 * rowset.
522 */
523static Datum
525{
526 FuncCallContext *funcctx;
527 List *found;
528 JsonbValue *v;
529 ListCell *c;
530
531 if (SRF_IS_FIRSTCALL())
532 {
533 JsonPath *jp;
534 Jsonb *jb;
535 MemoryContext oldcontext;
536 Jsonb *vars;
537 bool silent;
538 JsonValueList found = {0};
539
540 funcctx = SRF_FIRSTCALL_INIT();
541 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
542
546 silent = PG_GETARG_BOOL(3);
547
550 jb, !silent, &found, tz);
551
552 funcctx->user_fctx = JsonValueListGetList(&found);
553
554 MemoryContextSwitchTo(oldcontext);
555 }
556
557 funcctx = SRF_PERCALL_SETUP();
558 found = funcctx->user_fctx;
559
560 c = list_head(found);
561
562 if (c == NULL)
563 SRF_RETURN_DONE(funcctx);
564
565 v = lfirst(c);
566 funcctx->user_fctx = list_delete_first(found);
567
569}
570
571Datum
573{
574 return jsonb_path_query_internal(fcinfo, false);
575}
576
577Datum
579{
580 return jsonb_path_query_internal(fcinfo, true);
581}
582
583/*
584 * jsonb_path_query_array
585 * Executes jsonpath for given jsonb document and returns result as
586 * jsonb array.
587 */
588static Datum
590{
591 Jsonb *jb = PG_GETARG_JSONB_P(0);
593 JsonValueList found = {0};
595 bool silent = PG_GETARG_BOOL(3);
596
599 jb, !silent, &found, tz);
600
602}
603
604Datum
606{
607 return jsonb_path_query_array_internal(fcinfo, false);
608}
609
610Datum
612{
613 return jsonb_path_query_array_internal(fcinfo, true);
614}
615
616/*
617 * jsonb_path_query_first
618 * Executes jsonpath for given jsonb document and returns first result
619 * item. If there are no items, NULL returned.
620 */
621static Datum
623{
624 Jsonb *jb = PG_GETARG_JSONB_P(0);
626 JsonValueList found = {0};
628 bool silent = PG_GETARG_BOOL(3);
629
632 jb, !silent, &found, tz);
633
634 if (JsonValueListLength(&found) >= 1)
636 else
638}
639
640Datum
642{
643 return jsonb_path_query_first_internal(fcinfo, false);
644}
645
646Datum
648{
649 return jsonb_path_query_first_internal(fcinfo, true);
650}
651
652/********************Execute functions for JsonPath**************************/
653
654/*
655 * Interface to jsonpath executor
656 *
657 * 'path' - jsonpath to be executed
658 * 'vars' - variables to be substituted to jsonpath
659 * 'getVar' - callback used by getJsonPathVariable() to extract variables from
660 * 'vars'
661 * 'countVars' - callback to count the number of jsonpath variables in 'vars'
662 * 'json' - target document for jsonpath evaluation
663 * 'throwErrors' - whether we should throw suppressible errors
664 * 'result' - list to store result items into
665 *
666 * Returns an error if a recoverable error happens during processing, or NULL
667 * on no error.
668 *
669 * Note, jsonb and jsonpath values should be available and untoasted during
670 * work because JsonPathItem, JsonbValue and result item could have pointers
671 * into input values. If caller needs to just check if document matches
672 * jsonpath, then it doesn't provide a result arg. In this case executor
673 * works till first positive result and does not check the rest if possible.
674 * In other case it tries to find all the satisfied result items.
675 */
679 Jsonb *json, bool throwErrors, JsonValueList *result,
680 bool useTz)
681{
684 JsonPathItem jsp;
685 JsonbValue jbv;
686
687 jspInit(&jsp, path);
688
689 if (!JsonbExtractScalar(&json->root, &jbv))
690 JsonbInitBinary(&jbv, json);
691
692 cxt.vars = vars;
693 cxt.getVar = getVar;
694 cxt.laxMode = (path->header & JSONPATH_LAX) != 0;
696 cxt.root = &jbv;
697 cxt.current = &jbv;
698 cxt.baseObject.jbc = NULL;
699 cxt.baseObject.id = 0;
700 /* 1 + number of base objects in vars */
701 cxt.lastGeneratedObjectId = 1 + countVars(vars);
702 cxt.innermostArraySize = -1;
703 cxt.throwErrors = throwErrors;
704 cxt.useTz = useTz;
705
706 if (jspStrictAbsenceOfErrors(&cxt) && !result)
707 {
708 /*
709 * In strict mode we must get a complete list of values to check that
710 * there are no errors at all.
711 */
712 JsonValueList vals = {0};
713
714 res = executeItem(&cxt, &jsp, &jbv, &vals);
715
716 if (jperIsError(res))
717 return res;
718
719 return JsonValueListIsEmpty(&vals) ? jperNotFound : jperOk;
720 }
721
722 res = executeItem(&cxt, &jsp, &jbv, result);
723
724 Assert(!throwErrors || !jperIsError(res));
725
726 return res;
727}
728
729/*
730 * Execute jsonpath with automatic unwrapping of current item in lax mode.
731 */
734 JsonbValue *jb, JsonValueList *found)
735{
736 return executeItemOptUnwrapTarget(cxt, jsp, jb, found, jspAutoUnwrap(cxt));
737}
738
739/*
740 * Main jsonpath executor function: walks on jsonpath structure, finds
741 * relevant parts of jsonb and evaluates expressions over them.
742 * When 'unwrap' is true current SQL/JSON item is unwrapped if it is an array.
743 */
746 JsonbValue *jb, JsonValueList *found, bool unwrap)
747{
748 JsonPathItem elem;
750 JsonBaseObjectInfo baseObject;
751
754
755 switch (jsp->type)
756 {
757 case jpiNull:
758 case jpiBool:
759 case jpiNumeric:
760 case jpiString:
761 case jpiVariable:
762 {
763 JsonbValue vbuf;
764 JsonbValue *v;
765 bool hasNext = jspGetNext(jsp, &elem);
766
767 if (!hasNext && !found && jsp->type != jpiVariable)
768 {
769 /*
770 * Skip evaluation, but not for variables. We must
771 * trigger an error for the missing variable.
772 */
773 res = jperOk;
774 break;
775 }
776
777 v = hasNext ? &vbuf : palloc(sizeof(*v));
778
779 baseObject = cxt->baseObject;
780 getJsonPathItem(cxt, jsp, v);
781
782 res = executeNextItem(cxt, jsp, &elem,
783 v, found, hasNext);
784 cxt->baseObject = baseObject;
785 }
786 break;
787
788 /* all boolean item types: */
789 case jpiAnd:
790 case jpiOr:
791 case jpiNot:
792 case jpiIsUnknown:
793 case jpiEqual:
794 case jpiNotEqual:
795 case jpiLess:
796 case jpiGreater:
797 case jpiLessOrEqual:
799 case jpiExists:
800 case jpiStartsWith:
801 case jpiLikeRegex:
802 {
803 JsonPathBool st = executeBoolItem(cxt, jsp, jb, true);
804
805 res = appendBoolResult(cxt, jsp, found, st);
806 break;
807 }
808
809 case jpiAdd:
810 return executeBinaryArithmExpr(cxt, jsp, jb,
811 numeric_add_opt_error, found);
812
813 case jpiSub:
814 return executeBinaryArithmExpr(cxt, jsp, jb,
815 numeric_sub_opt_error, found);
816
817 case jpiMul:
818 return executeBinaryArithmExpr(cxt, jsp, jb,
819 numeric_mul_opt_error, found);
820
821 case jpiDiv:
822 return executeBinaryArithmExpr(cxt, jsp, jb,
823 numeric_div_opt_error, found);
824
825 case jpiMod:
826 return executeBinaryArithmExpr(cxt, jsp, jb,
827 numeric_mod_opt_error, found);
828
829 case jpiPlus:
830 return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
831
832 case jpiMinus:
833 return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
834 found);
835
836 case jpiAnyArray:
837 if (JsonbType(jb) == jbvArray)
838 {
839 bool hasNext = jspGetNext(jsp, &elem);
840
841 res = executeItemUnwrapTargetArray(cxt, hasNext ? &elem : NULL,
842 jb, found, jspAutoUnwrap(cxt));
843 }
844 else if (jspAutoWrap(cxt))
845 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
846 else if (!jspIgnoreStructuralErrors(cxt))
848 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
849 errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
850 break;
851
852 case jpiAnyKey:
853 if (JsonbType(jb) == jbvObject)
854 {
855 bool hasNext = jspGetNext(jsp, &elem);
856
857 if (jb->type != jbvBinary)
858 elog(ERROR, "invalid jsonb object type: %d", jb->type);
859
860 return executeAnyItem
861 (cxt, hasNext ? &elem : NULL,
862 jb->val.binary.data, found, 1, 1, 1,
863 false, jspAutoUnwrap(cxt));
864 }
865 else if (unwrap && JsonbType(jb) == jbvArray)
866 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
867 else if (!jspIgnoreStructuralErrors(cxt))
868 {
869 Assert(found);
871 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
872 errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
873 }
874 break;
875
876 case jpiIndexArray:
877 if (JsonbType(jb) == jbvArray || jspAutoWrap(cxt))
878 {
879 int innermostArraySize = cxt->innermostArraySize;
880 int i;
881 int size = JsonbArraySize(jb);
882 bool singleton = size < 0;
883 bool hasNext = jspGetNext(jsp, &elem);
884
885 if (singleton)
886 size = 1;
887
888 cxt->innermostArraySize = size; /* for LAST evaluation */
889
890 for (i = 0; i < jsp->content.array.nelems; i++)
891 {
892 JsonPathItem from;
893 JsonPathItem to;
894 int32 index;
895 int32 index_from;
896 int32 index_to;
897 bool range = jspGetArraySubscript(jsp, &from,
898 &to, i);
899
900 res = getArrayIndex(cxt, &from, jb, &index_from);
901
902 if (jperIsError(res))
903 break;
904
905 if (range)
906 {
907 res = getArrayIndex(cxt, &to, jb, &index_to);
908
909 if (jperIsError(res))
910 break;
911 }
912 else
913 index_to = index_from;
914
915 if (!jspIgnoreStructuralErrors(cxt) &&
916 (index_from < 0 ||
917 index_from > index_to ||
918 index_to >= size))
920 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
921 errmsg("jsonpath array subscript is out of bounds"))));
922
923 if (index_from < 0)
924 index_from = 0;
925
926 if (index_to >= size)
927 index_to = size - 1;
928
930
931 for (index = index_from; index <= index_to; index++)
932 {
933 JsonbValue *v;
934 bool copy;
935
936 if (singleton)
937 {
938 v = jb;
939 copy = true;
940 }
941 else
942 {
943 v = getIthJsonbValueFromContainer(jb->val.binary.data,
944 (uint32) index);
945
946 if (v == NULL)
947 continue;
948
949 copy = false;
950 }
951
952 if (!hasNext && !found)
953 return jperOk;
954
955 res = executeNextItem(cxt, jsp, &elem, v, found,
956 copy);
957
958 if (jperIsError(res))
959 break;
960
961 if (res == jperOk && !found)
962 break;
963 }
964
965 if (jperIsError(res))
966 break;
967
968 if (res == jperOk && !found)
969 break;
970 }
971
972 cxt->innermostArraySize = innermostArraySize;
973 }
974 else if (!jspIgnoreStructuralErrors(cxt))
975 {
977 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
978 errmsg("jsonpath array accessor can only be applied to an array"))));
979 }
980 break;
981
982 case jpiAny:
983 {
984 bool hasNext = jspGetNext(jsp, &elem);
985
986 /* first try without any intermediate steps */
987 if (jsp->content.anybounds.first == 0)
988 {
989 bool savedIgnoreStructuralErrors;
990
991 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
992 cxt->ignoreStructuralErrors = true;
993 res = executeNextItem(cxt, jsp, &elem,
994 jb, found, true);
995 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
996
997 if (res == jperOk && !found)
998 break;
999 }
1000
1001 if (jb->type == jbvBinary)
1003 (cxt, hasNext ? &elem : NULL,
1004 jb->val.binary.data, found,
1005 1,
1006 jsp->content.anybounds.first,
1007 jsp->content.anybounds.last,
1008 true, jspAutoUnwrap(cxt));
1009 break;
1010 }
1011
1012 case jpiKey:
1013 if (JsonbType(jb) == jbvObject)
1014 {
1015 JsonbValue *v;
1017
1018 key.type = jbvString;
1019 key.val.string.val = jspGetString(jsp, &key.val.string.len);
1020
1021 v = findJsonbValueFromContainer(jb->val.binary.data,
1022 JB_FOBJECT, &key);
1023
1024 if (v != NULL)
1025 {
1026 res = executeNextItem(cxt, jsp, NULL,
1027 v, found, false);
1028
1029 /* free value if it was not added to found list */
1030 if (jspHasNext(jsp) || !found)
1031 pfree(v);
1032 }
1033 else if (!jspIgnoreStructuralErrors(cxt))
1034 {
1035 Assert(found);
1036
1037 if (!jspThrowErrors(cxt))
1038 return jperError;
1039
1040 ereport(ERROR,
1041 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
1042 errmsg("JSON object does not contain key \"%s\"",
1043 pnstrdup(key.val.string.val,
1044 key.val.string.len))));
1045 }
1046 }
1047 else if (unwrap && JsonbType(jb) == jbvArray)
1048 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1049 else if (!jspIgnoreStructuralErrors(cxt))
1050 {
1051 Assert(found);
1053 (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
1054 errmsg("jsonpath member accessor can only be applied to an object"))));
1055 }
1056 break;
1057
1058 case jpiCurrent:
1059 res = executeNextItem(cxt, jsp, NULL, cxt->current,
1060 found, true);
1061 break;
1062
1063 case jpiRoot:
1064 jb = cxt->root;
1065 baseObject = setBaseObject(cxt, jb, 0);
1066 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1067 cxt->baseObject = baseObject;
1068 break;
1069
1070 case jpiFilter:
1071 {
1072 JsonPathBool st;
1073
1074 if (unwrap && JsonbType(jb) == jbvArray)
1075 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1076 false);
1077
1078 jspGetArg(jsp, &elem);
1079 st = executeNestedBoolItem(cxt, &elem, jb);
1080 if (st != jpbTrue)
1081 res = jperNotFound;
1082 else
1083 res = executeNextItem(cxt, jsp, NULL,
1084 jb, found, true);
1085 break;
1086 }
1087
1088 case jpiType:
1089 {
1090 JsonbValue *jbv = palloc(sizeof(*jbv));
1091
1092 jbv->type = jbvString;
1093 jbv->val.string.val = pstrdup(JsonbTypeName(jb));
1094 jbv->val.string.len = strlen(jbv->val.string.val);
1095
1096 res = executeNextItem(cxt, jsp, NULL, jbv,
1097 found, false);
1098 }
1099 break;
1100
1101 case jpiSize:
1102 {
1103 int size = JsonbArraySize(jb);
1104
1105 if (size < 0)
1106 {
1107 if (!jspAutoWrap(cxt))
1108 {
1109 if (!jspIgnoreStructuralErrors(cxt))
1111 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1112 errmsg("jsonpath item method .%s() can only be applied to an array",
1113 jspOperationName(jsp->type)))));
1114 break;
1115 }
1116
1117 size = 1;
1118 }
1119
1120 jb = palloc(sizeof(*jb));
1121
1122 jb->type = jbvNumeric;
1123 jb->val.numeric = int64_to_numeric(size);
1124
1125 res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1126 }
1127 break;
1128
1129 case jpiAbs:
1130 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1131 found);
1132
1133 case jpiFloor:
1134 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1135 found);
1136
1137 case jpiCeiling:
1138 return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1139 found);
1140
1141 case jpiDouble:
1142 {
1143 JsonbValue jbv;
1144
1145 if (unwrap && JsonbType(jb) == jbvArray)
1146 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1147 false);
1148
1149 if (jb->type == jbvNumeric)
1150 {
1152 NumericGetDatum(jb->val.numeric)));
1153 double val;
1154 ErrorSaveContext escontext = {T_ErrorSaveContext};
1155
1156 val = float8in_internal(tmp,
1157 NULL,
1158 "double precision",
1159 tmp,
1160 (Node *) &escontext);
1161
1162 if (escontext.error_occurred)
1164 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1165 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1166 tmp, jspOperationName(jsp->type), "double precision"))));
1167 if (isinf(val) || isnan(val))
1169 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1170 errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1171 jspOperationName(jsp->type)))));
1172 res = jperOk;
1173 }
1174 else if (jb->type == jbvString)
1175 {
1176 /* cast string as double */
1177 double val;
1178 char *tmp = pnstrdup(jb->val.string.val,
1179 jb->val.string.len);
1180 ErrorSaveContext escontext = {T_ErrorSaveContext};
1181
1182 val = float8in_internal(tmp,
1183 NULL,
1184 "double precision",
1185 tmp,
1186 (Node *) &escontext);
1187
1188 if (escontext.error_occurred)
1190 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1191 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1192 tmp, jspOperationName(jsp->type), "double precision"))));
1193 if (isinf(val) || isnan(val))
1195 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1196 errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1197 jspOperationName(jsp->type)))));
1198
1199 jb = &jbv;
1200 jb->type = jbvNumeric;
1203 res = jperOk;
1204 }
1205
1206 if (res == jperNotFound)
1208 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1209 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1210 jspOperationName(jsp->type)))));
1211
1212 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1213 }
1214 break;
1215
1216 case jpiDatetime:
1217 case jpiDate:
1218 case jpiTime:
1219 case jpiTimeTz:
1220 case jpiTimestamp:
1221 case jpiTimestampTz:
1222 if (unwrap && JsonbType(jb) == jbvArray)
1223 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1224
1225 return executeDateTimeMethod(cxt, jsp, jb, found);
1226
1227 case jpiKeyValue:
1228 if (unwrap && JsonbType(jb) == jbvArray)
1229 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1230
1231 return executeKeyValueMethod(cxt, jsp, jb, found);
1232
1233 case jpiLast:
1234 {
1235 JsonbValue tmpjbv;
1236 JsonbValue *lastjbv;
1237 int last;
1238 bool hasNext = jspGetNext(jsp, &elem);
1239
1240 if (cxt->innermostArraySize < 0)
1241 elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
1242
1243 if (!hasNext && !found)
1244 {
1245 res = jperOk;
1246 break;
1247 }
1248
1249 last = cxt->innermostArraySize - 1;
1250
1251 lastjbv = hasNext ? &tmpjbv : palloc(sizeof(*lastjbv));
1252
1253 lastjbv->type = jbvNumeric;
1254 lastjbv->val.numeric = int64_to_numeric(last);
1255
1256 res = executeNextItem(cxt, jsp, &elem,
1257 lastjbv, found, hasNext);
1258 }
1259 break;
1260
1261 case jpiBigint:
1262 {
1263 JsonbValue jbv;
1264 Datum datum;
1265
1266 if (unwrap && JsonbType(jb) == jbvArray)
1267 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1268 false);
1269
1270 if (jb->type == jbvNumeric)
1271 {
1272 bool have_error;
1273 int64 val;
1274
1275 val = numeric_int8_opt_error(jb->val.numeric, &have_error);
1276 if (have_error)
1278 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1279 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1281 NumericGetDatum(jb->val.numeric))),
1282 jspOperationName(jsp->type),
1283 "bigint"))));
1284
1285 datum = Int64GetDatum(val);
1286 res = jperOk;
1287 }
1288 else if (jb->type == jbvString)
1289 {
1290 /* cast string as bigint */
1291 char *tmp = pnstrdup(jb->val.string.val,
1292 jb->val.string.len);
1293 ErrorSaveContext escontext = {T_ErrorSaveContext};
1294 bool noerr;
1295
1297 InvalidOid, -1,
1298 (Node *) &escontext,
1299 &datum);
1300
1301 if (!noerr || escontext.error_occurred)
1303 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1304 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1305 tmp, jspOperationName(jsp->type), "bigint"))));
1306 res = jperOk;
1307 }
1308
1309 if (res == jperNotFound)
1311 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1312 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1313 jspOperationName(jsp->type)))));
1314
1315 jb = &jbv;
1316 jb->type = jbvNumeric;
1318 datum));
1319
1320 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1321 }
1322 break;
1323
1324 case jpiBoolean:
1325 {
1326 JsonbValue jbv;
1327 bool bval;
1328
1329 if (unwrap && JsonbType(jb) == jbvArray)
1330 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1331 false);
1332
1333 if (jb->type == jbvBool)
1334 {
1335 bval = jb->val.boolean;
1336
1337 res = jperOk;
1338 }
1339 else if (jb->type == jbvNumeric)
1340 {
1341 int ival;
1342 Datum datum;
1343 bool noerr;
1345 NumericGetDatum(jb->val.numeric)));
1346 ErrorSaveContext escontext = {T_ErrorSaveContext};
1347
1349 InvalidOid, -1,
1350 (Node *) &escontext,
1351 &datum);
1352
1353 if (!noerr || escontext.error_occurred)
1355 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1356 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1357 tmp, jspOperationName(jsp->type), "boolean"))));
1358
1359 ival = DatumGetInt32(datum);
1360 if (ival == 0)
1361 bval = false;
1362 else
1363 bval = true;
1364
1365 res = jperOk;
1366 }
1367 else if (jb->type == jbvString)
1368 {
1369 /* cast string as boolean */
1370 char *tmp = pnstrdup(jb->val.string.val,
1371 jb->val.string.len);
1372
1373 if (!parse_bool(tmp, &bval))
1375 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1376 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1377 tmp, jspOperationName(jsp->type), "boolean"))));
1378
1379 res = jperOk;
1380 }
1381
1382 if (res == jperNotFound)
1384 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1385 errmsg("jsonpath item method .%s() can only be applied to a boolean, string, or numeric value",
1386 jspOperationName(jsp->type)))));
1387
1388 jb = &jbv;
1389 jb->type = jbvBool;
1390 jb->val.boolean = bval;
1391
1392 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1393 }
1394 break;
1395
1396 case jpiDecimal:
1397 case jpiNumber:
1398 {
1399 JsonbValue jbv;
1400 Numeric num;
1401 char *numstr = NULL;
1402
1403 if (unwrap && JsonbType(jb) == jbvArray)
1404 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1405 false);
1406
1407 if (jb->type == jbvNumeric)
1408 {
1409 num = jb->val.numeric;
1410 if (numeric_is_nan(num) || numeric_is_inf(num))
1412 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1413 errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1414 jspOperationName(jsp->type)))));
1415
1416 if (jsp->type == jpiDecimal)
1418 NumericGetDatum(num)));
1419 res = jperOk;
1420 }
1421 else if (jb->type == jbvString)
1422 {
1423 /* cast string as number */
1424 Datum datum;
1425 bool noerr;
1426 ErrorSaveContext escontext = {T_ErrorSaveContext};
1427
1428 numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
1429
1431 InvalidOid, -1,
1432 (Node *) &escontext,
1433 &datum);
1434
1435 if (!noerr || escontext.error_occurred)
1437 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1438 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1439 numstr, jspOperationName(jsp->type), "numeric"))));
1440
1441 num = DatumGetNumeric(datum);
1442 if (numeric_is_nan(num) || numeric_is_inf(num))
1444 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1445 errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
1446 jspOperationName(jsp->type)))));
1447
1448 res = jperOk;
1449 }
1450
1451 if (res == jperNotFound)
1453 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1454 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1455 jspOperationName(jsp->type)))));
1456
1457 /*
1458 * If we have arguments, then they must be the precision and
1459 * optional scale used in .decimal(). Convert them to the
1460 * typmod equivalent and then truncate the numeric value per
1461 * this typmod details.
1462 */
1463 if (jsp->type == jpiDecimal && jsp->content.args.left)
1464 {
1465 Datum numdatum;
1466 Datum dtypmod;
1467 int32 precision;
1468 int32 scale = 0;
1469 bool have_error;
1470 bool noerr;
1471 ArrayType *arrtypmod;
1472 Datum datums[2];
1473 char pstr[12]; /* sign, 10 digits and '\0' */
1474 char sstr[12]; /* sign, 10 digits and '\0' */
1475 ErrorSaveContext escontext = {T_ErrorSaveContext};
1476
1477 jspGetLeftArg(jsp, &elem);
1478 if (elem.type != jpiNumeric)
1479 elog(ERROR, "invalid jsonpath item type for .decimal() precision");
1480
1481 precision = numeric_int4_opt_error(jspGetNumeric(&elem),
1482 &have_error);
1483 if (have_error)
1485 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1486 errmsg("precision of jsonpath item method .%s() is out of range for type integer",
1487 jspOperationName(jsp->type)))));
1488
1489 if (jsp->content.args.right)
1490 {
1491 jspGetRightArg(jsp, &elem);
1492 if (elem.type != jpiNumeric)
1493 elog(ERROR, "invalid jsonpath item type for .decimal() scale");
1494
1496 &have_error);
1497 if (have_error)
1499 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1500 errmsg("scale of jsonpath item method .%s() is out of range for type integer",
1501 jspOperationName(jsp->type)))));
1502 }
1503
1504 /*
1505 * numerictypmodin() takes the precision and scale in the
1506 * form of CString arrays.
1507 */
1508 pg_ltoa(precision, pstr);
1509 datums[0] = CStringGetDatum(pstr);
1510 pg_ltoa(scale, sstr);
1511 datums[1] = CStringGetDatum(sstr);
1512 arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
1513
1515 PointerGetDatum(arrtypmod));
1516
1517 /* Convert numstr to Numeric with typmod */
1518 Assert(numstr != NULL);
1520 InvalidOid, dtypmod,
1521 (Node *) &escontext,
1522 &numdatum);
1523
1524 if (!noerr || escontext.error_occurred)
1526 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1527 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1528 numstr, jspOperationName(jsp->type), "numeric"))));
1529
1530 num = DatumGetNumeric(numdatum);
1531 pfree(arrtypmod);
1532 }
1533
1534 jb = &jbv;
1535 jb->type = jbvNumeric;
1536 jb->val.numeric = num;
1537
1538 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1539 }
1540 break;
1541
1542 case jpiInteger:
1543 {
1544 JsonbValue jbv;
1545 Datum datum;
1546
1547 if (unwrap && JsonbType(jb) == jbvArray)
1548 return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1549 false);
1550
1551 if (jb->type == jbvNumeric)
1552 {
1553 bool have_error;
1554 int32 val;
1555
1556 val = numeric_int4_opt_error(jb->val.numeric, &have_error);
1557 if (have_error)
1559 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1560 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1562 NumericGetDatum(jb->val.numeric))),
1563 jspOperationName(jsp->type), "integer"))));
1564
1565 datum = Int32GetDatum(val);
1566 res = jperOk;
1567 }
1568 else if (jb->type == jbvString)
1569 {
1570 /* cast string as integer */
1571 char *tmp = pnstrdup(jb->val.string.val,
1572 jb->val.string.len);
1573 ErrorSaveContext escontext = {T_ErrorSaveContext};
1574 bool noerr;
1575
1577 InvalidOid, -1,
1578 (Node *) &escontext,
1579 &datum);
1580
1581 if (!noerr || escontext.error_occurred)
1583 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1584 errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type %s",
1585 tmp, jspOperationName(jsp->type), "integer"))));
1586 res = jperOk;
1587 }
1588
1589 if (res == jperNotFound)
1591 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1592 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1593 jspOperationName(jsp->type)))));
1594
1595 jb = &jbv;
1596 jb->type = jbvNumeric;
1598 datum));
1599
1600 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1601 }
1602 break;
1603
1604 case jpiStringFunc:
1605 {
1606 JsonbValue jbv;
1607 char *tmp = NULL;
1608
1609 if (unwrap && JsonbType(jb) == jbvArray)
1610 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1611
1612 switch (JsonbType(jb))
1613 {
1614 case jbvString:
1615
1616 /*
1617 * Value is not necessarily null-terminated, so we do
1618 * pnstrdup() here.
1619 */
1620 tmp = pnstrdup(jb->val.string.val,
1621 jb->val.string.len);
1622 break;
1623 case jbvNumeric:
1625 NumericGetDatum(jb->val.numeric)));
1626 break;
1627 case jbvBool:
1628 tmp = (jb->val.boolean) ? "true" : "false";
1629 break;
1630 case jbvDatetime:
1631 {
1632 char buf[MAXDATELEN + 1];
1633
1635 jb->val.datetime.value,
1636 jb->val.datetime.typid,
1637 &jb->val.datetime.tz);
1638 tmp = pstrdup(buf);
1639 }
1640 break;
1641 case jbvNull:
1642 case jbvArray:
1643 case jbvObject:
1644 case jbvBinary:
1646 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1647 errmsg("jsonpath item method .%s() can only be applied to a boolean, string, numeric, or datetime value",
1648 jspOperationName(jsp->type)))));
1649 break;
1650 }
1651
1652 jb = &jbv;
1653 Assert(tmp != NULL); /* We must have set tmp above */
1654 jb->val.string.val = tmp;
1655 jb->val.string.len = strlen(jb->val.string.val);
1656 jb->type = jbvString;
1657
1658 res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1659 }
1660 break;
1661
1662 default:
1663 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1664 }
1665
1666 return res;
1667}
1668
1669/*
1670 * Unwrap current array item and execute jsonpath for each of its elements.
1671 */
1672static JsonPathExecResult
1674 JsonbValue *jb, JsonValueList *found,
1675 bool unwrapElements)
1676{
1677 if (jb->type != jbvBinary)
1678 {
1679 Assert(jb->type != jbvArray);
1680 elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1681 }
1682
1683 return executeAnyItem
1684 (cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1685 false, unwrapElements);
1686}
1687
1688/*
1689 * Execute next jsonpath item if exists. Otherwise put "v" to the "found"
1690 * list if provided.
1691 */
1692static JsonPathExecResult
1695 JsonbValue *v, JsonValueList *found, bool copy)
1696{
1697 JsonPathItem elem;
1698 bool hasNext;
1699
1700 if (!cur)
1701 hasNext = next != NULL;
1702 else if (next)
1703 hasNext = jspHasNext(cur);
1704 else
1705 {
1706 next = &elem;
1707 hasNext = jspGetNext(cur, next);
1708 }
1709
1710 if (hasNext)
1711 return executeItem(cxt, next, v, found);
1712
1713 if (found)
1714 JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1715
1716 return jperOk;
1717}
1718
1719/*
1720 * Same as executeItem(), but when "unwrap == true" automatically unwraps
1721 * each array item from the resulting sequence in lax mode.
1722 */
1723static JsonPathExecResult
1725 JsonbValue *jb, bool unwrap,
1726 JsonValueList *found)
1727{
1728 if (unwrap && jspAutoUnwrap(cxt))
1729 {
1730 JsonValueList seq = {0};
1732 JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1733 JsonbValue *item;
1734
1735 if (jperIsError(res))
1736 return res;
1737
1738 JsonValueListInitIterator(&seq, &it);
1739 while ((item = JsonValueListNext(&seq, &it)))
1740 {
1741 Assert(item->type != jbvArray);
1742
1743 if (JsonbType(item) == jbvArray)
1744 executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1745 else
1746 JsonValueListAppend(found, item);
1747 }
1748
1749 return jperOk;
1750 }
1751
1752 return executeItem(cxt, jsp, jb, found);
1753}
1754
1755/*
1756 * Same as executeItemOptUnwrapResult(), but with error suppression.
1757 */
1758static JsonPathExecResult
1760 JsonPathItem *jsp,
1761 JsonbValue *jb, bool unwrap,
1762 JsonValueList *found)
1763{
1765 bool throwErrors = cxt->throwErrors;
1766
1767 cxt->throwErrors = false;
1768 res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1769 cxt->throwErrors = throwErrors;
1770
1771 return res;
1772}
1773
1774/* Execute boolean-valued jsonpath expression. */
1775static JsonPathBool
1777 JsonbValue *jb, bool canHaveNext)
1778{
1779 JsonPathItem larg;
1780 JsonPathItem rarg;
1782 JsonPathBool res2;
1783
1784 /* since this function recurses, it could be driven to stack overflow */
1786
1787 if (!canHaveNext && jspHasNext(jsp))
1788 elog(ERROR, "boolean jsonpath item cannot have next item");
1789
1790 switch (jsp->type)
1791 {
1792 case jpiAnd:
1793 jspGetLeftArg(jsp, &larg);
1794 res = executeBoolItem(cxt, &larg, jb, false);
1795
1796 if (res == jpbFalse)
1797 return jpbFalse;
1798
1799 /*
1800 * SQL/JSON says that we should check second arg in case of
1801 * jperError
1802 */
1803
1804 jspGetRightArg(jsp, &rarg);
1805 res2 = executeBoolItem(cxt, &rarg, jb, false);
1806
1807 return res2 == jpbTrue ? res : res2;
1808
1809 case jpiOr:
1810 jspGetLeftArg(jsp, &larg);
1811 res = executeBoolItem(cxt, &larg, jb, false);
1812
1813 if (res == jpbTrue)
1814 return jpbTrue;
1815
1816 jspGetRightArg(jsp, &rarg);
1817 res2 = executeBoolItem(cxt, &rarg, jb, false);
1818
1819 return res2 == jpbFalse ? res : res2;
1820
1821 case jpiNot:
1822 jspGetArg(jsp, &larg);
1823
1824 res = executeBoolItem(cxt, &larg, jb, false);
1825
1826 if (res == jpbUnknown)
1827 return jpbUnknown;
1828
1829 return res == jpbTrue ? jpbFalse : jpbTrue;
1830
1831 case jpiIsUnknown:
1832 jspGetArg(jsp, &larg);
1833 res = executeBoolItem(cxt, &larg, jb, false);
1834 return res == jpbUnknown ? jpbTrue : jpbFalse;
1835
1836 case jpiEqual:
1837 case jpiNotEqual:
1838 case jpiLess:
1839 case jpiGreater:
1840 case jpiLessOrEqual:
1841 case jpiGreaterOrEqual:
1842 jspGetLeftArg(jsp, &larg);
1843 jspGetRightArg(jsp, &rarg);
1844 return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1845 executeComparison, cxt);
1846
1847 case jpiStartsWith: /* 'whole STARTS WITH initial' */
1848 jspGetLeftArg(jsp, &larg); /* 'whole' */
1849 jspGetRightArg(jsp, &rarg); /* 'initial' */
1850 return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1851 executeStartsWith, NULL);
1852
1853 case jpiLikeRegex: /* 'expr LIKE_REGEX pattern FLAGS flags' */
1854 {
1855 /*
1856 * 'expr' is a sequence-returning expression. 'pattern' is a
1857 * regex string literal. SQL/JSON standard requires XQuery
1858 * regexes, but we use Postgres regexes here. 'flags' is a
1859 * string literal converted to integer flags at compile-time.
1860 */
1861 JsonLikeRegexContext lrcxt = {0};
1862
1863 jspInitByBuffer(&larg, jsp->base,
1864 jsp->content.like_regex.expr);
1865
1866 return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1867 executeLikeRegex, &lrcxt);
1868 }
1869
1870 case jpiExists:
1871 jspGetArg(jsp, &larg);
1872
1873 if (jspStrictAbsenceOfErrors(cxt))
1874 {
1875 /*
1876 * In strict mode we must get a complete list of values to
1877 * check that there are no errors at all.
1878 */
1879 JsonValueList vals = {0};
1881 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1882 false, &vals);
1883
1884 if (jperIsError(res))
1885 return jpbUnknown;
1886
1887 return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1888 }
1889 else
1890 {
1892 executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1893 false, NULL);
1894
1895 if (jperIsError(res))
1896 return jpbUnknown;
1897
1898 return res == jperOk ? jpbTrue : jpbFalse;
1899 }
1900
1901 default:
1902 elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1903 return jpbUnknown;
1904 }
1905}
1906
1907/*
1908 * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1909 * item onto the stack.
1910 */
1911static JsonPathBool
1913 JsonbValue *jb)
1914{
1915 JsonbValue *prev;
1917
1918 prev = cxt->current;
1919 cxt->current = jb;
1920 res = executeBoolItem(cxt, jsp, jb, false);
1921 cxt->current = prev;
1922
1923 return res;
1924}
1925
1926/*
1927 * Implementation of several jsonpath nodes:
1928 * - jpiAny (.** accessor),
1929 * - jpiAnyKey (.* accessor),
1930 * - jpiAnyArray ([*] accessor)
1931 */
1932static JsonPathExecResult
1934 JsonValueList *found, uint32 level, uint32 first, uint32 last,
1935 bool ignoreStructuralErrors, bool unwrapNext)
1936{
1938 JsonbIterator *it;
1939 int32 r;
1940 JsonbValue v;
1941
1943
1944 if (level > last)
1945 return res;
1946
1947 it = JsonbIteratorInit(jbc);
1948
1949 /*
1950 * Recursively iterate over jsonb objects/arrays
1951 */
1952 while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1953 {
1954 if (r == WJB_KEY)
1955 {
1956 r = JsonbIteratorNext(&it, &v, true);
1957 Assert(r == WJB_VALUE);
1958 }
1959
1960 if (r == WJB_VALUE || r == WJB_ELEM)
1961 {
1962
1963 if (level >= first ||
1964 (first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1965 v.type != jbvBinary)) /* leaves only requested */
1966 {
1967 /* check expression */
1968 if (jsp)
1969 {
1970 if (ignoreStructuralErrors)
1971 {
1972 bool savedIgnoreStructuralErrors;
1973
1974 savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1975 cxt->ignoreStructuralErrors = true;
1976 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1977 cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1978 }
1979 else
1980 res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1981
1982 if (jperIsError(res))
1983 break;
1984
1985 if (res == jperOk && !found)
1986 break;
1987 }
1988 else if (found)
1990 else
1991 return jperOk;
1992 }
1993
1994 if (level < last && v.type == jbvBinary)
1995 {
1997 (cxt, jsp, v.val.binary.data, found,
1998 level + 1, first, last,
1999 ignoreStructuralErrors, unwrapNext);
2000
2001 if (jperIsError(res))
2002 break;
2003
2004 if (res == jperOk && found == NULL)
2005 break;
2006 }
2007 }
2008 }
2009
2010 return res;
2011}
2012
2013/*
2014 * Execute unary or binary predicate.
2015 *
2016 * Predicates have existence semantics, because their operands are item
2017 * sequences. Pairs of items from the left and right operand's sequences are
2018 * checked. TRUE returned only if any pair satisfying the condition is found.
2019 * In strict mode, even if the desired pair has already been found, all pairs
2020 * still need to be examined to check the absence of errors. If any error
2021 * occurs, UNKNOWN (analogous to SQL NULL) is returned.
2022 */
2023static JsonPathBool
2025 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
2026 bool unwrapRightArg, JsonPathPredicateCallback exec,
2027 void *param)
2028{
2030 JsonValueListIterator lseqit;
2031 JsonValueList lseq = {0};
2032 JsonValueList rseq = {0};
2033 JsonbValue *lval;
2034 bool error = false;
2035 bool found = false;
2036
2037 /* Left argument is always auto-unwrapped. */
2038 res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
2039 if (jperIsError(res))
2040 return jpbUnknown;
2041
2042 if (rarg)
2043 {
2044 /* Right argument is conditionally auto-unwrapped. */
2045 res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
2046 unwrapRightArg, &rseq);
2047 if (jperIsError(res))
2048 return jpbUnknown;
2049 }
2050
2051 JsonValueListInitIterator(&lseq, &lseqit);
2052 while ((lval = JsonValueListNext(&lseq, &lseqit)))
2053 {
2054 JsonValueListIterator rseqit;
2055 JsonbValue *rval;
2056 bool first = true;
2057
2058 JsonValueListInitIterator(&rseq, &rseqit);
2059 if (rarg)
2060 rval = JsonValueListNext(&rseq, &rseqit);
2061 else
2062 rval = NULL;
2063
2064 /* Loop over right arg sequence or do single pass otherwise */
2065 while (rarg ? (rval != NULL) : first)
2066 {
2067 JsonPathBool res = exec(pred, lval, rval, param);
2068
2069 if (res == jpbUnknown)
2070 {
2071 if (jspStrictAbsenceOfErrors(cxt))
2072 return jpbUnknown;
2073
2074 error = true;
2075 }
2076 else if (res == jpbTrue)
2077 {
2078 if (!jspStrictAbsenceOfErrors(cxt))
2079 return jpbTrue;
2080
2081 found = true;
2082 }
2083
2084 first = false;
2085 if (rarg)
2086 rval = JsonValueListNext(&rseq, &rseqit);
2087 }
2088 }
2089
2090 if (found) /* possible only in strict mode */
2091 return jpbTrue;
2092
2093 if (error) /* possible only in lax mode */
2094 return jpbUnknown;
2095
2096 return jpbFalse;
2097}
2098
2099/*
2100 * Execute binary arithmetic expression on singleton numeric operands.
2101 * Array operands are automatically unwrapped in lax mode.
2102 */
2103static JsonPathExecResult
2105 JsonbValue *jb, BinaryArithmFunc func,
2106 JsonValueList *found)
2107{
2108 JsonPathExecResult jper;
2109 JsonPathItem elem;
2110 JsonValueList lseq = {0};
2111 JsonValueList rseq = {0};
2112 JsonbValue *lval;
2113 JsonbValue *rval;
2114 Numeric res;
2115
2116 jspGetLeftArg(jsp, &elem);
2117
2118 /*
2119 * XXX: By standard only operands of multiplicative expressions are
2120 * unwrapped. We extend it to other binary arithmetic expressions too.
2121 */
2122 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
2123 if (jperIsError(jper))
2124 return jper;
2125
2126 jspGetRightArg(jsp, &elem);
2127
2128 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
2129 if (jperIsError(jper))
2130 return jper;
2131
2132 if (JsonValueListLength(&lseq) != 1 ||
2133 !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
2135 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2136 errmsg("left operand of jsonpath operator %s is not a single numeric value",
2137 jspOperationName(jsp->type)))));
2138
2139 if (JsonValueListLength(&rseq) != 1 ||
2140 !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
2142 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
2143 errmsg("right operand of jsonpath operator %s is not a single numeric value",
2144 jspOperationName(jsp->type)))));
2145
2146 if (jspThrowErrors(cxt))
2147 {
2148 res = func(lval->val.numeric, rval->val.numeric, NULL);
2149 }
2150 else
2151 {
2152 bool error = false;
2153
2154 res = func(lval->val.numeric, rval->val.numeric, &error);
2155
2156 if (error)
2157 return jperError;
2158 }
2159
2160 if (!jspGetNext(jsp, &elem) && !found)
2161 return jperOk;
2162
2163 lval = palloc(sizeof(*lval));
2164 lval->type = jbvNumeric;
2165 lval->val.numeric = res;
2166
2167 return executeNextItem(cxt, jsp, &elem, lval, found, false);
2168}
2169
2170/*
2171 * Execute unary arithmetic expression for each numeric item in its operand's
2172 * sequence. Array operand is automatically unwrapped in lax mode.
2173 */
2174static JsonPathExecResult
2176 JsonbValue *jb, PGFunction func, JsonValueList *found)
2177{
2178 JsonPathExecResult jper;
2179 JsonPathExecResult jper2;
2180 JsonPathItem elem;
2181 JsonValueList seq = {0};
2183 JsonbValue *val;
2184 bool hasNext;
2185
2186 jspGetArg(jsp, &elem);
2187 jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
2188
2189 if (jperIsError(jper))
2190 return jper;
2191
2192 jper = jperNotFound;
2193
2194 hasNext = jspGetNext(jsp, &elem);
2195
2196 JsonValueListInitIterator(&seq, &it);
2197 while ((val = JsonValueListNext(&seq, &it)))
2198 {
2199 if ((val = getScalar(val, jbvNumeric)))
2200 {
2201 if (!found && !hasNext)
2202 return jperOk;
2203 }
2204 else
2205 {
2206 if (!found && !hasNext)
2207 continue; /* skip non-numerics processing */
2208
2210 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
2211 errmsg("operand of unary jsonpath operator %s is not a numeric value",
2212 jspOperationName(jsp->type)))));
2213 }
2214
2215 if (func)
2216 val->val.numeric =
2218 NumericGetDatum(val->val.numeric)));
2219
2220 jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
2221
2222 if (jperIsError(jper2))
2223 return jper2;
2224
2225 if (jper2 == jperOk)
2226 {
2227 if (!found)
2228 return jperOk;
2229 jper = jperOk;
2230 }
2231 }
2232
2233 return jper;
2234}
2235
2236/*
2237 * STARTS_WITH predicate callback.
2238 *
2239 * Check if the 'whole' string starts from 'initial' string.
2240 */
2241static JsonPathBool
2243 void *param)
2244{
2245 if (!(whole = getScalar(whole, jbvString)))
2246 return jpbUnknown; /* error */
2247
2248 if (!(initial = getScalar(initial, jbvString)))
2249 return jpbUnknown; /* error */
2250
2251 if (whole->val.string.len >= initial->val.string.len &&
2252 !memcmp(whole->val.string.val,
2253 initial->val.string.val,
2254 initial->val.string.len))
2255 return jpbTrue;
2256
2257 return jpbFalse;
2258}
2259
2260/*
2261 * LIKE_REGEX predicate callback.
2262 *
2263 * Check if the string matches regex pattern.
2264 */
2265static JsonPathBool
2267 void *param)
2268{
2269 JsonLikeRegexContext *cxt = param;
2270
2271 if (!(str = getScalar(str, jbvString)))
2272 return jpbUnknown;
2273
2274 /* Cache regex text and converted flags. */
2275 if (!cxt->regex)
2276 {
2277 cxt->regex =
2281 &(cxt->cflags), NULL);
2282 }
2283
2284 if (RE_compile_and_execute(cxt->regex, str->val.string.val,
2285 str->val.string.len,
2286 cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
2287 return jpbTrue;
2288
2289 return jpbFalse;
2290}
2291
2292/*
2293 * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
2294 * user function 'func'.
2295 */
2296static JsonPathExecResult
2298 JsonbValue *jb, bool unwrap, PGFunction func,
2299 JsonValueList *found)
2300{
2302 Datum datum;
2303
2304 if (unwrap && JsonbType(jb) == jbvArray)
2305 return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
2306
2307 if (!(jb = getScalar(jb, jbvNumeric)))
2309 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
2310 errmsg("jsonpath item method .%s() can only be applied to a numeric value",
2311 jspOperationName(jsp->type)))));
2312
2313 datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
2314
2315 if (!jspGetNext(jsp, &next) && !found)
2316 return jperOk;
2317
2318 jb = palloc(sizeof(*jb));
2319 jb->type = jbvNumeric;
2320 jb->val.numeric = DatumGetNumeric(datum);
2321
2322 return executeNextItem(cxt, jsp, &next, jb, found, false);
2323}
2324
2325/*
2326 * Implementation of the .datetime() and related methods.
2327 *
2328 * Converts a string into a date/time value. The actual type is determined at
2329 * run time.
2330 * If an argument is provided, this argument is used as a template string.
2331 * Otherwise, the first fitting ISO format is selected.
2332 *
2333 * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
2334 * have a format, so ISO format is used. However, except for .date(), they all
2335 * take an optional time precision.
2336 */
2337static JsonPathExecResult
2339 JsonbValue *jb, JsonValueList *found)
2340{
2341 JsonbValue jbvbuf;
2342 Datum value;
2343 text *datetime;
2344 Oid collid;
2345 Oid typid;
2346 int32 typmod = -1;
2347 int tz = 0;
2348 bool hasNext;
2350 JsonPathItem elem;
2351 int32 time_precision = -1;
2352
2353 if (!(jb = getScalar(jb, jbvString)))
2355 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2356 errmsg("jsonpath item method .%s() can only be applied to a string",
2357 jspOperationName(jsp->type)))));
2358
2359 datetime = cstring_to_text_with_len(jb->val.string.val,
2360 jb->val.string.len);
2361
2362 /*
2363 * At some point we might wish to have callers supply the collation to
2364 * use, but right now it's unclear that they'd be able to do better than
2365 * DEFAULT_COLLATION_OID anyway.
2366 */
2367 collid = DEFAULT_COLLATION_OID;
2368
2369 /*
2370 * .datetime(template) has an argument, the rest of the methods don't have
2371 * an argument. So we handle that separately.
2372 */
2373 if (jsp->type == jpiDatetime && jsp->content.arg)
2374 {
2375 text *template;
2376 char *template_str;
2377 int template_len;
2378 ErrorSaveContext escontext = {T_ErrorSaveContext};
2379
2380 jspGetArg(jsp, &elem);
2381
2382 if (elem.type != jpiString)
2383 elog(ERROR, "invalid jsonpath item type for .datetime() argument");
2384
2385 template_str = jspGetString(&elem, &template_len);
2386
2387 template = cstring_to_text_with_len(template_str,
2388 template_len);
2389
2390 value = parse_datetime(datetime, template, collid, true,
2391 &typid, &typmod, &tz,
2392 jspThrowErrors(cxt) ? NULL : (Node *) &escontext);
2393
2394 if (escontext.error_occurred)
2395 res = jperError;
2396 else
2397 res = jperOk;
2398 }
2399 else
2400 {
2401 /*
2402 * According to SQL/JSON standard enumerate ISO formats for: date,
2403 * timetz, time, timestamptz, timestamp.
2404 *
2405 * We also support ISO 8601 format (with "T") for timestamps, because
2406 * to_json[b]() functions use this format.
2407 */
2408 static const char *fmt_str[] =
2409 {
2410 "yyyy-mm-dd", /* date */
2411 "HH24:MI:SS.USTZ", /* timetz */
2412 "HH24:MI:SSTZ",
2413 "HH24:MI:SS.US", /* time without tz */
2414 "HH24:MI:SS",
2415 "yyyy-mm-dd HH24:MI:SS.USTZ", /* timestamptz */
2416 "yyyy-mm-dd HH24:MI:SSTZ",
2417 "yyyy-mm-dd\"T\"HH24:MI:SS.USTZ",
2418 "yyyy-mm-dd\"T\"HH24:MI:SSTZ",
2419 "yyyy-mm-dd HH24:MI:SS.US", /* timestamp without tz */
2420 "yyyy-mm-dd HH24:MI:SS",
2421 "yyyy-mm-dd\"T\"HH24:MI:SS.US",
2422 "yyyy-mm-dd\"T\"HH24:MI:SS"
2423 };
2424
2425 /* cache for format texts */
2426 static text *fmt_txt[lengthof(fmt_str)] = {0};
2427 int i;
2428
2429 /*
2430 * Check for optional precision for methods other than .datetime() and
2431 * .date()
2432 */
2433 if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
2434 jsp->content.arg)
2435 {
2436 bool have_error;
2437
2438 jspGetArg(jsp, &elem);
2439
2440 if (elem.type != jpiNumeric)
2441 elog(ERROR, "invalid jsonpath item type for %s argument",
2442 jspOperationName(jsp->type));
2443
2444 time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
2445 &have_error);
2446 if (have_error)
2448 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2449 errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
2450 jspOperationName(jsp->type)))));
2451 }
2452
2453 /* loop until datetime format fits */
2454 for (i = 0; i < lengthof(fmt_str); i++)
2455 {
2456 ErrorSaveContext escontext = {T_ErrorSaveContext};
2457
2458 if (!fmt_txt[i])
2459 {
2460 MemoryContext oldcxt =
2462
2463 fmt_txt[i] = cstring_to_text(fmt_str[i]);
2464 MemoryContextSwitchTo(oldcxt);
2465 }
2466
2467 value = parse_datetime(datetime, fmt_txt[i], collid, true,
2468 &typid, &typmod, &tz,
2469 (Node *) &escontext);
2470
2471 if (!escontext.error_occurred)
2472 {
2473 res = jperOk;
2474 break;
2475 }
2476 }
2477
2478 if (res == jperNotFound)
2479 {
2480 if (jsp->type == jpiDatetime)
2482 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2483 errmsg("%s format is not recognized: \"%s\"",
2484 "datetime", text_to_cstring(datetime)),
2485 errhint("Use a datetime template argument to specify the input data format."))));
2486 else
2488 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2489 errmsg("%s format is not recognized: \"%s\"",
2490 jspOperationName(jsp->type), text_to_cstring(datetime)))));
2491
2492 }
2493 }
2494
2495 /*
2496 * parse_datetime() processes the entire input string per the template or
2497 * ISO format and returns the Datum in best fitted datetime type. So, if
2498 * this call is for a specific datatype, then we do the conversion here.
2499 * Throw an error for incompatible types.
2500 */
2501 switch (jsp->type)
2502 {
2503 case jpiDatetime: /* Nothing to do for DATETIME */
2504 break;
2505 case jpiDate:
2506 {
2507 /* Convert result type to date */
2508 switch (typid)
2509 {
2510 case DATEOID: /* Nothing to do for DATE */
2511 break;
2512 case TIMEOID:
2513 case TIMETZOID:
2515 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2516 errmsg("%s format is not recognized: \"%s\"",
2517 "date", text_to_cstring(datetime)))));
2518 break;
2519 case TIMESTAMPOID:
2521 value);
2522 break;
2523 case TIMESTAMPTZOID:
2525 "timestamptz", "date");
2527 value);
2528 break;
2529 default:
2530 elog(ERROR, "type with oid %u not supported", typid);
2531 }
2532
2533 typid = DATEOID;
2534 }
2535 break;
2536 case jpiTime:
2537 {
2538 /* Convert result type to time without time zone */
2539 switch (typid)
2540 {
2541 case DATEOID:
2543 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2544 errmsg("%s format is not recognized: \"%s\"",
2545 "time", text_to_cstring(datetime)))));
2546 break;
2547 case TIMEOID: /* Nothing to do for TIME */
2548 break;
2549 case TIMETZOID:
2551 "timetz", "time");
2553 value);
2554 break;
2555 case TIMESTAMPOID:
2557 value);
2558 break;
2559 case TIMESTAMPTZOID:
2561 "timestamptz", "time");
2563 value);
2564 break;
2565 default:
2566 elog(ERROR, "type with oid %u not supported", typid);
2567 }
2568
2569 /* Force the user-given time precision, if any */
2570 if (time_precision != -1)
2571 {
2572 TimeADT result;
2573
2574 /* Get a warning when precision is reduced */
2575 time_precision = anytime_typmod_check(false,
2576 time_precision);
2577 result = DatumGetTimeADT(value);
2578 AdjustTimeForTypmod(&result, time_precision);
2579 value = TimeADTGetDatum(result);
2580
2581 /* Update the typmod value with the user-given precision */
2582 typmod = time_precision;
2583 }
2584
2585 typid = TIMEOID;
2586 }
2587 break;
2588 case jpiTimeTz:
2589 {
2590 /* Convert result type to time with time zone */
2591 switch (typid)
2592 {
2593 case DATEOID:
2594 case TIMESTAMPOID:
2596 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2597 errmsg("%s format is not recognized: \"%s\"",
2598 "time_tz", text_to_cstring(datetime)))));
2599 break;
2600 case TIMEOID:
2602 "time", "timetz");
2604 value);
2605 break;
2606 case TIMETZOID: /* Nothing to do for TIMETZ */
2607 break;
2608 case TIMESTAMPTZOID:
2610 value);
2611 break;
2612 default:
2613 elog(ERROR, "type with oid %u not supported", typid);
2614 }
2615
2616 /* Force the user-given time precision, if any */
2617 if (time_precision != -1)
2618 {
2619 TimeTzADT *result;
2620
2621 /* Get a warning when precision is reduced */
2622 time_precision = anytime_typmod_check(true,
2623 time_precision);
2624 result = DatumGetTimeTzADTP(value);
2625 AdjustTimeForTypmod(&result->time, time_precision);
2626 value = TimeTzADTPGetDatum(result);
2627
2628 /* Update the typmod value with the user-given precision */
2629 typmod = time_precision;
2630 }
2631
2632 typid = TIMETZOID;
2633 }
2634 break;
2635 case jpiTimestamp:
2636 {
2637 /* Convert result type to timestamp without time zone */
2638 switch (typid)
2639 {
2640 case DATEOID:
2642 value);
2643 break;
2644 case TIMEOID:
2645 case TIMETZOID:
2647 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2648 errmsg("%s format is not recognized: \"%s\"",
2649 "timestamp", text_to_cstring(datetime)))));
2650 break;
2651 case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
2652 break;
2653 case TIMESTAMPTZOID:
2655 "timestamptz", "timestamp");
2657 value);
2658 break;
2659 default:
2660 elog(ERROR, "type with oid %u not supported", typid);
2661 }
2662
2663 /* Force the user-given time precision, if any */
2664 if (time_precision != -1)
2665 {
2666 Timestamp result;
2667 ErrorSaveContext escontext = {T_ErrorSaveContext};
2668
2669 /* Get a warning when precision is reduced */
2670 time_precision = anytimestamp_typmod_check(false,
2671 time_precision);
2672 result = DatumGetTimestamp(value);
2673 AdjustTimestampForTypmod(&result, time_precision,
2674 (Node *) &escontext);
2675 if (escontext.error_occurred) /* should not happen */
2677 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2678 errmsg("time precision of jsonpath item method .%s() is invalid",
2679 jspOperationName(jsp->type)))));
2680 value = TimestampGetDatum(result);
2681
2682 /* Update the typmod value with the user-given precision */
2683 typmod = time_precision;
2684 }
2685
2686 typid = TIMESTAMPOID;
2687 }
2688 break;
2689 case jpiTimestampTz:
2690 {
2691 struct pg_tm tm;
2692 fsec_t fsec;
2693
2694 /* Convert result type to timestamp with time zone */
2695 switch (typid)
2696 {
2697 case DATEOID:
2699 "date", "timestamptz");
2700
2701 /*
2702 * Get the timezone value explicitly since JsonbValue
2703 * keeps that separate.
2704 */
2706 &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
2707 tm.tm_hour = 0;
2708 tm.tm_min = 0;
2709 tm.tm_sec = 0;
2711
2713 value);
2714 break;
2715 case TIMEOID:
2716 case TIMETZOID:
2718 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2719 errmsg("%s format is not recognized: \"%s\"",
2720 "timestamp_tz", text_to_cstring(datetime)))));
2721 break;
2722 case TIMESTAMPOID:
2724 "timestamp", "timestamptz");
2725
2726 /*
2727 * Get the timezone value explicitly since JsonbValue
2728 * keeps that separate.
2729 */
2731 &fsec, NULL, NULL) == 0)
2734
2736 value);
2737 break;
2738 case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
2739 break;
2740 default:
2741 elog(ERROR, "type with oid %u not supported", typid);
2742 }
2743
2744 /* Force the user-given time precision, if any */
2745 if (time_precision != -1)
2746 {
2747 Timestamp result;
2748 ErrorSaveContext escontext = {T_ErrorSaveContext};
2749
2750 /* Get a warning when precision is reduced */
2751 time_precision = anytimestamp_typmod_check(true,
2752 time_precision);
2753 result = DatumGetTimestampTz(value);
2754 AdjustTimestampForTypmod(&result, time_precision,
2755 (Node *) &escontext);
2756 if (escontext.error_occurred) /* should not happen */
2758 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2759 errmsg("time precision of jsonpath item method .%s() is invalid",
2760 jspOperationName(jsp->type)))));
2761 value = TimestampTzGetDatum(result);
2762
2763 /* Update the typmod value with the user-given precision */
2764 typmod = time_precision;
2765 }
2766
2767 typid = TIMESTAMPTZOID;
2768 }
2769 break;
2770 default:
2771 elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
2772 }
2773
2774 pfree(datetime);
2775
2776 if (jperIsError(res))
2777 return res;
2778
2779 hasNext = jspGetNext(jsp, &elem);
2780
2781 if (!hasNext && !found)
2782 return res;
2783
2784 jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
2785
2786 jb->type = jbvDatetime;
2787 jb->val.datetime.value = value;
2788 jb->val.datetime.typid = typid;
2789 jb->val.datetime.typmod = typmod;
2790 jb->val.datetime.tz = tz;
2791
2792 return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
2793}
2794
2795/*
2796 * Implementation of .keyvalue() method.
2797 *
2798 * .keyvalue() method returns a sequence of object's key-value pairs in the
2799 * following format: '{ "key": key, "value": value, "id": id }'.
2800 *
2801 * "id" field is an object identifier which is constructed from the two parts:
2802 * base object id and its binary offset in base object's jsonb:
2803 * id = 10000000000 * base_object_id + obj_offset_in_base_object
2804 *
2805 * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
2806 * (maximal offset in jsonb). Decimal multiplier is used here to improve the
2807 * readability of identifiers.
2808 *
2809 * Base object is usually a root object of the path: context item '$' or path
2810 * variable '$var', literals can't produce objects for now. But if the path
2811 * contains generated objects (.keyvalue() itself, for example), then they
2812 * become base object for the subsequent .keyvalue().
2813 *
2814 * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
2815 * of variables (see getJsonPathVariable()). Ids for generated objects
2816 * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
2817 */
2818static JsonPathExecResult
2820 JsonbValue *jb, JsonValueList *found)
2821{
2824 JsonbContainer *jbc;
2827 JsonbValue idval;
2828 JsonbValue keystr;
2829 JsonbValue valstr;
2830 JsonbValue idstr;
2831 JsonbIterator *it;
2833 int64 id;
2834 bool hasNext;
2835
2836 if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
2838 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
2839 errmsg("jsonpath item method .%s() can only be applied to an object",
2840 jspOperationName(jsp->type)))));
2841
2842 jbc = jb->val.binary.data;
2843
2844 if (!JsonContainerSize(jbc))
2845 return jperNotFound; /* no key-value pairs */
2846
2847 hasNext = jspGetNext(jsp, &next);
2848
2849 keystr.type = jbvString;
2850 keystr.val.string.val = "key";
2851 keystr.val.string.len = 3;
2852
2853 valstr.type = jbvString;
2854 valstr.val.string.val = "value";
2855 valstr.val.string.len = 5;
2856
2857 idstr.type = jbvString;
2858 idstr.val.string.val = "id";
2859 idstr.val.string.len = 2;
2860
2861 /* construct object id from its base object and offset inside that */
2862 id = jb->type != jbvBinary ? 0 :
2863 (int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
2864 id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
2865
2866 idval.type = jbvNumeric;
2867 idval.val.numeric = int64_to_numeric(id);
2868
2869 it = JsonbIteratorInit(jbc);
2870
2871 while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
2872 {
2873 JsonBaseObjectInfo baseObject;
2874 JsonbValue obj;
2876 JsonbValue *keyval;
2877 Jsonb *jsonb;
2878
2879 if (tok != WJB_KEY)
2880 continue;
2881
2882 res = jperOk;
2883
2884 if (!hasNext && !found)
2885 break;
2886
2887 tok = JsonbIteratorNext(&it, &val, true);
2888 Assert(tok == WJB_VALUE);
2889
2890 ps = NULL;
2892
2893 pushJsonbValue(&ps, WJB_KEY, &keystr);
2895
2896 pushJsonbValue(&ps, WJB_KEY, &valstr);
2898
2899 pushJsonbValue(&ps, WJB_KEY, &idstr);
2900 pushJsonbValue(&ps, WJB_VALUE, &idval);
2901
2902 keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2903
2904 jsonb = JsonbValueToJsonb(keyval);
2905
2906 JsonbInitBinary(&obj, jsonb);
2907
2908 baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2909
2910 res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2911
2912 cxt->baseObject = baseObject;
2913
2914 if (jperIsError(res))
2915 return res;
2916
2917 if (res == jperOk && !found)
2918 break;
2919 }
2920
2921 return res;
2922}
2923
2924/*
2925 * Convert boolean execution status 'res' to a boolean JSON item and execute
2926 * next jsonpath.
2927 */
2928static JsonPathExecResult
2931{
2933 JsonbValue jbv;
2934
2935 if (!jspGetNext(jsp, &next) && !found)
2936 return jperOk; /* found singleton boolean value */
2937
2938 if (res == jpbUnknown)
2939 {
2940 jbv.type = jbvNull;
2941 }
2942 else
2943 {
2944 jbv.type = jbvBool;
2945 jbv.val.boolean = res == jpbTrue;
2946 }
2947
2948 return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2949}
2950
2951/*
2952 * Convert jsonpath's scalar or variable node to actual jsonb value.
2953 *
2954 * If node is a variable then its id returned, otherwise 0 returned.
2955 */
2956static void
2959{
2960 switch (item->type)
2961 {
2962 case jpiNull:
2963 value->type = jbvNull;
2964 break;
2965 case jpiBool:
2966 value->type = jbvBool;
2967 value->val.boolean = jspGetBool(item);
2968 break;
2969 case jpiNumeric:
2970 value->type = jbvNumeric;
2971 value->val.numeric = jspGetNumeric(item);
2972 break;
2973 case jpiString:
2974 value->type = jbvString;
2975 value->val.string.val = jspGetString(item,
2976 &value->val.string.len);
2977 break;
2978 case jpiVariable:
2979 getJsonPathVariable(cxt, item, value);
2980 return;
2981 default:
2982 elog(ERROR, "unexpected jsonpath item type");
2983 }
2984}
2985
2986/*
2987 * Returns the computed value of a JSON path variable with given name.
2988 */
2989static JsonbValue *
2990GetJsonPathVar(void *cxt, char *varName, int varNameLen,
2991 JsonbValue *baseObject, int *baseObjectId)
2992{
2993 JsonPathVariable *var = NULL;
2994 List *vars = cxt;
2995 ListCell *lc;
2996 JsonbValue *result;
2997 int id = 1;
2998
2999 foreach(lc, vars)
3000 {
3001 JsonPathVariable *curvar = lfirst(lc);
3002
3003 if (curvar->namelen == varNameLen &&
3004 strncmp(curvar->name, varName, varNameLen) == 0)
3005 {
3006 var = curvar;
3007 break;
3008 }
3009
3010 id++;
3011 }
3012
3013 if (var == NULL)
3014 {
3015 *baseObjectId = -1;
3016 return NULL;
3017 }
3018
3019 result = palloc(sizeof(JsonbValue));
3020 if (var->isnull)
3021 {
3022 *baseObjectId = 0;
3023 result->type = jbvNull;
3024 }
3025 else
3026 JsonItemFromDatum(var->value, var->typid, var->typmod, result);
3027
3028 *baseObject = *result;
3029 *baseObjectId = id;
3030
3031 return result;
3032}
3033
3034static int
3036{
3037 List *vars = (List *) cxt;
3038
3039 return list_length(vars);
3040}
3041
3042
3043/*
3044 * Initialize JsonbValue to pass to jsonpath executor from given
3045 * datum value of the specified type.
3046 */
3047static void
3049{
3050 switch (typid)
3051 {
3052 case BOOLOID:
3053 res->type = jbvBool;
3054 res->val.boolean = DatumGetBool(val);
3055 break;
3056 case NUMERICOID:
3058 break;
3059 case INT2OID:
3061 break;
3062 case INT4OID:
3064 break;
3065 case INT8OID:
3067 break;
3068 case FLOAT4OID:
3070 break;
3071 case FLOAT8OID:
3073 break;
3074 case TEXTOID:
3075 case VARCHAROID:
3076 res->type = jbvString;
3077 res->val.string.val = VARDATA_ANY(val);
3078 res->val.string.len = VARSIZE_ANY_EXHDR(val);
3079 break;
3080 case DATEOID:
3081 case TIMEOID:
3082 case TIMETZOID:
3083 case TIMESTAMPOID:
3084 case TIMESTAMPTZOID:
3085 res->type = jbvDatetime;
3086 res->val.datetime.value = val;
3087 res->val.datetime.typid = typid;
3088 res->val.datetime.typmod = typmod;
3089 res->val.datetime.tz = 0;
3090 break;
3091 case JSONBOID:
3092 {
3093 JsonbValue *jbv = res;
3094 Jsonb *jb = DatumGetJsonbP(val);
3095
3096 if (JsonContainerIsScalar(&jb->root))
3097 {
3098 bool result PG_USED_FOR_ASSERTS_ONLY;
3099
3100 result = JsonbExtractScalar(&jb->root, jbv);
3101 Assert(result);
3102 }
3103 else
3104 JsonbInitBinary(jbv, jb);
3105 break;
3106 }
3107 case JSONOID:
3108 {
3109 text *txt = DatumGetTextP(val);
3110 char *str = text_to_cstring(txt);
3111 Jsonb *jb;
3112
3115 pfree(str);
3116
3117 JsonItemFromDatum(JsonbPGetDatum(jb), JSONBOID, -1, res);
3118 break;
3119 }
3120 default:
3121 ereport(ERROR,
3122 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3123 errmsg("could not convert value of type %s to jsonpath",
3124 format_type_be(typid)));
3125 }
3126}
3127
3128/* Initialize numeric value from the given datum */
3129static void
3131{
3132 jbv->type = jbvNumeric;
3133 jbv->val.numeric = DatumGetNumeric(num);
3134}
3135
3136/*
3137 * Get the value of variable passed to jsonpath executor
3138 */
3139static void
3142{
3143 char *varName;
3144 int varNameLength;
3145 JsonbValue baseObject;
3146 int baseObjectId;
3147 JsonbValue *v;
3148
3150 varName = jspGetString(variable, &varNameLength);
3151
3152 if (cxt->vars == NULL ||
3153 (v = cxt->getVar(cxt->vars, varName, varNameLength,
3154 &baseObject, &baseObjectId)) == NULL)
3155 ereport(ERROR,
3156 (errcode(ERRCODE_UNDEFINED_OBJECT),
3157 errmsg("could not find jsonpath variable \"%s\"",
3158 pnstrdup(varName, varNameLength))));
3159
3160 if (baseObjectId > 0)
3161 {
3162 *value = *v;
3163 setBaseObject(cxt, &baseObject, baseObjectId);
3164 }
3165}
3166
3167/*
3168 * Definition of JsonPathGetVarCallback for when JsonPathExecContext.vars
3169 * is specified as a jsonb value.
3170 */
3171static JsonbValue *
3172getJsonPathVariableFromJsonb(void *varsJsonb, char *varName, int varNameLength,
3173 JsonbValue *baseObject, int *baseObjectId)
3174{
3175 Jsonb *vars = varsJsonb;
3176 JsonbValue tmp;
3177 JsonbValue *result;
3178
3179 tmp.type = jbvString;
3180 tmp.val.string.val = varName;
3181 tmp.val.string.len = varNameLength;
3182
3183 result = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
3184
3185 if (result == NULL)
3186 {
3187 *baseObjectId = -1;
3188 return NULL;
3189 }
3190
3191 *baseObjectId = 1;
3192 JsonbInitBinary(baseObject, vars);
3193
3194 return result;
3195}
3196
3197/*
3198 * Definition of JsonPathCountVarsCallback for when JsonPathExecContext.vars
3199 * is specified as a jsonb value.
3200 */
3201static int
3203{
3204 Jsonb *vars = varsJsonb;
3205
3206 if (vars && !JsonContainerIsObject(&vars->root))
3207 {
3208 ereport(ERROR,
3209 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3210 errmsg("\"vars\" argument is not an object"),
3211 errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object."));
3212 }
3213
3214 /* count of base objects */
3215 return vars != NULL ? 1 : 0;
3216}
3217
3218/**************** Support functions for JsonPath execution *****************/
3219
3220/*
3221 * Returns the size of an array item, or -1 if item is not an array.
3222 */
3223static int
3225{
3226 Assert(jb->type != jbvArray);
3227
3228 if (jb->type == jbvBinary)
3229 {
3230 JsonbContainer *jbc = jb->val.binary.data;
3231
3233 return JsonContainerSize(jbc);
3234 }
3235
3236 return -1;
3237}
3238
3239/* Comparison predicate callback. */
3240static JsonPathBool
3242{
3244
3245 return compareItems(cmp->type, lv, rv, cxt->useTz);
3246}
3247
3248/*
3249 * Perform per-byte comparison of two strings.
3250 */
3251static int
3252binaryCompareStrings(const char *s1, int len1,
3253 const char *s2, int len2)
3254{
3255 int cmp;
3256
3257 cmp = memcmp(s1, s2, Min(len1, len2));
3258
3259 if (cmp != 0)
3260 return cmp;
3261
3262 if (len1 == len2)
3263 return 0;
3264
3265 return len1 < len2 ? -1 : 1;
3266}
3267
3268/*
3269 * Compare two strings in the current server encoding using Unicode codepoint
3270 * collation.
3271 */
3272static int
3273compareStrings(const char *mbstr1, int mblen1,
3274 const char *mbstr2, int mblen2)
3275{
3278 {
3279 /*
3280 * It's known property of UTF-8 strings that their per-byte comparison
3281 * result matches codepoints comparison result. ASCII can be
3282 * considered as special case of UTF-8.
3283 */
3284 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3285 }
3286 else
3287 {
3288 char *utf8str1,
3289 *utf8str2;
3290 int cmp,
3291 utf8len1,
3292 utf8len2;
3293
3294 /*
3295 * We have to convert other encodings to UTF-8 first, then compare.
3296 * Input strings may be not null-terminated and pg_server_to_any() may
3297 * return them "as is". So, use strlen() only if there is real
3298 * conversion.
3299 */
3300 utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
3301 utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
3302 utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
3303 utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
3304
3305 cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
3306
3307 /*
3308 * If pg_server_to_any() did no real conversion, then we actually
3309 * compared original strings. So, we already done.
3310 */
3311 if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
3312 return cmp;
3313
3314 /* Free memory if needed */
3315 if (mbstr1 != utf8str1)
3316 pfree(utf8str1);
3317 if (mbstr2 != utf8str2)
3318 pfree(utf8str2);
3319
3320 /*
3321 * When all Unicode codepoints are equal, return result of binary
3322 * comparison. In some edge cases, same characters may have different
3323 * representations in encoding. Then our behavior could diverge from
3324 * standard. However, that allow us to do simple binary comparison
3325 * for "==" operator, which is performance critical in typical cases.
3326 * In future to implement strict standard conformance, we can do
3327 * normalization of input JSON strings.
3328 */
3329 if (cmp == 0)
3330 return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
3331 else
3332 return cmp;
3333 }
3334}
3335
3336/*
3337 * Compare two SQL/JSON items using comparison operation 'op'.
3338 */
3339static JsonPathBool
3340compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
3341{
3342 int cmp;
3343 bool res;
3344
3345 if (jb1->type != jb2->type)
3346 {
3347 if (jb1->type == jbvNull || jb2->type == jbvNull)
3348
3349 /*
3350 * Equality and order comparison of nulls to non-nulls returns
3351 * always false, but inequality comparison returns true.
3352 */
3353 return op == jpiNotEqual ? jpbTrue : jpbFalse;
3354
3355 /* Non-null items of different types are not comparable. */
3356 return jpbUnknown;
3357 }
3358
3359 switch (jb1->type)
3360 {
3361 case jbvNull:
3362 cmp = 0;
3363 break;
3364 case jbvBool:
3365 cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
3366 jb1->val.boolean ? 1 : -1;
3367 break;
3368 case jbvNumeric:
3369 cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
3370 break;
3371 case jbvString:
3372 if (op == jpiEqual)
3373 return jb1->val.string.len != jb2->val.string.len ||
3374 memcmp(jb1->val.string.val,
3375 jb2->val.string.val,
3376 jb1->val.string.len) ? jpbFalse : jpbTrue;
3377
3378 cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
3379 jb2->val.string.val, jb2->val.string.len);
3380 break;
3381 case jbvDatetime:
3382 {
3383 bool cast_error;
3384
3385 cmp = compareDatetime(jb1->val.datetime.value,
3386 jb1->val.datetime.typid,
3387 jb2->val.datetime.value,
3388 jb2->val.datetime.typid,
3389 useTz,
3390 &cast_error);
3391
3392 if (cast_error)
3393 return jpbUnknown;
3394 }
3395 break;
3396
3397 case jbvBinary:
3398 case jbvArray:
3399 case jbvObject:
3400 return jpbUnknown; /* non-scalars are not comparable */
3401
3402 default:
3403 elog(ERROR, "invalid jsonb value type %d", jb1->type);
3404 }
3405
3406 switch (op)
3407 {
3408 case jpiEqual:
3409 res = (cmp == 0);
3410 break;
3411 case jpiNotEqual:
3412 res = (cmp != 0);
3413 break;
3414 case jpiLess:
3415 res = (cmp < 0);
3416 break;
3417 case jpiGreater:
3418 res = (cmp > 0);
3419 break;
3420 case jpiLessOrEqual:
3421 res = (cmp <= 0);
3422 break;
3423 case jpiGreaterOrEqual:
3424 res = (cmp >= 0);
3425 break;
3426 default:
3427 elog(ERROR, "unrecognized jsonpath operation: %d", op);
3428 return jpbUnknown;
3429 }
3430
3431 return res ? jpbTrue : jpbFalse;
3432}
3433
3434/* Compare two numerics */
3435static int
3437{
3440 NumericGetDatum(b)));
3441}
3442
3443static JsonbValue *
3445{
3446 JsonbValue *dst = palloc(sizeof(*dst));
3447
3448 *dst = *src;
3449
3450 return dst;
3451}
3452
3453/*
3454 * Execute array subscript expression and convert resulting numeric item to
3455 * the integer type with truncation.
3456 */
3457static JsonPathExecResult
3459 int32 *index)
3460{
3461 JsonbValue *jbv;
3462 JsonValueList found = {0};
3463 JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
3464 Datum numeric_index;
3465 bool have_error = false;
3466
3467 if (jperIsError(res))
3468 return res;
3469
3470 if (JsonValueListLength(&found) != 1 ||
3471 !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
3473 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3474 errmsg("jsonpath array subscript is not a single numeric value"))));
3475
3476 numeric_index = DirectFunctionCall2(numeric_trunc,
3477 NumericGetDatum(jbv->val.numeric),
3478 Int32GetDatum(0));
3479
3481 &have_error);
3482
3483 if (have_error)
3485 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
3486 errmsg("jsonpath array subscript is out of integer range"))));
3487
3488 return jperOk;
3489}
3490
3491/* Save base object and its id needed for the execution of .keyvalue(). */
3492static JsonBaseObjectInfo
3494{
3495 JsonBaseObjectInfo baseObject = cxt->baseObject;
3496
3497 cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
3498 (JsonbContainer *) jbv->val.binary.data;
3499 cxt->baseObject.id = id;
3500
3501 return baseObject;
3502}
3503
3504static void
3506{
3507 jvl->singleton = NULL;
3508 jvl->list = NIL;
3509}
3510
3511static void
3513{
3514 if (jvl->singleton)
3515 {
3516 jvl->list = list_make2(jvl->singleton, jbv);
3517 jvl->singleton = NULL;
3518 }
3519 else if (!jvl->list)
3520 jvl->singleton = jbv;
3521 else
3522 jvl->list = lappend(jvl->list, jbv);
3523}
3524
3525static int
3527{
3528 return jvl->singleton ? 1 : list_length(jvl->list);
3529}
3530
3531static bool
3533{
3534 return !jvl->singleton && (jvl->list == NIL);
3535}
3536
3537static JsonbValue *
3539{
3540 return jvl->singleton ? jvl->singleton : linitial(jvl->list);
3541}
3542
3543static List *
3545{
3546 if (jvl->singleton)
3547 return list_make1(jvl->singleton);
3548
3549 return jvl->list;
3550}
3551
3552static void
3554{
3555 if (jvl->singleton)
3556 {
3557 it->value = jvl->singleton;
3558 it->list = NIL;
3559 it->next = NULL;
3560 }
3561 else if (jvl->list != NIL)
3562 {
3563 it->value = (JsonbValue *) linitial(jvl->list);
3564 it->list = jvl->list;
3565 it->next = list_second_cell(jvl->list);
3566 }
3567 else
3568 {
3569 it->value = NULL;
3570 it->list = NIL;
3571 it->next = NULL;
3572 }
3573}
3574
3575/*
3576 * Get the next item from the sequence advancing iterator.
3577 */
3578static JsonbValue *
3580{
3581 JsonbValue *result = it->value;
3582
3583 if (it->next)
3584 {
3585 it->value = lfirst(it->next);
3586 it->next = lnext(it->list, it->next);
3587 }
3588 else
3589 {
3590 it->value = NULL;
3591 }
3592
3593 return result;
3594}
3595
3596/*
3597 * Initialize a binary JsonbValue with the given jsonb container.
3598 */
3599static JsonbValue *
3601{
3602 jbv->type = jbvBinary;
3603 jbv->val.binary.data = &jb->root;
3604 jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
3605
3606 return jbv;
3607}
3608
3609/*
3610 * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
3611 */
3612static int
3614{
3615 int type = jb->type;
3616
3617 if (jb->type == jbvBinary)
3618 {
3619 JsonbContainer *jbc = jb->val.binary.data;
3620
3621 /* Scalars should be always extracted during jsonpath execution. */
3623
3624 if (JsonContainerIsObject(jbc))
3625 type = jbvObject;
3626 else if (JsonContainerIsArray(jbc))
3627 type = jbvArray;
3628 else
3629 elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
3630 }
3631
3632 return type;
3633}
3634
3635/* Get scalar of given type or NULL on type mismatch */
3636static JsonbValue *
3638{
3639 /* Scalars should be always extracted during jsonpath execution. */
3640 Assert(scalar->type != jbvBinary ||
3641 !JsonContainerIsScalar(scalar->val.binary.data));
3642
3643 return scalar->type == type ? scalar : NULL;
3644}
3645
3646/* Construct a JSON array from the item list */
3647static JsonbValue *
3649{
3650 JsonbParseState *ps = NULL;
3652 JsonbValue *jbv;
3653
3655
3657 while ((jbv = JsonValueListNext(items, &it)))
3658 pushJsonbValue(&ps, WJB_ELEM, jbv);
3659
3660 return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
3661}
3662
3663/* Check if the timezone required for casting from type1 to type2 is used */
3664static void
3665checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
3666{
3667 if (!useTz)
3668 ereport(ERROR,
3669 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3670 errmsg("cannot convert value from %s to %s without time zone usage",
3671 type1, type2),
3672 errhint("Use *_tz() function for time zone support.")));
3673}
3674
3675/* Convert time datum to timetz datum */
3676static Datum
3677castTimeToTimeTz(Datum time, bool useTz)
3678{
3679 checkTimezoneIsUsedForCast(useTz, "time", "timetz");
3680
3681 return DirectFunctionCall1(time_timetz, time);
3682}
3683
3684/*
3685 * Compare date to timestamp.
3686 * Note that this doesn't involve any timezone considerations.
3687 */
3688static int
3689cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
3690{
3691 return date_cmp_timestamp_internal(date1, ts2);
3692}
3693
3694/*
3695 * Compare date to timestamptz.
3696 */
3697static int
3699{
3700 checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
3701
3702 return date_cmp_timestamptz_internal(date1, tstz2);
3703}
3704
3705/*
3706 * Compare timestamp to timestamptz.
3707 */
3708static int
3710{
3711 checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
3712
3713 return timestamp_cmp_timestamptz_internal(ts1, tstz2);
3714}
3715
3716/*
3717 * Cross-type comparison of two datetime SQL/JSON items. If items are
3718 * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
3719 * If the cast requires timezone and it is not used, then explicit error is thrown.
3720 */
3721static int
3722compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
3723 bool useTz, bool *cast_error)
3724{
3725 PGFunction cmpfunc;
3726
3727 *cast_error = false;
3728
3729 switch (typid1)
3730 {
3731 case DATEOID:
3732 switch (typid2)
3733 {
3734 case DATEOID:
3735 cmpfunc = date_cmp;
3736
3737 break;
3738
3739 case TIMESTAMPOID:
3741 DatumGetTimestamp(val2),
3742 useTz);
3743
3744 case TIMESTAMPTZOID:
3746 DatumGetTimestampTz(val2),
3747 useTz);
3748
3749 case TIMEOID:
3750 case TIMETZOID:
3751 *cast_error = true; /* uncomparable types */
3752 return 0;
3753
3754 default:
3755 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3756 typid2);
3757 }
3758 break;
3759
3760 case TIMEOID:
3761 switch (typid2)
3762 {
3763 case TIMEOID:
3764 cmpfunc = time_cmp;
3765
3766 break;
3767
3768 case TIMETZOID:
3769 val1 = castTimeToTimeTz(val1, useTz);
3770 cmpfunc = timetz_cmp;
3771
3772 break;
3773
3774 case DATEOID:
3775 case TIMESTAMPOID:
3776 case TIMESTAMPTZOID:
3777 *cast_error = true; /* uncomparable types */
3778 return 0;
3779
3780 default:
3781 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3782 typid2);
3783 }
3784 break;
3785
3786 case TIMETZOID:
3787 switch (typid2)
3788 {
3789 case TIMEOID:
3790 val2 = castTimeToTimeTz(val2, useTz);
3791 cmpfunc = timetz_cmp;
3792
3793 break;
3794
3795 case TIMETZOID:
3796 cmpfunc = timetz_cmp;
3797
3798 break;
3799
3800 case DATEOID:
3801 case TIMESTAMPOID:
3802 case TIMESTAMPTZOID:
3803 *cast_error = true; /* uncomparable types */
3804 return 0;
3805
3806 default:
3807 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3808 typid2);
3809 }
3810 break;
3811
3812 case TIMESTAMPOID:
3813 switch (typid2)
3814 {
3815 case DATEOID:
3816 return -cmpDateToTimestamp(DatumGetDateADT(val2),
3817 DatumGetTimestamp(val1),
3818 useTz);
3819
3820 case TIMESTAMPOID:
3821 cmpfunc = timestamp_cmp;
3822
3823 break;
3824
3825 case TIMESTAMPTZOID:
3827 DatumGetTimestampTz(val2),
3828 useTz);
3829
3830 case TIMEOID:
3831 case TIMETZOID:
3832 *cast_error = true; /* uncomparable types */
3833 return 0;
3834
3835 default:
3836 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3837 typid2);
3838 }
3839 break;
3840
3841 case TIMESTAMPTZOID:
3842 switch (typid2)
3843 {
3844 case DATEOID:
3846 DatumGetTimestampTz(val1),
3847 useTz);
3848
3849 case TIMESTAMPOID:
3851 DatumGetTimestampTz(val1),
3852 useTz);
3853
3854 case TIMESTAMPTZOID:
3855 cmpfunc = timestamp_cmp;
3856
3857 break;
3858
3859 case TIMEOID:
3860 case TIMETZOID:
3861 *cast_error = true; /* uncomparable types */
3862 return 0;
3863
3864 default:
3865 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
3866 typid2);
3867 }
3868 break;
3869
3870 default:
3871 elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
3872 }
3873
3874 if (*cast_error)
3875 return 0; /* cast error */
3876
3877 return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
3878}
3879
3880/*
3881 * Executor-callable JSON_EXISTS implementation
3882 *
3883 * Returns NULL instead of throwing errors if 'error' is not NULL, setting
3884 * *error to true.
3885 */
3886bool
3888{
3890
3891 res = executeJsonPath(jp, vars,
3893 DatumGetJsonbP(jb), !error, NULL, true);
3894
3896
3897 if (error && jperIsError(res))
3898 *error = true;
3899
3900 return res == jperOk;
3901}
3902
3903/*
3904 * Executor-callable JSON_QUERY implementation
3905 *
3906 * Returns NULL instead of throwing errors if 'error' is not NULL, setting
3907 * *error to true. *empty is set to true if no match is found.
3908 */
3909Datum
3910JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
3911 bool *error, List *vars,
3912 const char *column_name)
3913{
3914 JsonbValue *singleton;
3915 bool wrap;
3916 JsonValueList found = {0};
3918 int count;
3919
3920 res = executeJsonPath(jp, vars,
3922 DatumGetJsonbP(jb), !error, &found, true);
3924 if (error && jperIsError(res))
3925 {
3926 *error = true;
3927 *empty = false;
3928 return (Datum) 0;
3929 }
3930
3931 /*
3932 * Determine whether to wrap the result in a JSON array or not.
3933 *
3934 * First, count the number of SQL/JSON items in the returned
3935 * JsonValueList. If the list is empty (singleton == NULL), no wrapping is
3936 * necessary.
3937 *
3938 * If the wrapper mode is JSW_NONE or JSW_UNSPEC, wrapping is explicitly
3939 * disabled. This enforces a WITHOUT WRAPPER clause, which is also the
3940 * default when no WRAPPER clause is specified.
3941 *
3942 * If the mode is JSW_UNCONDITIONAL, wrapping is enforced regardless of
3943 * the number of SQL/JSON items, enforcing a WITH WRAPPER or WITH
3944 * UNCONDITIONAL WRAPPER clause.
3945 *
3946 * For JSW_CONDITIONAL, wrapping occurs only if there is more than one
3947 * SQL/JSON item in the list, enforcing a WITH CONDITIONAL WRAPPER clause.
3948 */
3949 count = JsonValueListLength(&found);
3950 singleton = count > 0 ? JsonValueListHead(&found) : NULL;
3951 if (singleton == NULL)
3952 wrap = false;
3953 else if (wrapper == JSW_NONE || wrapper == JSW_UNSPEC)
3954 wrap = false;
3955 else if (wrapper == JSW_UNCONDITIONAL)
3956 wrap = true;
3957 else if (wrapper == JSW_CONDITIONAL)
3958 wrap = count > 1;
3959 else
3960 {
3961 elog(ERROR, "unrecognized json wrapper %d", (int) wrapper);
3962 wrap = false;
3963 }
3964
3965 if (wrap)
3967
3968 /* No wrapping means only one item is expected. */
3969 if (count > 1)
3970 {
3971 if (error)
3972 {
3973 *error = true;
3974 return (Datum) 0;
3975 }
3976
3977 if (column_name)
3978 ereport(ERROR,
3979 (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3980 errmsg("JSON path expression for column \"%s\" should return single item without wrapper",
3981 column_name),
3982 errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
3983 else
3984 ereport(ERROR,
3985 (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
3986 errmsg("JSON path expression in JSON_QUERY should return single item without wrapper"),
3987 errhint("Use the WITH WRAPPER clause to wrap SQL/JSON items into an array.")));
3988 }
3989
3990 if (singleton)
3991 return JsonbPGetDatum(JsonbValueToJsonb(singleton));
3992
3993 *empty = true;
3994 return PointerGetDatum(NULL);
3995}
3996
3997/*
3998 * Executor-callable JSON_VALUE implementation
3999 *
4000 * Returns NULL instead of throwing errors if 'error' is not NULL, setting
4001 * *error to true. *empty is set to true if no match is found.
4002 */
4003JsonbValue *
4004JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
4005 const char *column_name)
4006{
4007 JsonbValue *res;
4008 JsonValueList found = {0};
4010 int count;
4011
4013 DatumGetJsonbP(jb),
4014 !error, &found, true);
4015
4016 Assert(error || !jperIsError(jper));
4017
4018 if (error && jperIsError(jper))
4019 {
4020 *error = true;
4021 *empty = false;
4022 return NULL;
4023 }
4024
4025 count = JsonValueListLength(&found);
4026
4027 *empty = (count == 0);
4028
4029 if (*empty)
4030 return NULL;
4031
4032 /* JSON_VALUE expects to get only singletons. */
4033 if (count > 1)
4034 {
4035 if (error)
4036 {
4037 *error = true;
4038 return NULL;
4039 }
4040
4041 if (column_name)
4042 ereport(ERROR,
4043 (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4044 errmsg("JSON path expression for column \"%s\" should return single scalar item",
4045 column_name)));
4046 else
4047 ereport(ERROR,
4048 (errcode(ERRCODE_MORE_THAN_ONE_SQL_JSON_ITEM),
4049 errmsg("JSON path expression in JSON_VALUE should return single scalar item")));
4050 }
4051
4052 res = JsonValueListHead(&found);
4053 if (res->type == jbvBinary && JsonContainerIsScalar(res->val.binary.data))
4054 JsonbExtractScalar(res->val.binary.data, res);
4055
4056 /* JSON_VALUE expects to get only scalars. */
4057 if (!IsAJsonbScalar(res))
4058 {
4059 if (error)
4060 {
4061 *error = true;
4062 return NULL;
4063 }
4064
4065 if (column_name)
4066 ereport(ERROR,
4067 (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4068 errmsg("JSON path expression for column \"%s\" should return single scalar item",
4069 column_name)));
4070 else
4071 ereport(ERROR,
4072 (errcode(ERRCODE_SQL_JSON_SCALAR_REQUIRED),
4073 errmsg("JSON path expression in JSON_VALUE should return single scalar item")));
4074 }
4075
4076 if (res->type == jbvNull)
4077 return NULL;
4078
4079 return res;
4080}
4081
4082/************************ JSON_TABLE functions ***************************/
4083
4084/*
4085 * Sanity-checks and returns the opaque JsonTableExecContext from the
4086 * given executor state struct.
4087 */
4088static inline JsonTableExecContext *
4090{
4091 JsonTableExecContext *result;
4092
4094 elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4095 result = (JsonTableExecContext *) state->opaque;
4097 elog(ERROR, "%s called with invalid TableFuncScanState", fname);
4098
4099 return result;
4100}
4101
4102/*
4103 * JsonTableInitOpaque
4104 * Fill in TableFuncScanState->opaque for processing JSON_TABLE
4105 *
4106 * This initializes the PASSING arguments and the JsonTablePlanState for
4107 * JsonTablePlan given in TableFunc.
4108 */
4109static void
4111{
4113 PlanState *ps = &state->ss.ps;
4114 TableFuncScan *tfs = castNode(TableFuncScan, ps->plan);
4115 TableFunc *tf = tfs->tablefunc;
4116 JsonTablePlan *rootplan = (JsonTablePlan *) tf->plan;
4117 JsonExpr *je = castNode(JsonExpr, tf->docexpr);
4118 List *args = NIL;
4119
4120 cxt = palloc0(sizeof(JsonTableExecContext));
4122
4123 /*
4124 * Evaluate JSON_TABLE() PASSING arguments to be passed to the jsonpath
4125 * executor via JsonPathVariables.
4126 */
4127 if (state->passingvalexprs)
4128 {
4129 ListCell *exprlc;
4130 ListCell *namelc;
4131
4132 Assert(list_length(state->passingvalexprs) ==
4133 list_length(je->passing_names));
4134 forboth(exprlc, state->passingvalexprs,
4135 namelc, je->passing_names)
4136 {
4138 String *name = lfirst_node(String, namelc);
4139 JsonPathVariable *var = palloc(sizeof(*var));
4140
4141 var->name = pstrdup(name->sval);
4142 var->namelen = strlen(var->name);
4143 var->typid = exprType((Node *) state->expr);
4144 var->typmod = exprTypmod((Node *) state->expr);
4145
4146 /*
4147 * Evaluate the expression and save the value to be returned by
4148 * GetJsonPathVar().
4149 */
4150 var->value = ExecEvalExpr(state, ps->ps_ExprContext,
4151 &var->isnull);
4152
4153 args = lappend(args, var);
4154 }
4155 }
4156
4157 cxt->colplanstates = palloc(sizeof(JsonTablePlanState *) *
4158 list_length(tf->colvalexprs));
4159
4160 /*
4161 * Initialize plan for the root path and, recursively, also any child
4162 * plans that compute the NESTED paths.
4163 */
4164 cxt->rootplanstate = JsonTableInitPlan(cxt, rootplan, NULL, args,
4166
4167 state->opaque = cxt;
4168}
4169
4170/*
4171 * JsonTableDestroyOpaque
4172 * Resets state->opaque
4173 */
4174static void
4176{
4178 GetJsonTableExecContext(state, "JsonTableDestroyOpaque");
4179
4180 /* not valid anymore */
4181 cxt->magic = 0;
4182
4183 state->opaque = NULL;
4184}
4185
4186/*
4187 * JsonTableInitPlan
4188 * Initialize information for evaluating jsonpath in the given
4189 * JsonTablePlan and, recursively, in any child plans
4190 */
4191static JsonTablePlanState *
4193 JsonTablePlanState *parentstate,
4194 List *args, MemoryContext mcxt)
4195{
4196 JsonTablePlanState *planstate = palloc0(sizeof(*planstate));
4197
4198 planstate->plan = plan;
4199 planstate->parent = parentstate;
4200
4202 {
4204 int i;
4205
4206 planstate->path = DatumGetJsonPathP(scan->path->value->constvalue);
4207 planstate->args = args;
4208 planstate->mcxt = AllocSetContextCreate(mcxt, "JsonTableExecContext",
4210
4211 /* No row pattern evaluated yet. */
4212 planstate->current.value = PointerGetDatum(NULL);
4213 planstate->current.isnull = true;
4214
4215 for (i = scan->colMin; i >= 0 && i <= scan->colMax; i++)
4216 cxt->colplanstates[i] = planstate;
4217
4218 planstate->nested = scan->child ?
4219 JsonTableInitPlan(cxt, scan->child, planstate, args, mcxt) : NULL;
4220 }
4221 else if (IsA(plan, JsonTableSiblingJoin))
4222 {
4224
4225 planstate->left = JsonTableInitPlan(cxt, join->lplan, parentstate,
4226 args, mcxt);
4227 planstate->right = JsonTableInitPlan(cxt, join->rplan, parentstate,
4228 args, mcxt);
4229 }
4230
4231 return planstate;
4232}
4233
4234/*
4235 * JsonTableSetDocument
4236 * Install the input document and evaluate the row pattern
4237 */
4238static void
4240{
4242 GetJsonTableExecContext(state, "JsonTableSetDocument");
4243
4245}
4246
4247/*
4248 * Evaluate a JsonTablePlan's jsonpath to get a new row pattern from
4249 * the given context item
4250 */
4251static void
4253{
4254 JsonTablePathScan *scan = castNode(JsonTablePathScan, planstate->plan);
4255 MemoryContext oldcxt;
4257 Jsonb *js = (Jsonb *) DatumGetJsonbP(item);
4258
4259 JsonValueListClear(&planstate->found);
4260
4261 MemoryContextResetOnly(planstate->mcxt);
4262
4263 oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4264
4265 res = executeJsonPath(planstate->path, planstate->args,
4267 js, scan->errorOnError,
4268 &planstate->found,
4269 true);
4270
4271 MemoryContextSwitchTo(oldcxt);
4272
4273 if (jperIsError(res))
4274 {
4275 Assert(!scan->errorOnError);
4276 JsonValueListClear(&planstate->found);
4277 }
4278
4279 /* Reset plan iterator to the beginning of the item list */
4280 JsonValueListInitIterator(&planstate->found, &planstate->iter);
4281 planstate->current.value = PointerGetDatum(NULL);
4282 planstate->current.isnull = true;
4283 planstate->ordinal = 0;
4284}
4285
4286/*
4287 * Fetch next row from a JsonTablePlan.
4288 *
4289 * Returns false if the plan has run out of rows, true otherwise.
4290 */
4291static bool
4293{
4294 if (IsA(planstate->plan, JsonTablePathScan))
4295 return JsonTablePlanScanNextRow(planstate);
4296 else if (IsA(planstate->plan, JsonTableSiblingJoin))
4297 return JsonTablePlanJoinNextRow(planstate);
4298 else
4299 elog(ERROR, "invalid JsonTablePlan %d", (int) planstate->plan->type);
4300
4301 Assert(false);
4302 /* Appease compiler */
4303 return false;
4304}
4305
4306/*
4307 * Fetch next row from a JsonTablePlan's path evaluation result and from
4308 * any child nested path(s).
4309 *
4310 * Returns true if any of the paths (this or the nested) has more rows to
4311 * return.
4312 *
4313 * By fetching the nested path(s)'s rows based on the parent row at each
4314 * level, this essentially joins the rows of different levels. If a nested
4315 * path at a given level has no matching rows, the columns of that level will
4316 * compute to NULL, making it an OUTER join.
4317 */
4318static bool
4320{
4321 JsonbValue *jbv;
4322 MemoryContext oldcxt;
4323
4324 /*
4325 * If planstate already has an active row and there is a nested plan,
4326 * check if it has an active row to join with the former.
4327 */
4328 if (!planstate->current.isnull)
4329 {
4330 if (planstate->nested && JsonTablePlanNextRow(planstate->nested))
4331 return true;
4332 }
4333
4334 /* Fetch new row from the list of found values to set as active. */
4335 jbv = JsonValueListNext(&planstate->found, &planstate->iter);
4336
4337 /* End of list? */
4338 if (jbv == NULL)
4339 {
4340 planstate->current.value = PointerGetDatum(NULL);
4341 planstate->current.isnull = true;
4342 return false;
4343 }
4344
4345 /*
4346 * Set current row item for subsequent JsonTableGetValue() calls for
4347 * evaluating individual columns.
4348 */
4349 oldcxt = MemoryContextSwitchTo(planstate->mcxt);
4350 planstate->current.value = JsonbPGetDatum(JsonbValueToJsonb(jbv));
4351 planstate->current.isnull = false;
4352 MemoryContextSwitchTo(oldcxt);
4353
4354 /* Next row! */
4355 planstate->ordinal++;
4356
4357 /* Process nested plan(s), if any. */
4358 if (planstate->nested)
4359 {
4360 /* Re-evaluate the nested path using the above parent row. */
4361 JsonTableResetNestedPlan(planstate->nested);
4362
4363 /*
4364 * Now fetch the nested plan's current row to be joined against the
4365 * parent row. Any further nested plans' paths will be re-evaluated
4366 * recursively, level at a time, after setting each nested plan's
4367 * current row.
4368 */
4369 (void) JsonTablePlanNextRow(planstate->nested);
4370 }
4371
4372 /* There are more rows. */
4373 return true;
4374}
4375
4376/*
4377 * Re-evaluate the row pattern of a nested plan using the new parent row
4378 * pattern.
4379 */
4380static void
4382{
4383 /* This better be a child plan. */
4384 Assert(planstate->parent != NULL);
4385 if (IsA(planstate->plan, JsonTablePathScan))
4386 {
4387 JsonTablePlanState *parent = planstate->parent;
4388
4389 if (!parent->current.isnull)
4390 JsonTableResetRowPattern(planstate, parent->current.value);
4391
4392 /*
4393 * If this plan itself has a child nested plan, it will be reset when
4394 * the caller calls JsonTablePlanNextRow() on this plan.
4395 */
4396 }
4397 else if (IsA(planstate->plan, JsonTableSiblingJoin))
4398 {
4399 JsonTableResetNestedPlan(planstate->left);
4400 JsonTableResetNestedPlan(planstate->right);
4401 }
4402}
4403
4404/*
4405 * Fetch the next row from a JsonTableSiblingJoin.
4406 *
4407 * This is essentially a UNION between the rows from left and right siblings.
4408 */
4409static bool
4411{
4412
4413 /* Fetch row from left sibling. */
4414 if (!JsonTablePlanNextRow(planstate->left))
4415 {
4416 /*
4417 * Left sibling ran out of rows, so start fetching from the right
4418 * sibling.
4419 */
4420 if (!JsonTablePlanNextRow(planstate->right))
4421 {
4422 /* Right sibling ran out of row, so there are more rows. */
4423 return false;
4424 }
4425 }
4426
4427 return true;
4428}
4429
4430/*
4431 * JsonTableFetchRow
4432 * Prepare the next "current" row for upcoming GetValue calls.
4433 *
4434 * Returns false if no more rows can be returned.
4435 */
4436static bool
4438{
4440 GetJsonTableExecContext(state, "JsonTableFetchRow");
4441
4443}
4444
4445/*
4446 * JsonTableGetValue
4447 * Return the value for column number 'colnum' for the current row.
4448 *
4449 * This leaks memory, so be sure to reset often the context in which it's
4450 * called.
4451 */
4452static Datum
4454 Oid typid, int32 typmod, bool *isnull)
4455{
4457 GetJsonTableExecContext(state, "JsonTableGetValue");
4458 ExprContext *econtext = state->ss.ps.ps_ExprContext;
4459 ExprState *estate = list_nth(state->colvalexprs, colnum);
4460 JsonTablePlanState *planstate = cxt->colplanstates[colnum];
4461 JsonTablePlanRowSource *current = &planstate->current;
4462 Datum result;
4463
4464 /* Row pattern value is NULL */
4465 if (current->isnull)
4466 {
4467 result = (Datum) 0;
4468 *isnull = true;
4469 }
4470 /* Evaluate JsonExpr. */
4471 else if (estate)
4472 {
4473 Datum saved_caseValue = econtext->caseValue_datum;
4474 bool saved_caseIsNull = econtext->caseValue_isNull;
4475
4476 /* Pass the row pattern value via CaseTestExpr. */
4477 econtext->caseValue_datum = current->value;
4478 econtext->caseValue_isNull = false;
4479
4480 result = ExecEvalExpr(estate, econtext, isnull);
4481
4482 econtext->caseValue_datum = saved_caseValue;
4483 econtext->caseValue_isNull = saved_caseIsNull;
4484 }
4485 /* ORDINAL column */
4486 else
4487 {
4488 result = Int32GetDatum(planstate->ordinal);
4489 *isnull = false;
4490 }
4491
4492 return result;
4493}
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3381
int DetermineTimeZoneOffset(struct pg_tm *tm, pg_tz *tzp)
Definition: datetime.c:1595
void j2date(int jd, int *year, int *month, int *day)
Definition: datetime.c:321
int32 numeric_int4_opt_error(Numeric num, bool *have_error)
Definition: numeric.c:4515
Datum float8_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:4711
Datum numeric_cmp(PG_FUNCTION_ARGS)
Definition: numeric.c:2517
Numeric numeric_mod_opt_error(Numeric num1, Numeric num2, bool *have_error)
Definition: numeric.c:3486
Numeric numeric_div_opt_error(Numeric num1, Numeric num2, bool *have_error)
Definition: numeric.c:3262
Numeric int64_to_numeric(int64 val)
Definition: numeric.c:4401
Datum float4_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:4805
Datum int4_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:4507
Datum numeric_uminus(PG_FUNCTION_ARGS)
Definition: numeric.c:1420
Datum numeric_ceil(PG_FUNCTION_ARGS)
Definition: numeric.c:1647
Datum numerictypmodin(PG_FUNCTION_ARGS)
Definition: numeric.c:1324
Datum numeric_out(PG_FUNCTION_ARGS)
Definition: numeric.c:816
int64 numeric_int8_opt_error(Numeric num, bool *have_error)
Definition: numeric.c:4603
Datum numeric_trunc(PG_FUNCTION_ARGS)
Definition: numeric.c:1597
Datum numeric_in(PG_FUNCTION_ARGS)
Definition: numeric.c:637
bool numeric_is_nan(Numeric num)
Definition: numeric.c:851
Numeric numeric_sub_opt_error(Numeric num1, Numeric num2, bool *have_error)
Definition: numeric.c:3063
Numeric numeric_mul_opt_error(Numeric num1, Numeric num2, bool *have_error)
Definition: numeric.c:3141
Datum int2_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:4662
Datum numeric_abs(PG_FUNCTION_ARGS)
Definition: numeric.c:1393
Datum int8_numeric(PG_FUNCTION_ARGS)
Definition: numeric.c:4595
Numeric numeric_add_opt_error(Numeric num1, Numeric num2, bool *have_error)
Definition: numeric.c:2985
bool numeric_is_inf(Numeric num)
Definition: numeric.c:862
Datum numeric_floor(PG_FUNCTION_ARGS)
Definition: numeric.c:1675
Datum timestamp_cmp(PG_FUNCTION_ARGS)
Definition: timestamp.c:2251
bool AdjustTimestampForTypmod(Timestamp *time, int32 typmod, Node *escontext)
Definition: timestamp.c:367
Datum timestamp_timestamptz(PG_FUNCTION_ARGS)
Definition: timestamp.c:6382
int32 timestamp_cmp_timestamptz_internal(Timestamp timestampVal, TimestampTz dt2)
Definition: timestamp.c:2318
int timestamp2tm(Timestamp dt, int *tzp, struct pg_tm *tm, fsec_t *fsec, const char **tzn, pg_tz *attimezone)
Definition: timestamp.c:1891
int32 anytimestamp_typmod_check(bool istz, int32 typmod)
Definition: timestamp.c:124
Datum timestamptz_timestamp(PG_FUNCTION_ARGS)
Definition: timestamp.c:6461
static int32 next
Definition: blutils.c:219
bool parse_bool(const char *value, bool *result)
Definition: bool.c:31
#define INT64CONST(x)
Definition: c.h:502
#define Min(x, y)
Definition: c.h:961
#define PG_UINT32_MAX
Definition: c.h:547
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:204
#define Assert(condition)
Definition: c.h:815
int64_t int64
Definition: c.h:485
int32_t int32
Definition: c.h:484
uint32_t uint32
Definition: c.h:488
#define lengthof(array)
Definition: c.h:745
Oid collid
int64 Timestamp
Definition: timestamp.h:38
int64 TimestampTz
Definition: timestamp.h:39
int32 fsec_t
Definition: timestamp.h:41
#define POSTGRES_EPOCH_JDATE
Definition: timestamp.h:235
int32 date_cmp_timestamp_internal(DateADT dateVal, Timestamp dt2)
Definition: date.c:762
Datum date_cmp(PG_FUNCTION_ARGS)
Definition: date.c:444
Datum time_cmp(PG_FUNCTION_ARGS)
Definition: date.c:1753
Datum timestamp_time(PG_FUNCTION_ARGS)
Definition: date.c:1924
int32 anytime_typmod_check(bool istz, int32 typmod)
Definition: date.c:71
Datum date_timestamptz(PG_FUNCTION_ARGS)
Definition: date.c:1346
Datum timetz_cmp(PG_FUNCTION_ARGS)
Definition: date.c:2543
Datum timetz_time(PG_FUNCTION_ARGS)
Definition: date.c:2834
Datum time_timetz(PG_FUNCTION_ARGS)
Definition: date.c:2847
Datum timestamptz_timetz(PG_FUNCTION_ARGS)
Definition: date.c:2873
void AdjustTimeForTypmod(TimeADT *time, int32 typmod)
Definition: date.c:1664
Datum timestamptz_date(PG_FUNCTION_ARGS)
Definition: date.c:1361
Datum timestamp_date(PG_FUNCTION_ARGS)
Definition: date.c:1316
int32 date_cmp_timestamptz_internal(DateADT dateVal, TimestampTz dt2)
Definition: date.c:842
Datum timestamptz_time(PG_FUNCTION_ARGS)
Definition: date.c:1954
Datum date_timestamp(PG_FUNCTION_ARGS)
Definition: date.c:1302
static TimeTzADT * DatumGetTimeTzADTP(Datum X)
Definition: date.h:66
int32 DateADT
Definition: date.h:23
static DateADT DatumGetDateADT(Datum X)
Definition: date.h:54
static TimeADT DatumGetTimeADT(Datum X)
Definition: date.h:60
static Datum TimeTzADTPGetDatum(const TimeTzADT *X)
Definition: date.h:84
int64 TimeADT
Definition: date.h:25
static Datum TimeADTGetDatum(TimeADT X)
Definition: date.h:78
struct cursor * cur
Definition: ecpg.c:29
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errhint(const char *fmt,...)
Definition: elog.c:1317
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:346
float8 float8in_internal(char *num, char **endptr_p, const char *type_name, const char *orig_string, struct Node *escontext)
Definition: float.c:395
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1816
bool DirectInputFunctionCallSafe(PGFunction func, char *str, Oid typioparam, int32 typmod, fmNodePtr escontext, Datum *result)
Definition: fmgr.c:1640
#define PG_FREE_IF_COPY(ptr, n)
Definition: fmgr.h:260
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:643
#define DirectFunctionCall1(func, arg1)
Definition: fmgr.h:641
#define PG_NARGS()
Definition: fmgr.h:203
#define PG_RETURN_NULL()
Definition: fmgr.h:345
#define PG_GETARG_BOOL(n)
Definition: fmgr.h:274
Datum(* PGFunction)(FunctionCallInfo fcinfo)
Definition: fmgr.h:40
#define DatumGetTextP(X)
Definition: fmgr.h:332
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
Datum parse_datetime(text *date_txt, text *fmt, Oid collid, bool strict, Oid *typid, int32 *typmod, int *tz, Node *escontext)
Definition: formatting.c:4087
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:308
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:306
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:328
const char * str
#define MAXDATELEN
Definition: datetime.h:200
struct parser_state ps
static struct @162 value
long val
Definition: informix.c:689
Datum int8in(PG_FUNCTION_ARGS)
Definition: int8.c:50
Datum int4in(PG_FUNCTION_ARGS)
Definition: int.c:287
int b
Definition: isn.c:69
int a
Definition: isn.c:68
int i
Definition: isn.c:72
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:76
char * JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
Definition: json.c:310
Datum jsonb_in(PG_FUNCTION_ARGS)
Definition: jsonb.c:73
const char * JsonbTypeName(JsonbValue *val)
Definition: jsonb.c:180
bool JsonbExtractScalar(JsonbContainer *jbc, JsonbValue *res)
Definition: jsonb.c:1968
jbvType
Definition: jsonb.h:226
@ jbvObject
Definition: jsonb.h:234
@ jbvNumeric
Definition: jsonb.h:230
@ jbvBool
Definition: jsonb.h:231
@ jbvArray
Definition: jsonb.h:233
@ jbvBinary
Definition: jsonb.h:236
@ jbvNull
Definition: jsonb.h:228
@ jbvDatetime
Definition: jsonb.h:244
@ jbvString
Definition: jsonb.h:229
#define JsonContainerIsScalar(jc)
Definition: jsonb.h:207
#define JsonContainerIsArray(jc)
Definition: jsonb.h:209
#define JsonContainerSize(jc)
Definition: jsonb.h:206
#define PG_GETARG_JSONB_P_COPY(x)
Definition: jsonb.h:392
static Datum JsonbPGetDatum(const Jsonb *p)
Definition: jsonb.h:386
#define IsAJsonbScalar(jsonbval)
Definition: jsonb.h:297
#define PG_RETURN_JSONB_P(x)
Definition: jsonb.h:393
#define PG_GETARG_JSONB_P(x)
Definition: jsonb.h:391
#define JsonContainerIsObject(jc)
Definition: jsonb.h:208
static Jsonb * DatumGetJsonbP(Datum d)
Definition: jsonb.h:374
JsonbIteratorToken
Definition: jsonb.h:21
@ WJB_KEY
Definition: jsonb.h:23
@ WJB_DONE
Definition: jsonb.h:22
@ WJB_END_ARRAY
Definition: jsonb.h:27
@ WJB_VALUE
Definition: jsonb.h:24
@ WJB_END_OBJECT
Definition: jsonb.h:29
@ WJB_ELEM
Definition: jsonb.h:25
@ WJB_BEGIN_OBJECT
Definition: jsonb.h:28
@ WJB_BEGIN_ARRAY
Definition: jsonb.h:26
#define JB_FOBJECT
Definition: jsonb.h:202
JsonbValue * pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq, JsonbValue *jbval)
Definition: jsonb_util.c:573
JsonbIterator * JsonbIteratorInit(JsonbContainer *container)
Definition: jsonb_util.c:824
JsonbValue * findJsonbValueFromContainer(JsonbContainer *container, uint32 flags, JsonbValue *key)
Definition: jsonb_util.c:351
JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val, bool skipNested)
Definition: jsonb_util.c:860
JsonbValue * getIthJsonbValueFromContainer(JsonbContainer *container, uint32 i)
Definition: jsonb_util.c:475
Jsonb * JsonbValueToJsonb(JsonbValue *val)
Definition: jsonb_util.c:92
void jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1166
void jspGetArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1081
void jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
Definition: jsonpath.c:990
bool jspGetBool(JsonPathItem *v)
Definition: jsonpath.c:1210
void jspInit(JsonPathItem *v, JsonPath *js)
Definition: jsonpath.c:980
char * jspGetString(JsonPathItem *v, int32 *len)
Definition: jsonpath.c:1226
Numeric jspGetNumeric(JsonPathItem *v)
Definition: jsonpath.c:1218
bool jspGetArraySubscript(JsonPathItem *v, JsonPathItem *from, JsonPathItem *to, int i)
Definition: jsonpath.c:1238
const char * jspOperationName(JsonPathItemType type)
Definition: jsonpath.c:843
bool jspGetNext(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1099
void jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
Definition: jsonpath.c:1188
bool jspConvertRegexFlags(uint32 xflags, int *result, struct Node *escontext)
#define jspHasNext(jsp)
Definition: jsonpath.h:194
#define PG_GETARG_JSONPATH_P(x)
Definition: jsonpath.h:46
#define PG_GETARG_JSONPATH_P_COPY(x)
Definition: jsonpath.h:47
static JsonPath * DatumGetJsonPathP(Datum d)
Definition: jsonpath.h:35
@ jpiAdd
Definition: jsonpath.h:78
@ jpiString
Definition: jsonpath.h:65
@ jpiAbs
Definition: jsonpath.h:97
@ jpiIndexArray
Definition: jsonpath.h:87
@ jpiAny
Definition: jsonpath.h:88
@ jpiDatetime
Definition: jsonpath.h:101
@ jpiBigint
Definition: jsonpath.h:107
@ jpiBool
Definition: jsonpath.h:67
@ jpiType
Definition: jsonpath.h:95
@ jpiFloor
Definition: jsonpath.h:98
@ jpiAnyArray
Definition: jsonpath.h:85
@ jpiExists
Definition: jsonpath.h:94
@ jpiSize
Definition: jsonpath.h:96
@ jpiSub
Definition: jsonpath.h:79
@ jpiNotEqual
Definition: jsonpath.h:73
@ jpiMul
Definition: jsonpath.h:80
@ jpiVariable
Definition: jsonpath.h:92
@ jpiTimeTz
Definition: jsonpath.h:115
@ jpiNot
Definition: jsonpath.h:70
@ jpiDate
Definition: jsonpath.h:109
@ jpiGreaterOrEqual
Definition: jsonpath.h:77
@ jpiPlus
Definition: jsonpath.h:83
@ jpiDouble
Definition: jsonpath.h:100
@ jpiGreater
Definition: jsonpath.h:75
@ jpiNumber
Definition: jsonpath.h:112
@ jpiAnd
Definition: jsonpath.h:68
@ jpiStartsWith
Definition: jsonpath.h:105
@ jpiOr
Definition: jsonpath.h:69
@ jpiMod
Definition: jsonpath.h:82
@ jpiTimestamp
Definition: jsonpath.h:116
@ jpiLikeRegex
Definition: jsonpath.h:106
@ jpiTimestampTz
Definition: jsonpath.h:117
@ jpiInteger