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