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