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-2020, 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 less 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 =
846 					DatumGetNumeric(DirectFunctionCall1(int4_numeric,
847 														Int32GetDatum(last)));
848 
849 				res = executeNextItem(cxt, jsp, &elem,
850 									  lastjbv, found, hasNext);
851 			}
852 			break;
853 
854 		case jpiAnyKey:
855 			if (JsonbType(jb) == jbvObject)
856 			{
857 				bool		hasNext = jspGetNext(jsp, &elem);
858 
859 				if (jb->type != jbvBinary)
860 					elog(ERROR, "invalid jsonb object type: %d", jb->type);
861 
862 				return executeAnyItem
863 					(cxt, hasNext ? &elem : NULL,
864 					 jb->val.binary.data, found, 1, 1, 1,
865 					 false, jspAutoUnwrap(cxt));
866 			}
867 			else if (unwrap && JsonbType(jb) == jbvArray)
868 				return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
869 			else if (!jspIgnoreStructuralErrors(cxt))
870 			{
871 				Assert(found);
872 				RETURN_ERROR(ereport(ERROR,
873 									 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
874 									  errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
875 			}
876 			break;
877 
878 		case jpiAdd:
879 			return executeBinaryArithmExpr(cxt, jsp, jb,
880 										   numeric_add_opt_error, found);
881 
882 		case jpiSub:
883 			return executeBinaryArithmExpr(cxt, jsp, jb,
884 										   numeric_sub_opt_error, found);
885 
886 		case jpiMul:
887 			return executeBinaryArithmExpr(cxt, jsp, jb,
888 										   numeric_mul_opt_error, found);
889 
890 		case jpiDiv:
891 			return executeBinaryArithmExpr(cxt, jsp, jb,
892 										   numeric_div_opt_error, found);
893 
894 		case jpiMod:
895 			return executeBinaryArithmExpr(cxt, jsp, jb,
896 										   numeric_mod_opt_error, found);
897 
898 		case jpiPlus:
899 			return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
900 
901 		case jpiMinus:
902 			return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
903 										  found);
904 
905 		case jpiFilter:
906 			{
907 				JsonPathBool st;
908 
909 				if (unwrap && JsonbType(jb) == jbvArray)
910 					return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
911 														false);
912 
913 				jspGetArg(jsp, &elem);
914 				st = executeNestedBoolItem(cxt, &elem, jb);
915 				if (st != jpbTrue)
916 					res = jperNotFound;
917 				else
918 					res = executeNextItem(cxt, jsp, NULL,
919 										  jb, found, true);
920 				break;
921 			}
922 
923 		case jpiAny:
924 			{
925 				bool		hasNext = jspGetNext(jsp, &elem);
926 
927 				/* first try without any intermediate steps */
928 				if (jsp->content.anybounds.first == 0)
929 				{
930 					bool		savedIgnoreStructuralErrors;
931 
932 					savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
933 					cxt->ignoreStructuralErrors = true;
934 					res = executeNextItem(cxt, jsp, &elem,
935 										  jb, found, true);
936 					cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
937 
938 					if (res == jperOk && !found)
939 						break;
940 				}
941 
942 				if (jb->type == jbvBinary)
943 					res = executeAnyItem
944 						(cxt, hasNext ? &elem : NULL,
945 						 jb->val.binary.data, found,
946 						 1,
947 						 jsp->content.anybounds.first,
948 						 jsp->content.anybounds.last,
949 						 true, jspAutoUnwrap(cxt));
950 				break;
951 			}
952 
953 		case jpiNull:
954 		case jpiBool:
955 		case jpiNumeric:
956 		case jpiString:
957 		case jpiVariable:
958 			{
959 				JsonbValue	vbuf;
960 				JsonbValue *v;
961 				bool		hasNext = jspGetNext(jsp, &elem);
962 
963 				if (!hasNext && !found)
964 				{
965 					res = jperOk;	/* skip evaluation */
966 					break;
967 				}
968 
969 				v = hasNext ? &vbuf : palloc(sizeof(*v));
970 
971 				baseObject = cxt->baseObject;
972 				getJsonPathItem(cxt, jsp, v);
973 
974 				res = executeNextItem(cxt, jsp, &elem,
975 									  v, found, hasNext);
976 				cxt->baseObject = baseObject;
977 			}
978 			break;
979 
980 		case jpiType:
981 			{
982 				JsonbValue *jbv = palloc(sizeof(*jbv));
983 
984 				jbv->type = jbvString;
985 				jbv->val.string.val = pstrdup(JsonbTypeName(jb));
986 				jbv->val.string.len = strlen(jbv->val.string.val);
987 
988 				res = executeNextItem(cxt, jsp, NULL, jbv,
989 									  found, false);
990 			}
991 			break;
992 
993 		case jpiSize:
994 			{
995 				int			size = JsonbArraySize(jb);
996 
997 				if (size < 0)
998 				{
999 					if (!jspAutoWrap(cxt))
1000 					{
1001 						if (!jspIgnoreStructuralErrors(cxt))
1002 							RETURN_ERROR(ereport(ERROR,
1003 												 (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
1004 												  errmsg("jsonpath item method .%s() can only be applied to an array",
1005 														 jspOperationName(jsp->type)))));
1006 						break;
1007 					}
1008 
1009 					size = 1;
1010 				}
1011 
1012 				jb = palloc(sizeof(*jb));
1013 
1014 				jb->type = jbvNumeric;
1015 				jb->val.numeric =
1016 					DatumGetNumeric(DirectFunctionCall1(int4_numeric,
1017 														Int32GetDatum(size)));
1018 
1019 				res = executeNextItem(cxt, jsp, NULL, jb, found, false);
1020 			}
1021 			break;
1022 
1023 		case jpiAbs:
1024 			return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
1025 											found);
1026 
1027 		case jpiFloor:
1028 			return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
1029 											found);
1030 
1031 		case jpiCeiling:
1032 			return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
1033 											found);
1034 
1035 		case jpiDouble:
1036 			{
1037 				JsonbValue	jbv;
1038 
1039 				if (unwrap && JsonbType(jb) == jbvArray)
1040 					return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
1041 														false);
1042 
1043 				if (jb->type == jbvNumeric)
1044 				{
1045 					char	   *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
1046 																		  NumericGetDatum(jb->val.numeric)));
1047 					double		val;
1048 					bool		have_error = false;
1049 
1050 					val = float8in_internal_opt_error(tmp,
1051 													  NULL,
1052 													  "double precision",
1053 													  tmp,
1054 													  &have_error);
1055 
1056 					if (have_error || isinf(val) || isnan(val))
1057 						RETURN_ERROR(ereport(ERROR,
1058 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1059 											  errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
1060 													 jspOperationName(jsp->type)))));
1061 					res = jperOk;
1062 				}
1063 				else if (jb->type == jbvString)
1064 				{
1065 					/* cast string as double */
1066 					double		val;
1067 					char	   *tmp = pnstrdup(jb->val.string.val,
1068 											   jb->val.string.len);
1069 					bool		have_error = false;
1070 
1071 					val = float8in_internal_opt_error(tmp,
1072 													  NULL,
1073 													  "double precision",
1074 													  tmp,
1075 													  &have_error);
1076 
1077 					if (have_error || isinf(val) || isnan(val))
1078 						RETURN_ERROR(ereport(ERROR,
1079 											 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1080 											  errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
1081 													 jspOperationName(jsp->type)))));
1082 
1083 					jb = &jbv;
1084 					jb->type = jbvNumeric;
1085 					jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(float8_numeric,
1086 																		  Float8GetDatum(val)));
1087 					res = jperOk;
1088 				}
1089 
1090 				if (res == jperNotFound)
1091 					RETURN_ERROR(ereport(ERROR,
1092 										 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1093 										  errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
1094 												 jspOperationName(jsp->type)))));
1095 
1096 				res = executeNextItem(cxt, jsp, NULL, jb, found, true);
1097 			}
1098 			break;
1099 
1100 		case jpiDatetime:
1101 			if (unwrap && JsonbType(jb) == jbvArray)
1102 				return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1103 
1104 			return executeDateTimeMethod(cxt, jsp, jb, found);
1105 
1106 		case jpiKeyValue:
1107 			if (unwrap && JsonbType(jb) == jbvArray)
1108 				return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1109 
1110 			return executeKeyValueMethod(cxt, jsp, jb, found);
1111 
1112 		default:
1113 			elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
1114 	}
1115 
1116 	return res;
1117 }
1118 
1119 /*
1120  * Unwrap current array item and execute jsonpath for each of its elements.
1121  */
1122 static JsonPathExecResult
executeItemUnwrapTargetArray(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,JsonValueList * found,bool unwrapElements)1123 executeItemUnwrapTargetArray(JsonPathExecContext *cxt, JsonPathItem *jsp,
1124 							 JsonbValue *jb, JsonValueList *found,
1125 							 bool unwrapElements)
1126 {
1127 	if (jb->type != jbvBinary)
1128 	{
1129 		Assert(jb->type != jbvArray);
1130 		elog(ERROR, "invalid jsonb array value type: %d", jb->type);
1131 	}
1132 
1133 	return executeAnyItem
1134 		(cxt, jsp, jb->val.binary.data, found, 1, 1, 1,
1135 		 false, unwrapElements);
1136 }
1137 
1138 /*
1139  * Execute next jsonpath item if exists.  Otherwise put "v" to the "found"
1140  * list if provided.
1141  */
1142 static JsonPathExecResult
executeNextItem(JsonPathExecContext * cxt,JsonPathItem * cur,JsonPathItem * next,JsonbValue * v,JsonValueList * found,bool copy)1143 executeNextItem(JsonPathExecContext *cxt,
1144 				JsonPathItem *cur, JsonPathItem *next,
1145 				JsonbValue *v, JsonValueList *found, bool copy)
1146 {
1147 	JsonPathItem elem;
1148 	bool		hasNext;
1149 
1150 	if (!cur)
1151 		hasNext = next != NULL;
1152 	else if (next)
1153 		hasNext = jspHasNext(cur);
1154 	else
1155 	{
1156 		next = &elem;
1157 		hasNext = jspGetNext(cur, next);
1158 	}
1159 
1160 	if (hasNext)
1161 		return executeItem(cxt, next, v, found);
1162 
1163 	if (found)
1164 		JsonValueListAppend(found, copy ? copyJsonbValue(v) : v);
1165 
1166 	return jperOk;
1167 }
1168 
1169 /*
1170  * Same as executeItem(), but when "unwrap == true" automatically unwraps
1171  * each array item from the resulting sequence in lax mode.
1172  */
1173 static JsonPathExecResult
executeItemOptUnwrapResult(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool unwrap,JsonValueList * found)1174 executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
1175 						   JsonbValue *jb, bool unwrap,
1176 						   JsonValueList *found)
1177 {
1178 	if (unwrap && jspAutoUnwrap(cxt))
1179 	{
1180 		JsonValueList seq = {0};
1181 		JsonValueListIterator it;
1182 		JsonPathExecResult res = executeItem(cxt, jsp, jb, &seq);
1183 		JsonbValue *item;
1184 
1185 		if (jperIsError(res))
1186 			return res;
1187 
1188 		JsonValueListInitIterator(&seq, &it);
1189 		while ((item = JsonValueListNext(&seq, &it)))
1190 		{
1191 			Assert(item->type != jbvArray);
1192 
1193 			if (JsonbType(item) == jbvArray)
1194 				executeItemUnwrapTargetArray(cxt, NULL, item, found, false);
1195 			else
1196 				JsonValueListAppend(found, item);
1197 		}
1198 
1199 		return jperOk;
1200 	}
1201 
1202 	return executeItem(cxt, jsp, jb, found);
1203 }
1204 
1205 /*
1206  * Same as executeItemOptUnwrapResult(), but with error suppression.
1207  */
1208 static JsonPathExecResult
executeItemOptUnwrapResultNoThrow(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool unwrap,JsonValueList * found)1209 executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt,
1210 								  JsonPathItem *jsp,
1211 								  JsonbValue *jb, bool unwrap,
1212 								  JsonValueList *found)
1213 {
1214 	JsonPathExecResult res;
1215 	bool		throwErrors = cxt->throwErrors;
1216 
1217 	cxt->throwErrors = false;
1218 	res = executeItemOptUnwrapResult(cxt, jsp, jb, unwrap, found);
1219 	cxt->throwErrors = throwErrors;
1220 
1221 	return res;
1222 }
1223 
1224 /* Execute boolean-valued jsonpath expression. */
1225 static JsonPathBool
executeBoolItem(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool canHaveNext)1226 executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1227 				JsonbValue *jb, bool canHaveNext)
1228 {
1229 	JsonPathItem larg;
1230 	JsonPathItem rarg;
1231 	JsonPathBool res;
1232 	JsonPathBool res2;
1233 
1234 	if (!canHaveNext && jspHasNext(jsp))
1235 		elog(ERROR, "boolean jsonpath item cannot have next item");
1236 
1237 	switch (jsp->type)
1238 	{
1239 		case jpiAnd:
1240 			jspGetLeftArg(jsp, &larg);
1241 			res = executeBoolItem(cxt, &larg, jb, false);
1242 
1243 			if (res == jpbFalse)
1244 				return jpbFalse;
1245 
1246 			/*
1247 			 * SQL/JSON says that we should check second arg in case of
1248 			 * jperError
1249 			 */
1250 
1251 			jspGetRightArg(jsp, &rarg);
1252 			res2 = executeBoolItem(cxt, &rarg, jb, false);
1253 
1254 			return res2 == jpbTrue ? res : res2;
1255 
1256 		case jpiOr:
1257 			jspGetLeftArg(jsp, &larg);
1258 			res = executeBoolItem(cxt, &larg, jb, false);
1259 
1260 			if (res == jpbTrue)
1261 				return jpbTrue;
1262 
1263 			jspGetRightArg(jsp, &rarg);
1264 			res2 = executeBoolItem(cxt, &rarg, jb, false);
1265 
1266 			return res2 == jpbFalse ? res : res2;
1267 
1268 		case jpiNot:
1269 			jspGetArg(jsp, &larg);
1270 
1271 			res = executeBoolItem(cxt, &larg, jb, false);
1272 
1273 			if (res == jpbUnknown)
1274 				return jpbUnknown;
1275 
1276 			return res == jpbTrue ? jpbFalse : jpbTrue;
1277 
1278 		case jpiIsUnknown:
1279 			jspGetArg(jsp, &larg);
1280 			res = executeBoolItem(cxt, &larg, jb, false);
1281 			return res == jpbUnknown ? jpbTrue : jpbFalse;
1282 
1283 		case jpiEqual:
1284 		case jpiNotEqual:
1285 		case jpiLess:
1286 		case jpiGreater:
1287 		case jpiLessOrEqual:
1288 		case jpiGreaterOrEqual:
1289 			jspGetLeftArg(jsp, &larg);
1290 			jspGetRightArg(jsp, &rarg);
1291 			return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
1292 									executeComparison, cxt);
1293 
1294 		case jpiStartsWith:		/* 'whole STARTS WITH initial' */
1295 			jspGetLeftArg(jsp, &larg);	/* 'whole' */
1296 			jspGetRightArg(jsp, &rarg); /* 'initial' */
1297 			return executePredicate(cxt, jsp, &larg, &rarg, jb, false,
1298 									executeStartsWith, NULL);
1299 
1300 		case jpiLikeRegex:		/* 'expr LIKE_REGEX pattern FLAGS flags' */
1301 			{
1302 				/*
1303 				 * 'expr' is a sequence-returning expression.  'pattern' is a
1304 				 * regex string literal.  SQL/JSON standard requires XQuery
1305 				 * regexes, but we use Postgres regexes here.  'flags' is a
1306 				 * string literal converted to integer flags at compile-time.
1307 				 */
1308 				JsonLikeRegexContext lrcxt = {0};
1309 
1310 				jspInitByBuffer(&larg, jsp->base,
1311 								jsp->content.like_regex.expr);
1312 
1313 				return executePredicate(cxt, jsp, &larg, NULL, jb, false,
1314 										executeLikeRegex, &lrcxt);
1315 			}
1316 
1317 		case jpiExists:
1318 			jspGetArg(jsp, &larg);
1319 
1320 			if (jspStrictAbsenseOfErrors(cxt))
1321 			{
1322 				/*
1323 				 * In strict mode we must get a complete list of values to
1324 				 * check that there are no errors at all.
1325 				 */
1326 				JsonValueList vals = {0};
1327 				JsonPathExecResult res =
1328 				executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1329 												  false, &vals);
1330 
1331 				if (jperIsError(res))
1332 					return jpbUnknown;
1333 
1334 				return JsonValueListIsEmpty(&vals) ? jpbFalse : jpbTrue;
1335 			}
1336 			else
1337 			{
1338 				JsonPathExecResult res =
1339 				executeItemOptUnwrapResultNoThrow(cxt, &larg, jb,
1340 												  false, NULL);
1341 
1342 				if (jperIsError(res))
1343 					return jpbUnknown;
1344 
1345 				return res == jperOk ? jpbTrue : jpbFalse;
1346 			}
1347 
1348 		default:
1349 			elog(ERROR, "invalid boolean jsonpath item type: %d", jsp->type);
1350 			return jpbUnknown;
1351 	}
1352 }
1353 
1354 /*
1355  * Execute nested (filters etc.) boolean expression pushing current SQL/JSON
1356  * item onto the stack.
1357  */
1358 static JsonPathBool
executeNestedBoolItem(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb)1359 executeNestedBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
1360 					  JsonbValue *jb)
1361 {
1362 	JsonbValue *prev;
1363 	JsonPathBool res;
1364 
1365 	prev = cxt->current;
1366 	cxt->current = jb;
1367 	res = executeBoolItem(cxt, jsp, jb, false);
1368 	cxt->current = prev;
1369 
1370 	return res;
1371 }
1372 
1373 /*
1374  * Implementation of several jsonpath nodes:
1375  *  - jpiAny (.** accessor),
1376  *  - jpiAnyKey (.* accessor),
1377  *  - jpiAnyArray ([*] accessor)
1378  */
1379 static JsonPathExecResult
executeAnyItem(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbContainer * jbc,JsonValueList * found,uint32 level,uint32 first,uint32 last,bool ignoreStructuralErrors,bool unwrapNext)1380 executeAnyItem(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbContainer *jbc,
1381 			   JsonValueList *found, uint32 level, uint32 first, uint32 last,
1382 			   bool ignoreStructuralErrors, bool unwrapNext)
1383 {
1384 	JsonPathExecResult res = jperNotFound;
1385 	JsonbIterator *it;
1386 	int32		r;
1387 	JsonbValue	v;
1388 
1389 	check_stack_depth();
1390 
1391 	if (level > last)
1392 		return res;
1393 
1394 	it = JsonbIteratorInit(jbc);
1395 
1396 	/*
1397 	 * Recursively iterate over jsonb objects/arrays
1398 	 */
1399 	while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
1400 	{
1401 		if (r == WJB_KEY)
1402 		{
1403 			r = JsonbIteratorNext(&it, &v, true);
1404 			Assert(r == WJB_VALUE);
1405 		}
1406 
1407 		if (r == WJB_VALUE || r == WJB_ELEM)
1408 		{
1409 
1410 			if (level >= first ||
1411 				(first == PG_UINT32_MAX && last == PG_UINT32_MAX &&
1412 				 v.type != jbvBinary))	/* leaves only requested */
1413 			{
1414 				/* check expression */
1415 				if (jsp)
1416 				{
1417 					if (ignoreStructuralErrors)
1418 					{
1419 						bool		savedIgnoreStructuralErrors;
1420 
1421 						savedIgnoreStructuralErrors = cxt->ignoreStructuralErrors;
1422 						cxt->ignoreStructuralErrors = true;
1423 						res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1424 						cxt->ignoreStructuralErrors = savedIgnoreStructuralErrors;
1425 					}
1426 					else
1427 						res = executeItemOptUnwrapTarget(cxt, jsp, &v, found, unwrapNext);
1428 
1429 					if (jperIsError(res))
1430 						break;
1431 
1432 					if (res == jperOk && !found)
1433 						break;
1434 				}
1435 				else if (found)
1436 					JsonValueListAppend(found, copyJsonbValue(&v));
1437 				else
1438 					return jperOk;
1439 			}
1440 
1441 			if (level < last && v.type == jbvBinary)
1442 			{
1443 				res = executeAnyItem
1444 					(cxt, jsp, v.val.binary.data, found,
1445 					 level + 1, first, last,
1446 					 ignoreStructuralErrors, unwrapNext);
1447 
1448 				if (jperIsError(res))
1449 					break;
1450 
1451 				if (res == jperOk && found == NULL)
1452 					break;
1453 			}
1454 		}
1455 	}
1456 
1457 	return res;
1458 }
1459 
1460 /*
1461  * Execute unary or binary predicate.
1462  *
1463  * Predicates have existence semantics, because their operands are item
1464  * sequences.  Pairs of items from the left and right operand's sequences are
1465  * checked.  TRUE returned only if any pair satisfying the condition is found.
1466  * In strict mode, even if the desired pair has already been found, all pairs
1467  * still need to be examined to check the absence of errors.  If any error
1468  * occurs, UNKNOWN (analogous to SQL NULL) is returned.
1469  */
1470 static JsonPathBool
executePredicate(JsonPathExecContext * cxt,JsonPathItem * pred,JsonPathItem * larg,JsonPathItem * rarg,JsonbValue * jb,bool unwrapRightArg,JsonPathPredicateCallback exec,void * param)1471 executePredicate(JsonPathExecContext *cxt, JsonPathItem *pred,
1472 				 JsonPathItem *larg, JsonPathItem *rarg, JsonbValue *jb,
1473 				 bool unwrapRightArg, JsonPathPredicateCallback exec,
1474 				 void *param)
1475 {
1476 	JsonPathExecResult res;
1477 	JsonValueListIterator lseqit;
1478 	JsonValueList lseq = {0};
1479 	JsonValueList rseq = {0};
1480 	JsonbValue *lval;
1481 	bool		error = false;
1482 	bool		found = false;
1483 
1484 	/* Left argument is always auto-unwrapped. */
1485 	res = executeItemOptUnwrapResultNoThrow(cxt, larg, jb, true, &lseq);
1486 	if (jperIsError(res))
1487 		return jpbUnknown;
1488 
1489 	if (rarg)
1490 	{
1491 		/* Right argument is conditionally auto-unwrapped. */
1492 		res = executeItemOptUnwrapResultNoThrow(cxt, rarg, jb,
1493 												unwrapRightArg, &rseq);
1494 		if (jperIsError(res))
1495 			return jpbUnknown;
1496 	}
1497 
1498 	JsonValueListInitIterator(&lseq, &lseqit);
1499 	while ((lval = JsonValueListNext(&lseq, &lseqit)))
1500 	{
1501 		JsonValueListIterator rseqit;
1502 		JsonbValue *rval;
1503 		bool		first = true;
1504 
1505 		JsonValueListInitIterator(&rseq, &rseqit);
1506 		if (rarg)
1507 			rval = JsonValueListNext(&rseq, &rseqit);
1508 		else
1509 			rval = NULL;
1510 
1511 		/* Loop over right arg sequence or do single pass otherwise */
1512 		while (rarg ? (rval != NULL) : first)
1513 		{
1514 			JsonPathBool res = exec(pred, lval, rval, param);
1515 
1516 			if (res == jpbUnknown)
1517 			{
1518 				if (jspStrictAbsenseOfErrors(cxt))
1519 					return jpbUnknown;
1520 
1521 				error = true;
1522 			}
1523 			else if (res == jpbTrue)
1524 			{
1525 				if (!jspStrictAbsenseOfErrors(cxt))
1526 					return jpbTrue;
1527 
1528 				found = true;
1529 			}
1530 
1531 			first = false;
1532 			if (rarg)
1533 				rval = JsonValueListNext(&rseq, &rseqit);
1534 		}
1535 	}
1536 
1537 	if (found)					/* possible only in strict mode */
1538 		return jpbTrue;
1539 
1540 	if (error)					/* possible only in lax mode */
1541 		return jpbUnknown;
1542 
1543 	return jpbFalse;
1544 }
1545 
1546 /*
1547  * Execute binary arithmetic expression on singleton numeric operands.
1548  * Array operands are automatically unwrapped in lax mode.
1549  */
1550 static JsonPathExecResult
executeBinaryArithmExpr(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,BinaryArithmFunc func,JsonValueList * found)1551 executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1552 						JsonbValue *jb, BinaryArithmFunc func,
1553 						JsonValueList *found)
1554 {
1555 	JsonPathExecResult jper;
1556 	JsonPathItem elem;
1557 	JsonValueList lseq = {0};
1558 	JsonValueList rseq = {0};
1559 	JsonbValue *lval;
1560 	JsonbValue *rval;
1561 	Numeric		res;
1562 
1563 	jspGetLeftArg(jsp, &elem);
1564 
1565 	/*
1566 	 * XXX: By standard only operands of multiplicative expressions are
1567 	 * unwrapped.  We extend it to other binary arithmetic expressions too.
1568 	 */
1569 	jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
1570 	if (jperIsError(jper))
1571 		return jper;
1572 
1573 	jspGetRightArg(jsp, &elem);
1574 
1575 	jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &rseq);
1576 	if (jperIsError(jper))
1577 		return jper;
1578 
1579 	if (JsonValueListLength(&lseq) != 1 ||
1580 		!(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
1581 		RETURN_ERROR(ereport(ERROR,
1582 							 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1583 							  errmsg("left operand of jsonpath operator %s is not a single numeric value",
1584 									 jspOperationName(jsp->type)))));
1585 
1586 	if (JsonValueListLength(&rseq) != 1 ||
1587 		!(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
1588 		RETURN_ERROR(ereport(ERROR,
1589 							 (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
1590 							  errmsg("right operand of jsonpath operator %s is not a single numeric value",
1591 									 jspOperationName(jsp->type)))));
1592 
1593 	if (jspThrowErrors(cxt))
1594 	{
1595 		res = func(lval->val.numeric, rval->val.numeric, NULL);
1596 	}
1597 	else
1598 	{
1599 		bool		error = false;
1600 
1601 		res = func(lval->val.numeric, rval->val.numeric, &error);
1602 
1603 		if (error)
1604 			return jperError;
1605 	}
1606 
1607 	if (!jspGetNext(jsp, &elem) && !found)
1608 		return jperOk;
1609 
1610 	lval = palloc(sizeof(*lval));
1611 	lval->type = jbvNumeric;
1612 	lval->val.numeric = res;
1613 
1614 	return executeNextItem(cxt, jsp, &elem, lval, found, false);
1615 }
1616 
1617 /*
1618  * Execute unary arithmetic expression for each numeric item in its operand's
1619  * sequence.  Array operand is automatically unwrapped in lax mode.
1620  */
1621 static JsonPathExecResult
executeUnaryArithmExpr(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,PGFunction func,JsonValueList * found)1622 executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
1623 					   JsonbValue *jb, PGFunction func, JsonValueList *found)
1624 {
1625 	JsonPathExecResult jper;
1626 	JsonPathExecResult jper2;
1627 	JsonPathItem elem;
1628 	JsonValueList seq = {0};
1629 	JsonValueListIterator it;
1630 	JsonbValue *val;
1631 	bool		hasNext;
1632 
1633 	jspGetArg(jsp, &elem);
1634 	jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &seq);
1635 
1636 	if (jperIsError(jper))
1637 		return jper;
1638 
1639 	jper = jperNotFound;
1640 
1641 	hasNext = jspGetNext(jsp, &elem);
1642 
1643 	JsonValueListInitIterator(&seq, &it);
1644 	while ((val = JsonValueListNext(&seq, &it)))
1645 	{
1646 		if ((val = getScalar(val, jbvNumeric)))
1647 		{
1648 			if (!found && !hasNext)
1649 				return jperOk;
1650 		}
1651 		else
1652 		{
1653 			if (!found && !hasNext)
1654 				continue;		/* skip non-numerics processing */
1655 
1656 			RETURN_ERROR(ereport(ERROR,
1657 								 (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
1658 								  errmsg("operand of unary jsonpath operator %s is not a numeric value",
1659 										 jspOperationName(jsp->type)))));
1660 		}
1661 
1662 		if (func)
1663 			val->val.numeric =
1664 				DatumGetNumeric(DirectFunctionCall1(func,
1665 													NumericGetDatum(val->val.numeric)));
1666 
1667 		jper2 = executeNextItem(cxt, jsp, &elem, val, found, false);
1668 
1669 		if (jperIsError(jper2))
1670 			return jper2;
1671 
1672 		if (jper2 == jperOk)
1673 		{
1674 			if (!found)
1675 				return jperOk;
1676 			jper = jperOk;
1677 		}
1678 	}
1679 
1680 	return jper;
1681 }
1682 
1683 /*
1684  * STARTS_WITH predicate callback.
1685  *
1686  * Check if the 'whole' string starts from 'initial' string.
1687  */
1688 static JsonPathBool
executeStartsWith(JsonPathItem * jsp,JsonbValue * whole,JsonbValue * initial,void * param)1689 executeStartsWith(JsonPathItem *jsp, JsonbValue *whole, JsonbValue *initial,
1690 				  void *param)
1691 {
1692 	if (!(whole = getScalar(whole, jbvString)))
1693 		return jpbUnknown;		/* error */
1694 
1695 	if (!(initial = getScalar(initial, jbvString)))
1696 		return jpbUnknown;		/* error */
1697 
1698 	if (whole->val.string.len >= initial->val.string.len &&
1699 		!memcmp(whole->val.string.val,
1700 				initial->val.string.val,
1701 				initial->val.string.len))
1702 		return jpbTrue;
1703 
1704 	return jpbFalse;
1705 }
1706 
1707 /*
1708  * LIKE_REGEX predicate callback.
1709  *
1710  * Check if the string matches regex pattern.
1711  */
1712 static JsonPathBool
executeLikeRegex(JsonPathItem * jsp,JsonbValue * str,JsonbValue * rarg,void * param)1713 executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
1714 				 void *param)
1715 {
1716 	JsonLikeRegexContext *cxt = param;
1717 
1718 	if (!(str = getScalar(str, jbvString)))
1719 		return jpbUnknown;
1720 
1721 	/* Cache regex text and converted flags. */
1722 	if (!cxt->regex)
1723 	{
1724 		cxt->regex =
1725 			cstring_to_text_with_len(jsp->content.like_regex.pattern,
1726 									 jsp->content.like_regex.patternlen);
1727 		cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
1728 	}
1729 
1730 	if (RE_compile_and_execute(cxt->regex, str->val.string.val,
1731 							   str->val.string.len,
1732 							   cxt->cflags, DEFAULT_COLLATION_OID, 0, NULL))
1733 		return jpbTrue;
1734 
1735 	return jpbFalse;
1736 }
1737 
1738 /*
1739  * Execute numeric item methods (.abs(), .floor(), .ceil()) using the specified
1740  * user function 'func'.
1741  */
1742 static JsonPathExecResult
executeNumericItemMethod(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,bool unwrap,PGFunction func,JsonValueList * found)1743 executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1744 						 JsonbValue *jb, bool unwrap, PGFunction func,
1745 						 JsonValueList *found)
1746 {
1747 	JsonPathItem next;
1748 	Datum		datum;
1749 
1750 	if (unwrap && JsonbType(jb) == jbvArray)
1751 		return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
1752 
1753 	if (!(jb = getScalar(jb, jbvNumeric)))
1754 		RETURN_ERROR(ereport(ERROR,
1755 							 (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1756 							  errmsg("jsonpath item method .%s() can only be applied to a numeric value",
1757 									 jspOperationName(jsp->type)))));
1758 
1759 	datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
1760 
1761 	if (!jspGetNext(jsp, &next) && !found)
1762 		return jperOk;
1763 
1764 	jb = palloc(sizeof(*jb));
1765 	jb->type = jbvNumeric;
1766 	jb->val.numeric = DatumGetNumeric(datum);
1767 
1768 	return executeNextItem(cxt, jsp, &next, jb, found, false);
1769 }
1770 
1771 /*
1772  * Implementation of the .datetime() method.
1773  *
1774  * Converts a string into a date/time value. The actual type is determined at run time.
1775  * If an argument is provided, this argument is used as a template string.
1776  * Otherwise, the first fitting ISO format is selected.
1777  */
1778 static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,JsonValueList * found)1779 executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1780 					  JsonbValue *jb, JsonValueList *found)
1781 {
1782 	JsonbValue	jbvbuf;
1783 	Datum		value;
1784 	text	   *datetime;
1785 	Oid			collid;
1786 	Oid			typid;
1787 	int32		typmod = -1;
1788 	int			tz = 0;
1789 	bool		hasNext;
1790 	JsonPathExecResult res = jperNotFound;
1791 	JsonPathItem elem;
1792 
1793 	if (!(jb = getScalar(jb, jbvString)))
1794 		RETURN_ERROR(ereport(ERROR,
1795 							 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1796 							  errmsg("jsonpath item method .%s() can only be applied to a string",
1797 									 jspOperationName(jsp->type)))));
1798 
1799 	datetime = cstring_to_text_with_len(jb->val.string.val,
1800 										jb->val.string.len);
1801 
1802 	/*
1803 	 * At some point we might wish to have callers supply the collation to
1804 	 * use, but right now it's unclear that they'd be able to do better than
1805 	 * DEFAULT_COLLATION_OID anyway.
1806 	 */
1807 	collid = DEFAULT_COLLATION_OID;
1808 
1809 	if (jsp->content.arg)
1810 	{
1811 		text	   *template;
1812 		char	   *template_str;
1813 		int			template_len;
1814 		bool		have_error = false;
1815 
1816 		jspGetArg(jsp, &elem);
1817 
1818 		if (elem.type != jpiString)
1819 			elog(ERROR, "invalid jsonpath item type for .datetime() argument");
1820 
1821 		template_str = jspGetString(&elem, &template_len);
1822 
1823 		template = cstring_to_text_with_len(template_str,
1824 											template_len);
1825 
1826 		value = parse_datetime(datetime, template, collid, true,
1827 							   &typid, &typmod, &tz,
1828 							   jspThrowErrors(cxt) ? NULL : &have_error);
1829 
1830 		if (have_error)
1831 			res = jperError;
1832 		else
1833 			res = jperOk;
1834 	}
1835 	else
1836 	{
1837 		/*
1838 		 * According to SQL/JSON standard enumerate ISO formats for: date,
1839 		 * timetz, time, timestamptz, timestamp.
1840 		 *
1841 		 * We also support ISO 8601 for timestamps, because to_json[b]()
1842 		 * functions use this format.
1843 		 */
1844 		static const char *fmt_str[] =
1845 		{
1846 			"yyyy-mm-dd",
1847 			"HH24:MI:SSTZH:TZM",
1848 			"HH24:MI:SSTZH",
1849 			"HH24:MI:SS",
1850 			"yyyy-mm-dd HH24:MI:SSTZH:TZM",
1851 			"yyyy-mm-dd HH24:MI:SSTZH",
1852 			"yyyy-mm-dd HH24:MI:SS",
1853 			"yyyy-mm-dd\"T\"HH24:MI:SSTZH:TZM",
1854 			"yyyy-mm-dd\"T\"HH24:MI:SSTZH",
1855 			"yyyy-mm-dd\"T\"HH24:MI:SS"
1856 		};
1857 
1858 		/* cache for format texts */
1859 		static text *fmt_txt[lengthof(fmt_str)] = {0};
1860 		int			i;
1861 
1862 		/* loop until datetime format fits */
1863 		for (i = 0; i < lengthof(fmt_str); i++)
1864 		{
1865 			bool		have_error = false;
1866 
1867 			if (!fmt_txt[i])
1868 			{
1869 				MemoryContext oldcxt =
1870 				MemoryContextSwitchTo(TopMemoryContext);
1871 
1872 				fmt_txt[i] = cstring_to_text(fmt_str[i]);
1873 				MemoryContextSwitchTo(oldcxt);
1874 			}
1875 
1876 			value = parse_datetime(datetime, fmt_txt[i], collid, true,
1877 								   &typid, &typmod, &tz,
1878 								   &have_error);
1879 
1880 			if (!have_error)
1881 			{
1882 				res = jperOk;
1883 				break;
1884 			}
1885 		}
1886 
1887 		if (res == jperNotFound)
1888 			RETURN_ERROR(ereport(ERROR,
1889 								 (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
1890 								  errmsg("datetime format is not recognized: \"%s\"",
1891 										 text_to_cstring(datetime)),
1892 								  errhint("Use a datetime template argument to specify the input data format."))));
1893 	}
1894 
1895 	pfree(datetime);
1896 
1897 	if (jperIsError(res))
1898 		return res;
1899 
1900 	hasNext = jspGetNext(jsp, &elem);
1901 
1902 	if (!hasNext && !found)
1903 		return res;
1904 
1905 	jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
1906 
1907 	jb->type = jbvDatetime;
1908 	jb->val.datetime.value = value;
1909 	jb->val.datetime.typid = typid;
1910 	jb->val.datetime.typmod = typmod;
1911 	jb->val.datetime.tz = tz;
1912 
1913 	return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
1914 }
1915 
1916 /*
1917  * Implementation of .keyvalue() method.
1918  *
1919  * .keyvalue() method returns a sequence of object's key-value pairs in the
1920  * following format: '{ "key": key, "value": value, "id": id }'.
1921  *
1922  * "id" field is an object identifier which is constructed from the two parts:
1923  * base object id and its binary offset in base object's jsonb:
1924  * id = 10000000000 * base_object_id + obj_offset_in_base_object
1925  *
1926  * 10000000000 (10^10) -- is a first round decimal number greater than 2^32
1927  * (maximal offset in jsonb).  Decimal multiplier is used here to improve the
1928  * readability of identifiers.
1929  *
1930  * Base object is usually a root object of the path: context item '$' or path
1931  * variable '$var', literals can't produce objects for now.  But if the path
1932  * contains generated objects (.keyvalue() itself, for example), then they
1933  * become base object for the subsequent .keyvalue().
1934  *
1935  * Id of '$' is 0. Id of '$var' is its ordinal (positive) number in the list
1936  * of variables (see getJsonPathVariable()).  Ids for generated objects
1937  * are assigned using global counter JsonPathExecContext.lastGeneratedObjectId.
1938  */
1939 static JsonPathExecResult
executeKeyValueMethod(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,JsonValueList * found)1940 executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
1941 					  JsonbValue *jb, JsonValueList *found)
1942 {
1943 	JsonPathExecResult res = jperNotFound;
1944 	JsonPathItem next;
1945 	JsonbContainer *jbc;
1946 	JsonbValue	key;
1947 	JsonbValue	val;
1948 	JsonbValue	idval;
1949 	JsonbValue	keystr;
1950 	JsonbValue	valstr;
1951 	JsonbValue	idstr;
1952 	JsonbIterator *it;
1953 	JsonbIteratorToken tok;
1954 	int64		id;
1955 	bool		hasNext;
1956 
1957 	if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
1958 		RETURN_ERROR(ereport(ERROR,
1959 							 (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
1960 							  errmsg("jsonpath item method .%s() can only be applied to an object",
1961 									 jspOperationName(jsp->type)))));
1962 
1963 	jbc = jb->val.binary.data;
1964 
1965 	if (!JsonContainerSize(jbc))
1966 		return jperNotFound;	/* no key-value pairs */
1967 
1968 	hasNext = jspGetNext(jsp, &next);
1969 
1970 	keystr.type = jbvString;
1971 	keystr.val.string.val = "key";
1972 	keystr.val.string.len = 3;
1973 
1974 	valstr.type = jbvString;
1975 	valstr.val.string.val = "value";
1976 	valstr.val.string.len = 5;
1977 
1978 	idstr.type = jbvString;
1979 	idstr.val.string.val = "id";
1980 	idstr.val.string.len = 2;
1981 
1982 	/* construct object id from its base object and offset inside that */
1983 	id = jb->type != jbvBinary ? 0 :
1984 		(int64) ((char *) jbc - (char *) cxt->baseObject.jbc);
1985 	id += (int64) cxt->baseObject.id * INT64CONST(10000000000);
1986 
1987 	idval.type = jbvNumeric;
1988 	idval.val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
1989 															Int64GetDatum(id)));
1990 
1991 	it = JsonbIteratorInit(jbc);
1992 
1993 	while ((tok = JsonbIteratorNext(&it, &key, true)) != WJB_DONE)
1994 	{
1995 		JsonBaseObjectInfo baseObject;
1996 		JsonbValue	obj;
1997 		JsonbParseState *ps;
1998 		JsonbValue *keyval;
1999 		Jsonb	   *jsonb;
2000 
2001 		if (tok != WJB_KEY)
2002 			continue;
2003 
2004 		res = jperOk;
2005 
2006 		if (!hasNext && !found)
2007 			break;
2008 
2009 		tok = JsonbIteratorNext(&it, &val, true);
2010 		Assert(tok == WJB_VALUE);
2011 
2012 		ps = NULL;
2013 		pushJsonbValue(&ps, WJB_BEGIN_OBJECT, NULL);
2014 
2015 		pushJsonbValue(&ps, WJB_KEY, &keystr);
2016 		pushJsonbValue(&ps, WJB_VALUE, &key);
2017 
2018 		pushJsonbValue(&ps, WJB_KEY, &valstr);
2019 		pushJsonbValue(&ps, WJB_VALUE, &val);
2020 
2021 		pushJsonbValue(&ps, WJB_KEY, &idstr);
2022 		pushJsonbValue(&ps, WJB_VALUE, &idval);
2023 
2024 		keyval = pushJsonbValue(&ps, WJB_END_OBJECT, NULL);
2025 
2026 		jsonb = JsonbValueToJsonb(keyval);
2027 
2028 		JsonbInitBinary(&obj, jsonb);
2029 
2030 		baseObject = setBaseObject(cxt, &obj, cxt->lastGeneratedObjectId++);
2031 
2032 		res = executeNextItem(cxt, jsp, &next, &obj, found, true);
2033 
2034 		cxt->baseObject = baseObject;
2035 
2036 		if (jperIsError(res))
2037 			return res;
2038 
2039 		if (res == jperOk && !found)
2040 			break;
2041 	}
2042 
2043 	return res;
2044 }
2045 
2046 /*
2047  * Convert boolean execution status 'res' to a boolean JSON item and execute
2048  * next jsonpath.
2049  */
2050 static JsonPathExecResult
appendBoolResult(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonValueList * found,JsonPathBool res)2051 appendBoolResult(JsonPathExecContext *cxt, JsonPathItem *jsp,
2052 				 JsonValueList *found, JsonPathBool res)
2053 {
2054 	JsonPathItem next;
2055 	JsonbValue	jbv;
2056 
2057 	if (!jspGetNext(jsp, &next) && !found)
2058 		return jperOk;			/* found singleton boolean value */
2059 
2060 	if (res == jpbUnknown)
2061 	{
2062 		jbv.type = jbvNull;
2063 	}
2064 	else
2065 	{
2066 		jbv.type = jbvBool;
2067 		jbv.val.boolean = res == jpbTrue;
2068 	}
2069 
2070 	return executeNextItem(cxt, jsp, &next, &jbv, found, true);
2071 }
2072 
2073 /*
2074  * Convert jsonpath's scalar or variable node to actual jsonb value.
2075  *
2076  * If node is a variable then its id returned, otherwise 0 returned.
2077  */
2078 static void
getJsonPathItem(JsonPathExecContext * cxt,JsonPathItem * item,JsonbValue * value)2079 getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
2080 				JsonbValue *value)
2081 {
2082 	switch (item->type)
2083 	{
2084 		case jpiNull:
2085 			value->type = jbvNull;
2086 			break;
2087 		case jpiBool:
2088 			value->type = jbvBool;
2089 			value->val.boolean = jspGetBool(item);
2090 			break;
2091 		case jpiNumeric:
2092 			value->type = jbvNumeric;
2093 			value->val.numeric = jspGetNumeric(item);
2094 			break;
2095 		case jpiString:
2096 			value->type = jbvString;
2097 			value->val.string.val = jspGetString(item,
2098 												 &value->val.string.len);
2099 			break;
2100 		case jpiVariable:
2101 			getJsonPathVariable(cxt, item, cxt->vars, value);
2102 			return;
2103 		default:
2104 			elog(ERROR, "unexpected jsonpath item type");
2105 	}
2106 }
2107 
2108 /*
2109  * Get the value of variable passed to jsonpath executor
2110  */
2111 static void
getJsonPathVariable(JsonPathExecContext * cxt,JsonPathItem * variable,Jsonb * vars,JsonbValue * value)2112 getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
2113 					Jsonb *vars, JsonbValue *value)
2114 {
2115 	char	   *varName;
2116 	int			varNameLength;
2117 	JsonbValue	tmp;
2118 	JsonbValue *v;
2119 
2120 	if (!vars)
2121 	{
2122 		value->type = jbvNull;
2123 		return;
2124 	}
2125 
2126 	Assert(variable->type == jpiVariable);
2127 	varName = jspGetString(variable, &varNameLength);
2128 	tmp.type = jbvString;
2129 	tmp.val.string.val = varName;
2130 	tmp.val.string.len = varNameLength;
2131 
2132 	v = findJsonbValueFromContainer(&vars->root, JB_FOBJECT, &tmp);
2133 
2134 	if (v)
2135 	{
2136 		*value = *v;
2137 		pfree(v);
2138 	}
2139 	else
2140 	{
2141 		ereport(ERROR,
2142 				(errcode(ERRCODE_UNDEFINED_OBJECT),
2143 				 errmsg("could not find jsonpath variable \"%s\"",
2144 						pnstrdup(varName, varNameLength))));
2145 	}
2146 
2147 	JsonbInitBinary(&tmp, vars);
2148 	setBaseObject(cxt, &tmp, 1);
2149 }
2150 
2151 /**************** Support functions for JsonPath execution *****************/
2152 
2153 /*
2154  * Returns the size of an array item, or -1 if item is not an array.
2155  */
2156 static int
JsonbArraySize(JsonbValue * jb)2157 JsonbArraySize(JsonbValue *jb)
2158 {
2159 	Assert(jb->type != jbvArray);
2160 
2161 	if (jb->type == jbvBinary)
2162 	{
2163 		JsonbContainer *jbc = jb->val.binary.data;
2164 
2165 		if (JsonContainerIsArray(jbc) && !JsonContainerIsScalar(jbc))
2166 			return JsonContainerSize(jbc);
2167 	}
2168 
2169 	return -1;
2170 }
2171 
2172 /* Comparison predicate callback. */
2173 static JsonPathBool
executeComparison(JsonPathItem * cmp,JsonbValue * lv,JsonbValue * rv,void * p)2174 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
2175 {
2176 	JsonPathExecContext *cxt = (JsonPathExecContext *) p;
2177 
2178 	return compareItems(cmp->type, lv, rv, cxt->useTz);
2179 }
2180 
2181 /*
2182  * Perform per-byte comparison of two strings.
2183  */
2184 static int
binaryCompareStrings(const char * s1,int len1,const char * s2,int len2)2185 binaryCompareStrings(const char *s1, int len1,
2186 					 const char *s2, int len2)
2187 {
2188 	int			cmp;
2189 
2190 	cmp = memcmp(s1, s2, Min(len1, len2));
2191 
2192 	if (cmp != 0)
2193 		return cmp;
2194 
2195 	if (len1 == len2)
2196 		return 0;
2197 
2198 	return len1 < len2 ? -1 : 1;
2199 }
2200 
2201 /*
2202  * Compare two strings in the current server encoding using Unicode codepoint
2203  * collation.
2204  */
2205 static int
compareStrings(const char * mbstr1,int mblen1,const char * mbstr2,int mblen2)2206 compareStrings(const char *mbstr1, int mblen1,
2207 			   const char *mbstr2, int mblen2)
2208 {
2209 	if (GetDatabaseEncoding() == PG_SQL_ASCII ||
2210 		GetDatabaseEncoding() == PG_UTF8)
2211 	{
2212 		/*
2213 		 * It's known property of UTF-8 strings that their per-byte comparison
2214 		 * result matches codepoints comparison result.  ASCII can be
2215 		 * considered as special case of UTF-8.
2216 		 */
2217 		return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2218 	}
2219 	else
2220 	{
2221 		char	   *utf8str1,
2222 				   *utf8str2;
2223 		int			cmp,
2224 					utf8len1,
2225 					utf8len2;
2226 
2227 		/*
2228 		 * We have to convert other encodings to UTF-8 first, then compare.
2229 		 * Input strings may be not null-terminated and pg_server_to_any() may
2230 		 * return them "as is".  So, use strlen() only if there is real
2231 		 * conversion.
2232 		 */
2233 		utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
2234 		utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
2235 		utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
2236 		utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
2237 
2238 		cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
2239 
2240 		/*
2241 		 * If pg_server_to_any() did no real conversion, then we actually
2242 		 * compared original strings.  So, we already done.
2243 		 */
2244 		if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
2245 			return cmp;
2246 
2247 		/* Free memory if needed */
2248 		if (mbstr1 != utf8str1)
2249 			pfree(utf8str1);
2250 		if (mbstr2 != utf8str2)
2251 			pfree(utf8str2);
2252 
2253 		/*
2254 		 * When all Unicode codepoints are equal, return result of binary
2255 		 * comparison.  In some edge cases, same characters may have different
2256 		 * representations in encoding.  Then our behavior could diverge from
2257 		 * standard.  However, that allow us to do simple binary comparison
2258 		 * for "==" operator, which is performance critical in typical cases.
2259 		 * In future to implement strict standard conformance, we can do
2260 		 * normalization of input JSON strings.
2261 		 */
2262 		if (cmp == 0)
2263 			return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
2264 		else
2265 			return cmp;
2266 	}
2267 }
2268 
2269 /*
2270  * Compare two SQL/JSON items using comparison operation 'op'.
2271  */
2272 static JsonPathBool
compareItems(int32 op,JsonbValue * jb1,JsonbValue * jb2,bool useTz)2273 compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
2274 {
2275 	int			cmp;
2276 	bool		res;
2277 
2278 	if (jb1->type != jb2->type)
2279 	{
2280 		if (jb1->type == jbvNull || jb2->type == jbvNull)
2281 
2282 			/*
2283 			 * Equality and order comparison of nulls to non-nulls returns
2284 			 * always false, but inequality comparison returns true.
2285 			 */
2286 			return op == jpiNotEqual ? jpbTrue : jpbFalse;
2287 
2288 		/* Non-null items of different types are not comparable. */
2289 		return jpbUnknown;
2290 	}
2291 
2292 	switch (jb1->type)
2293 	{
2294 		case jbvNull:
2295 			cmp = 0;
2296 			break;
2297 		case jbvBool:
2298 			cmp = jb1->val.boolean == jb2->val.boolean ? 0 :
2299 				jb1->val.boolean ? 1 : -1;
2300 			break;
2301 		case jbvNumeric:
2302 			cmp = compareNumeric(jb1->val.numeric, jb2->val.numeric);
2303 			break;
2304 		case jbvString:
2305 			if (op == jpiEqual)
2306 				return jb1->val.string.len != jb2->val.string.len ||
2307 					memcmp(jb1->val.string.val,
2308 						   jb2->val.string.val,
2309 						   jb1->val.string.len) ? jpbFalse : jpbTrue;
2310 
2311 			cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
2312 								 jb2->val.string.val, jb2->val.string.len);
2313 			break;
2314 		case jbvDatetime:
2315 			{
2316 				bool		cast_error;
2317 
2318 				cmp = compareDatetime(jb1->val.datetime.value,
2319 									  jb1->val.datetime.typid,
2320 									  jb2->val.datetime.value,
2321 									  jb2->val.datetime.typid,
2322 									  useTz,
2323 									  &cast_error);
2324 
2325 				if (cast_error)
2326 					return jpbUnknown;
2327 			}
2328 			break;
2329 
2330 		case jbvBinary:
2331 		case jbvArray:
2332 		case jbvObject:
2333 			return jpbUnknown;	/* non-scalars are not comparable */
2334 
2335 		default:
2336 			elog(ERROR, "invalid jsonb value type %d", jb1->type);
2337 	}
2338 
2339 	switch (op)
2340 	{
2341 		case jpiEqual:
2342 			res = (cmp == 0);
2343 			break;
2344 		case jpiNotEqual:
2345 			res = (cmp != 0);
2346 			break;
2347 		case jpiLess:
2348 			res = (cmp < 0);
2349 			break;
2350 		case jpiGreater:
2351 			res = (cmp > 0);
2352 			break;
2353 		case jpiLessOrEqual:
2354 			res = (cmp <= 0);
2355 			break;
2356 		case jpiGreaterOrEqual:
2357 			res = (cmp >= 0);
2358 			break;
2359 		default:
2360 			elog(ERROR, "unrecognized jsonpath operation: %d", op);
2361 			return jpbUnknown;
2362 	}
2363 
2364 	return res ? jpbTrue : jpbFalse;
2365 }
2366 
2367 /* Compare two numerics */
2368 static int
compareNumeric(Numeric a,Numeric b)2369 compareNumeric(Numeric a, Numeric b)
2370 {
2371 	return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
2372 											 NumericGetDatum(a),
2373 											 NumericGetDatum(b)));
2374 }
2375 
2376 static JsonbValue *
copyJsonbValue(JsonbValue * src)2377 copyJsonbValue(JsonbValue *src)
2378 {
2379 	JsonbValue *dst = palloc(sizeof(*dst));
2380 
2381 	*dst = *src;
2382 
2383 	return dst;
2384 }
2385 
2386 /*
2387  * Execute array subscript expression and convert resulting numeric item to
2388  * the integer type with truncation.
2389  */
2390 static JsonPathExecResult
getArrayIndex(JsonPathExecContext * cxt,JsonPathItem * jsp,JsonbValue * jb,int32 * index)2391 getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
2392 			  int32 *index)
2393 {
2394 	JsonbValue *jbv;
2395 	JsonValueList found = {0};
2396 	JsonPathExecResult res = executeItem(cxt, jsp, jb, &found);
2397 	Datum		numeric_index;
2398 	bool		have_error = false;
2399 
2400 	if (jperIsError(res))
2401 		return res;
2402 
2403 	if (JsonValueListLength(&found) != 1 ||
2404 		!(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
2405 		RETURN_ERROR(ereport(ERROR,
2406 							 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2407 							  errmsg("jsonpath array subscript is not a single numeric value"))));
2408 
2409 	numeric_index = DirectFunctionCall2(numeric_trunc,
2410 										NumericGetDatum(jbv->val.numeric),
2411 										Int32GetDatum(0));
2412 
2413 	*index = numeric_int4_opt_error(DatumGetNumeric(numeric_index),
2414 									&have_error);
2415 
2416 	if (have_error)
2417 		RETURN_ERROR(ereport(ERROR,
2418 							 (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
2419 							  errmsg("jsonpath array subscript is out of integer range"))));
2420 
2421 	return jperOk;
2422 }
2423 
2424 /* Save base object and its id needed for the execution of .keyvalue(). */
2425 static JsonBaseObjectInfo
setBaseObject(JsonPathExecContext * cxt,JsonbValue * jbv,int32 id)2426 setBaseObject(JsonPathExecContext *cxt, JsonbValue *jbv, int32 id)
2427 {
2428 	JsonBaseObjectInfo baseObject = cxt->baseObject;
2429 
2430 	cxt->baseObject.jbc = jbv->type != jbvBinary ? NULL :
2431 		(JsonbContainer *) jbv->val.binary.data;
2432 	cxt->baseObject.id = id;
2433 
2434 	return baseObject;
2435 }
2436 
2437 static void
JsonValueListAppend(JsonValueList * jvl,JsonbValue * jbv)2438 JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv)
2439 {
2440 	if (jvl->singleton)
2441 	{
2442 		jvl->list = list_make2(jvl->singleton, jbv);
2443 		jvl->singleton = NULL;
2444 	}
2445 	else if (!jvl->list)
2446 		jvl->singleton = jbv;
2447 	else
2448 		jvl->list = lappend(jvl->list, jbv);
2449 }
2450 
2451 static int
JsonValueListLength(const JsonValueList * jvl)2452 JsonValueListLength(const JsonValueList *jvl)
2453 {
2454 	return jvl->singleton ? 1 : list_length(jvl->list);
2455 }
2456 
2457 static bool
JsonValueListIsEmpty(JsonValueList * jvl)2458 JsonValueListIsEmpty(JsonValueList *jvl)
2459 {
2460 	return !jvl->singleton && list_length(jvl->list) <= 0;
2461 }
2462 
2463 static JsonbValue *
JsonValueListHead(JsonValueList * jvl)2464 JsonValueListHead(JsonValueList *jvl)
2465 {
2466 	return jvl->singleton ? jvl->singleton : linitial(jvl->list);
2467 }
2468 
2469 static List *
JsonValueListGetList(JsonValueList * jvl)2470 JsonValueListGetList(JsonValueList *jvl)
2471 {
2472 	if (jvl->singleton)
2473 		return list_make1(jvl->singleton);
2474 
2475 	return jvl->list;
2476 }
2477 
2478 static void
JsonValueListInitIterator(const JsonValueList * jvl,JsonValueListIterator * it)2479 JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
2480 {
2481 	if (jvl->singleton)
2482 	{
2483 		it->value = jvl->singleton;
2484 		it->list = NIL;
2485 		it->next = NULL;
2486 	}
2487 	else if (jvl->list != NIL)
2488 	{
2489 		it->value = (JsonbValue *) linitial(jvl->list);
2490 		it->list = jvl->list;
2491 		it->next = list_second_cell(jvl->list);
2492 	}
2493 	else
2494 	{
2495 		it->value = NULL;
2496 		it->list = NIL;
2497 		it->next = NULL;
2498 	}
2499 }
2500 
2501 /*
2502  * Get the next item from the sequence advancing iterator.
2503  */
2504 static JsonbValue *
JsonValueListNext(const JsonValueList * jvl,JsonValueListIterator * it)2505 JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
2506 {
2507 	JsonbValue *result = it->value;
2508 
2509 	if (it->next)
2510 	{
2511 		it->value = lfirst(it->next);
2512 		it->next = lnext(it->list, it->next);
2513 	}
2514 	else
2515 	{
2516 		it->value = NULL;
2517 	}
2518 
2519 	return result;
2520 }
2521 
2522 /*
2523  * Initialize a binary JsonbValue with the given jsonb container.
2524  */
2525 static JsonbValue *
JsonbInitBinary(JsonbValue * jbv,Jsonb * jb)2526 JsonbInitBinary(JsonbValue *jbv, Jsonb *jb)
2527 {
2528 	jbv->type = jbvBinary;
2529 	jbv->val.binary.data = &jb->root;
2530 	jbv->val.binary.len = VARSIZE_ANY_EXHDR(jb);
2531 
2532 	return jbv;
2533 }
2534 
2535 /*
2536  * Returns jbv* type of JsonbValue. Note, it never returns jbvBinary as is.
2537  */
2538 static int
JsonbType(JsonbValue * jb)2539 JsonbType(JsonbValue *jb)
2540 {
2541 	int			type = jb->type;
2542 
2543 	if (jb->type == jbvBinary)
2544 	{
2545 		JsonbContainer *jbc = (void *) jb->val.binary.data;
2546 
2547 		/* Scalars should be always extracted during jsonpath execution. */
2548 		Assert(!JsonContainerIsScalar(jbc));
2549 
2550 		if (JsonContainerIsObject(jbc))
2551 			type = jbvObject;
2552 		else if (JsonContainerIsArray(jbc))
2553 			type = jbvArray;
2554 		else
2555 			elog(ERROR, "invalid jsonb container type: 0x%08x", jbc->header);
2556 	}
2557 
2558 	return type;
2559 }
2560 
2561 /* Get scalar of given type or NULL on type mismatch */
2562 static JsonbValue *
getScalar(JsonbValue * scalar,enum jbvType type)2563 getScalar(JsonbValue *scalar, enum jbvType type)
2564 {
2565 	/* Scalars should be always extracted during jsonpath execution. */
2566 	Assert(scalar->type != jbvBinary ||
2567 		   !JsonContainerIsScalar(scalar->val.binary.data));
2568 
2569 	return scalar->type == type ? scalar : NULL;
2570 }
2571 
2572 /* Construct a JSON array from the item list */
2573 static JsonbValue *
wrapItemsInArray(const JsonValueList * items)2574 wrapItemsInArray(const JsonValueList *items)
2575 {
2576 	JsonbParseState *ps = NULL;
2577 	JsonValueListIterator it;
2578 	JsonbValue *jbv;
2579 
2580 	pushJsonbValue(&ps, WJB_BEGIN_ARRAY, NULL);
2581 
2582 	JsonValueListInitIterator(items, &it);
2583 	while ((jbv = JsonValueListNext(items, &it)))
2584 		pushJsonbValue(&ps, WJB_ELEM, jbv);
2585 
2586 	return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
2587 }
2588 
2589 /* Check if the timezone required for casting from type1 to type2 is used */
2590 static void
checkTimezoneIsUsedForCast(bool useTz,const char * type1,const char * type2)2591 checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
2592 {
2593 	if (!useTz)
2594 		ereport(ERROR,
2595 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2596 				 errmsg("cannot convert value from %s to %s without time zone usage",
2597 						type1, type2),
2598 				 errhint("Use *_tz() function for time zone support.")));
2599 }
2600 
2601 /* Convert time datum to timetz datum */
2602 static Datum
castTimeToTimeTz(Datum time,bool useTz)2603 castTimeToTimeTz(Datum time, bool useTz)
2604 {
2605 	checkTimezoneIsUsedForCast(useTz, "time", "timetz");
2606 
2607 	return DirectFunctionCall1(time_timetz, time);
2608 }
2609 
2610 /*
2611  * Compare date to timestamp.
2612  * Note that this doesn't involve any timezone considerations.
2613  */
2614 static int
cmpDateToTimestamp(DateADT date1,Timestamp ts2,bool useTz)2615 cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
2616 {
2617 	return date_cmp_timestamp_internal(date1, ts2);
2618 }
2619 
2620 /*
2621  * Compare date to timestamptz.
2622  */
2623 static int
cmpDateToTimestampTz(DateADT date1,TimestampTz tstz2,bool useTz)2624 cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
2625 {
2626 	checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
2627 
2628 	return date_cmp_timestamptz_internal(date1, tstz2);
2629 }
2630 
2631 /*
2632  * Compare timestamp to timestamptz.
2633  */
2634 static int
cmpTimestampToTimestampTz(Timestamp ts1,TimestampTz tstz2,bool useTz)2635 cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
2636 {
2637 	checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
2638 
2639 	return timestamp_cmp_timestamptz_internal(ts1, tstz2);
2640 }
2641 
2642 /*
2643  * Cross-type comparison of two datetime SQL/JSON items.  If items are
2644  * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
2645  * If the cast requires timezone and it is not used, then explicit error is thrown.
2646  */
2647 static int
compareDatetime(Datum val1,Oid typid1,Datum val2,Oid typid2,bool useTz,bool * cast_error)2648 compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
2649 				bool useTz, bool *cast_error)
2650 {
2651 	PGFunction cmpfunc;
2652 
2653 	*cast_error = false;
2654 
2655 	switch (typid1)
2656 	{
2657 		case DATEOID:
2658 			switch (typid2)
2659 			{
2660 				case DATEOID:
2661 					cmpfunc = date_cmp;
2662 
2663 					break;
2664 
2665 				case TIMESTAMPOID:
2666 					return cmpDateToTimestamp(DatumGetDateADT(val1),
2667 											  DatumGetTimestamp(val2),
2668 											  useTz);
2669 
2670 				case TIMESTAMPTZOID:
2671 					return cmpDateToTimestampTz(DatumGetDateADT(val1),
2672 												DatumGetTimestampTz(val2),
2673 												useTz);
2674 
2675 				case TIMEOID:
2676 				case TIMETZOID:
2677 					*cast_error = true; /* uncomparable types */
2678 					return 0;
2679 
2680 				default:
2681 					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2682 						 typid2);
2683 			}
2684 			break;
2685 
2686 		case TIMEOID:
2687 			switch (typid2)
2688 			{
2689 				case TIMEOID:
2690 					cmpfunc = time_cmp;
2691 
2692 					break;
2693 
2694 				case TIMETZOID:
2695 					val1 = castTimeToTimeTz(val1, useTz);
2696 					cmpfunc = timetz_cmp;
2697 
2698 					break;
2699 
2700 				case DATEOID:
2701 				case TIMESTAMPOID:
2702 				case TIMESTAMPTZOID:
2703 					*cast_error = true; /* uncomparable types */
2704 					return 0;
2705 
2706 				default:
2707 					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2708 						 typid2);
2709 			}
2710 			break;
2711 
2712 		case TIMETZOID:
2713 			switch (typid2)
2714 			{
2715 				case TIMEOID:
2716 					val2 = castTimeToTimeTz(val2, useTz);
2717 					cmpfunc = timetz_cmp;
2718 
2719 					break;
2720 
2721 				case TIMETZOID:
2722 					cmpfunc = timetz_cmp;
2723 
2724 					break;
2725 
2726 				case DATEOID:
2727 				case TIMESTAMPOID:
2728 				case TIMESTAMPTZOID:
2729 					*cast_error = true; /* uncomparable types */
2730 					return 0;
2731 
2732 				default:
2733 					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2734 						 typid2);
2735 			}
2736 			break;
2737 
2738 		case TIMESTAMPOID:
2739 			switch (typid2)
2740 			{
2741 				case DATEOID:
2742 					return -cmpDateToTimestamp(DatumGetDateADT(val2),
2743 											   DatumGetTimestamp(val1),
2744 											   useTz);
2745 
2746 				case TIMESTAMPOID:
2747 					cmpfunc = timestamp_cmp;
2748 
2749 					break;
2750 
2751 				case TIMESTAMPTZOID:
2752 					return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
2753 													 DatumGetTimestampTz(val2),
2754 													 useTz);
2755 
2756 				case TIMEOID:
2757 				case TIMETZOID:
2758 					*cast_error = true; /* uncomparable types */
2759 					return 0;
2760 
2761 				default:
2762 					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2763 						 typid2);
2764 			}
2765 			break;
2766 
2767 		case TIMESTAMPTZOID:
2768 			switch (typid2)
2769 			{
2770 				case DATEOID:
2771 					return -cmpDateToTimestampTz(DatumGetDateADT(val2),
2772 												 DatumGetTimestampTz(val1),
2773 												 useTz);
2774 
2775 				case TIMESTAMPOID:
2776 					return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
2777 													  DatumGetTimestampTz(val1),
2778 													  useTz);
2779 
2780 				case TIMESTAMPTZOID:
2781 					cmpfunc = timestamp_cmp;
2782 
2783 					break;
2784 
2785 				case TIMEOID:
2786 				case TIMETZOID:
2787 					*cast_error = true; /* uncomparable types */
2788 					return 0;
2789 
2790 				default:
2791 					elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
2792 						 typid2);
2793 			}
2794 			break;
2795 
2796 		default:
2797 			elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
2798 	}
2799 
2800 	if (*cast_error)
2801 		return 0;				/* cast error */
2802 
2803 	return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
2804 }
2805