]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/jsonpath_exec.c
Fix initialization of fake LSN for unlogged relations
[postgresql] / src / backend / utils / adt / jsonpath_exec.c
index 074cea24ae31c2aeb2b6f120d11ccf52f7724427..e2c1bfb5a775054b0560629b5e1cac5e8c1f0ed2 100644 (file)
@@ -66,6 +66,7 @@
 #include "miscadmin.h"
 #include "regex/regex.h"
 #include "utils/builtins.h"
+#include "utils/datetime.h"
 #include "utils/datum.h"
 #include "utils/formatting.h"
 #include "utils/float.h"
 #include "utils/varlena.h"
 
 
-/* Standard error message for SQL/JSON errors */
-#define ERRMSG_JSON_ARRAY_NOT_FOUND                    "SQL/JSON array not found"
-#define ERRMSG_JSON_OBJECT_NOT_FOUND           "SQL/JSON object not found"
-#define ERRMSG_JSON_MEMBER_NOT_FOUND           "SQL/JSON member not found"
-#define ERRMSG_JSON_NUMBER_NOT_FOUND           "SQL/JSON number not found"
-#define ERRMSG_JSON_SCALAR_REQUIRED                    "SQL/JSON scalar required"
-#define ERRMSG_SINGLETON_JSON_ITEM_REQUIRED    "singleton SQL/JSON item required"
-#define ERRMSG_NON_NUMERIC_JSON_ITEM           "non-numeric SQL/JSON item"
-#define ERRMSG_INVALID_JSON_SUBSCRIPT          "invalid SQL/JSON subscript"
-
 /*
  * Represents "base object" and it's "id" for .keyvalue() evaluation.
  */
@@ -117,6 +108,7 @@ typedef struct JsonPathExecContext
                                                                                 * ignored */
        bool            throwErrors;    /* with "false" all suppressible errors are
                                                                 * suppressed */
+       bool            useTz;
 } JsonPathExecContext;
 
 /* Context for LIKE_REGEX execution. */
@@ -156,6 +148,7 @@ typedef struct JsonValueList
 typedef struct JsonValueListIterator
 {
        JsonbValue *value;
+       List       *list;
        ListCell   *next;
 } JsonValueListIterator;
 
@@ -182,81 +175,85 @@ typedef JsonPathBool (*JsonPathPredicateCallback) (JsonPathItem *jsp,
 typedef Numeric (*BinaryArithmFunc) (Numeric num1, Numeric num2, bool *error);
 
 static JsonPathExecResult executeJsonPath(JsonPath *path, Jsonb *vars,
-                               Jsonb *json, bool throwErrors, JsonValueList *result);
+                                                                                 Jsonb *json, bool throwErrors,
+                                                                                 JsonValueList *result, bool useTz);
 static JsonPathExecResult executeItem(JsonPathExecContext *cxt,
-                       JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
+                                                                         JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
 static JsonPathExecResult executeItemOptUnwrapTarget(JsonPathExecContext *cxt,
-                                                  JsonPathItem *jsp, JsonbValue *jb,
-                                                  JsonValueList *found, bool unwrap);
+                                                                                                        JsonPathItem *jsp, JsonbValue *jb,
+                                                                                                        JsonValueList *found, bool unwrap);
 static JsonPathExecResult executeItemUnwrapTargetArray(JsonPathExecContext *cxt,
-                                                        JsonPathItem *jsp, JsonbValue *jb,
-                                                        JsonValueList *found, bool unwrapElements);
+                                                                                                          JsonPathItem *jsp, JsonbValue *jb,
+                                                                                                          JsonValueList *found, bool unwrapElements);
 static JsonPathExecResult executeNextItem(JsonPathExecContext *cxt,
-                               JsonPathItem *cur, JsonPathItem *next,
-                               JsonbValue *v, JsonValueList *found, bool copy);
-static JsonPathExecResult executeItemOptUnwrapResult(
-                                                  JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
-                                                  bool unwrap, JsonValueList *found);
-static JsonPathExecResult executeItemOptUnwrapResultNoThrow(
-                                                                 JsonPathExecContext *cxt, JsonPathItem *jsp,
-                                                                 JsonbValue *jb, bool unwrap, JsonValueList *found);
+                                                                                 JsonPathItem *cur, JsonPathItem *next,
+                                                                                 JsonbValue *v, JsonValueList *found, bool copy);
+static JsonPathExecResult executeItemOptUnwrapResult(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
+                                                                                                        bool unwrap, JsonValueList *found);
+static JsonPathExecResult executeItemOptUnwrapResultNoThrow(JsonPathExecContext *cxt, JsonPathItem *jsp,
+                                                                                                                       JsonbValue *jb, bool unwrap, JsonValueList *found);
 static JsonPathBool executeBoolItem(JsonPathExecContext *cxt,
-                               JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
+                                                                       JsonPathItem *jsp, JsonbValue *jb, bool canHaveNext);
 static JsonPathBool executeNestedBoolItem(JsonPathExecContext *cxt,
-                                         JsonPathItem *jsp, JsonbValue *jb);
+                                                                                 JsonPathItem *jsp, JsonbValue *jb);
 static JsonPathExecResult executeAnyItem(JsonPathExecContext *cxt,
-                          JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
-                          uint32 level, uint32 first, uint32 last,
-                          bool ignoreStructuralErrors, bool unwrapNext);
+                                                                                JsonPathItem *jsp, JsonbContainer *jbc, JsonValueList *found,
+                                                                                uint32 level, uint32 first, uint32 last,
+                                                                                bool ignoreStructuralErrors, bool unwrapNext);
 static JsonPathBool executePredicate(JsonPathExecContext *cxt,
-                                JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
-                                JsonbValue *jb, bool unwrapRightArg,
-                                JsonPathPredicateCallback exec, void *param);
+                                                                        JsonPathItem *pred, JsonPathItem *larg, JsonPathItem *rarg,
+                                                                        JsonbValue *jb, bool unwrapRightArg,
+                                                                        JsonPathPredicateCallback exec, void *param);
 static JsonPathExecResult executeBinaryArithmExpr(JsonPathExecContext *cxt,
-                                               JsonPathItem *jsp, JsonbValue *jb,
-                                               BinaryArithmFunc func, JsonValueList *found);
+                                                                                                 JsonPathItem *jsp, JsonbValue *jb,
+                                                                                                 BinaryArithmFunc func, JsonValueList *found);
 static JsonPathExecResult executeUnaryArithmExpr(JsonPathExecContext *cxt,
-                                          JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
-                                          JsonValueList *found);
+                                                                                                JsonPathItem *jsp, JsonbValue *jb, PGFunction func,
+                                                                                                JsonValueList *found);
 static JsonPathBool executeStartsWith(JsonPathItem *jsp,
-                                 JsonbValue *whole, JsonbValue *initial, void *param);
+                                                                         JsonbValue *whole, JsonbValue *initial, void *param);
 static JsonPathBool executeLikeRegex(JsonPathItem *jsp, JsonbValue *str,
-                                JsonbValue *rarg, void *param);
+                                                                        JsonbValue *rarg, void *param);
 static JsonPathExecResult executeNumericItemMethod(JsonPathExecContext *cxt,
-                                                JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
-                                                JsonValueList *found);
+                                                                                                  JsonPathItem *jsp, JsonbValue *jb, bool unwrap, PGFunction func,
+                                                                                                  JsonValueList *found);
+static JsonPathExecResult executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
+                                                                                               JsonbValue *jb, JsonValueList *found);
 static JsonPathExecResult executeKeyValueMethod(JsonPathExecContext *cxt,
-                                         JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
+                                                                                               JsonPathItem *jsp, JsonbValue *jb, JsonValueList *found);
 static JsonPathExecResult appendBoolResult(JsonPathExecContext *cxt,
-                                JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
+                                                                                  JsonPathItem *jsp, JsonValueList *found, JsonPathBool res);
 static void getJsonPathItem(JsonPathExecContext *cxt, JsonPathItem *item,
-                               JsonbValue *value);
+                                                       JsonbValue *value);
 static void getJsonPathVariable(JsonPathExecContext *cxt,
-                                       JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
+                                                               JsonPathItem *variable, Jsonb *vars, JsonbValue *value);
 static int     JsonbArraySize(JsonbValue *jb);
 static JsonPathBool executeComparison(JsonPathItem *cmp, JsonbValue *lv,
-                                 JsonbValue *rv, void *p);
-static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2);
+                                                                         JsonbValue *rv, void *p);
+static JsonPathBool compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2,
+                                                                bool useTz);
 static int     compareNumeric(Numeric a, Numeric b);
 static JsonbValue *copyJsonbValue(JsonbValue *src);
 static JsonPathExecResult getArrayIndex(JsonPathExecContext *cxt,
-                         JsonPathItem *jsp, JsonbValue *jb, int32 *index);
+                                                                               JsonPathItem *jsp, JsonbValue *jb, int32 *index);
 static JsonBaseObjectInfo setBaseObject(JsonPathExecContext *cxt,
-                         JsonbValue *jbv, int32 id);
+                                                                               JsonbValue *jbv, int32 id);
 static void JsonValueListAppend(JsonValueList *jvl, JsonbValue *jbv);
 static int     JsonValueListLength(const JsonValueList *jvl);
 static bool JsonValueListIsEmpty(JsonValueList *jvl);
 static JsonbValue *JsonValueListHead(JsonValueList *jvl);
 static List *JsonValueListGetList(JsonValueList *jvl);
 static void JsonValueListInitIterator(const JsonValueList *jvl,
-                                                 JsonValueListIterator *it);
+                                                                         JsonValueListIterator *it);
 static JsonbValue *JsonValueListNext(const JsonValueList *jvl,
-                                 JsonValueListIterator *it);
+                                                                        JsonValueListIterator *it);
 static int     JsonbType(JsonbValue *jb);
 static JsonbValue *JsonbInitBinary(JsonbValue *jbv, Jsonb *jb);
 static int     JsonbType(JsonbValue *jb);
 static JsonbValue *getScalar(JsonbValue *scalar, enum jbvType type);
 static JsonbValue *wrapItemsInArray(const JsonValueList *items);
+static int     compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
+                                                       bool useTz, bool *have_error);
 
 /****************** User interface to JsonPath executor ********************/
 
@@ -272,8 +269,8 @@ static JsonbValue *wrapItemsInArray(const JsonValueList *items);
  *             SQL/JSON.  Regarding jsonb_path_match(), this function doesn't have
  *             an analogy in SQL/JSON, so we define its behavior on our own.
  */
-Datum
-jsonb_path_exists(PG_FUNCTION_ARGS)
+static Datum
+jsonb_path_exists_internal(FunctionCallInfo fcinfo, bool tz)
 {
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
@@ -287,7 +284,7 @@ jsonb_path_exists(PG_FUNCTION_ARGS)
                silent = PG_GETARG_BOOL(3);
        }
 
-       res = executeJsonPath(jp, vars, jb, !silent, NULL);
+       res = executeJsonPath(jp, vars, jb, !silent, NULL, tz);
 
        PG_FREE_IF_COPY(jb, 0);
        PG_FREE_IF_COPY(jp, 1);
@@ -298,6 +295,18 @@ jsonb_path_exists(PG_FUNCTION_ARGS)
        PG_RETURN_BOOL(res == jperOk);
 }
 
+Datum
+jsonb_path_exists(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_exists_internal(fcinfo, false);
+}
+
+Datum
+jsonb_path_exists_tz(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_exists_internal(fcinfo, true);
+}
+
 /*
  * jsonb_path_exists_opr
  *             Implementation of operator "jsonb @? jsonpath" (2-argument version of
@@ -307,7 +316,7 @@ Datum
 jsonb_path_exists_opr(PG_FUNCTION_ARGS)
 {
        /* just call the other one -- it can handle both cases */
-       return jsonb_path_exists(fcinfo);
+       return jsonb_path_exists_internal(fcinfo, false);
 }
 
 /*
@@ -315,8 +324,8 @@ jsonb_path_exists_opr(PG_FUNCTION_ARGS)
  *             Returns jsonpath predicate result item for the specified jsonb value.
  *             See jsonb_path_exists() comment for details regarding error handling.
  */
-Datum
-jsonb_path_match(PG_FUNCTION_ARGS)
+static Datum
+jsonb_path_match_internal(FunctionCallInfo fcinfo, bool tz)
 {
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
@@ -330,7 +339,7 @@ jsonb_path_match(PG_FUNCTION_ARGS)
                silent = PG_GETARG_BOOL(3);
        }
 
-       (void) executeJsonPath(jp, vars, jb, !silent, &found);
+       (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
 
        PG_FREE_IF_COPY(jb, 0);
        PG_FREE_IF_COPY(jp, 1);
@@ -348,13 +357,24 @@ jsonb_path_match(PG_FUNCTION_ARGS)
 
        if (!silent)
                ereport(ERROR,
-                               (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
-                                errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
-                                errdetail("expression should return a singleton boolean")));
+                               (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
+                                errmsg("single boolean result is expected")));
 
        PG_RETURN_NULL();
 }
 
+Datum
+jsonb_path_match(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_match_internal(fcinfo, false);
+}
+
+Datum
+jsonb_path_match_tz(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_match_internal(fcinfo, true);
+}
+
 /*
  * jsonb_path_match_opr
  *             Implementation of operator "jsonb @@ jsonpath" (2-argument version of
@@ -364,7 +384,7 @@ Datum
 jsonb_path_match_opr(PG_FUNCTION_ARGS)
 {
        /* just call the other one -- it can handle both cases */
-       return jsonb_path_match(fcinfo);
+       return jsonb_path_match_internal(fcinfo, false);
 }
 
 /*
@@ -372,8 +392,8 @@ jsonb_path_match_opr(PG_FUNCTION_ARGS)
  *             Executes jsonpath for given jsonb document and returns result as
  *             rowset.
  */
-Datum
-jsonb_path_query(PG_FUNCTION_ARGS)
+static Datum
+jsonb_path_query_internal(FunctionCallInfo fcinfo, bool tz)
 {
        FuncCallContext *funcctx;
        List       *found;
@@ -397,7 +417,7 @@ jsonb_path_query(PG_FUNCTION_ARGS)
                vars = PG_GETARG_JSONB_P_COPY(2);
                silent = PG_GETARG_BOOL(3);
 
-               (void) executeJsonPath(jp, vars, jb, !silent, &found);
+               (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
 
                funcctx->user_fctx = JsonValueListGetList(&found);
 
@@ -418,13 +438,25 @@ jsonb_path_query(PG_FUNCTION_ARGS)
        SRF_RETURN_NEXT(funcctx, JsonbPGetDatum(JsonbValueToJsonb(v)));
 }
 
+Datum
+jsonb_path_query(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_query_internal(fcinfo, false);
+}
+
+Datum
+jsonb_path_query_tz(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_query_internal(fcinfo, true);
+}
+
 /*
  * jsonb_path_query_array
  *             Executes jsonpath for given jsonb document and returns result as
  *             jsonb array.
  */
-Datum
-jsonb_path_query_array(FunctionCallInfo fcinfo)
+static Datum
+jsonb_path_query_array_internal(FunctionCallInfo fcinfo, bool tz)
 {
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
@@ -432,18 +464,30 @@ jsonb_path_query_array(FunctionCallInfo fcinfo)
        Jsonb      *vars = PG_GETARG_JSONB_P(2);
        bool            silent = PG_GETARG_BOOL(3);
 
-       (void) executeJsonPath(jp, vars, jb, !silent, &found);
+       (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
 
        PG_RETURN_JSONB_P(JsonbValueToJsonb(wrapItemsInArray(&found)));
 }
 
+Datum
+jsonb_path_query_array(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_query_array_internal(fcinfo, false);
+}
+
+Datum
+jsonb_path_query_array_tz(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_query_array_internal(fcinfo, true);
+}
+
 /*
  * jsonb_path_query_first
  *             Executes jsonpath for given jsonb document and returns first result
  *             item.  If there are no items, NULL returned.
  */
-Datum
-jsonb_path_query_first(FunctionCallInfo fcinfo)
+static Datum
+jsonb_path_query_first_internal(FunctionCallInfo fcinfo, bool tz)
 {
        Jsonb      *jb = PG_GETARG_JSONB_P(0);
        JsonPath   *jp = PG_GETARG_JSONPATH_P(1);
@@ -451,7 +495,7 @@ jsonb_path_query_first(FunctionCallInfo fcinfo)
        Jsonb      *vars = PG_GETARG_JSONB_P(2);
        bool            silent = PG_GETARG_BOOL(3);
 
-       (void) executeJsonPath(jp, vars, jb, !silent, &found);
+       (void) executeJsonPath(jp, vars, jb, !silent, &found, tz);
 
        if (JsonValueListLength(&found) >= 1)
                PG_RETURN_JSONB_P(JsonbValueToJsonb(JsonValueListHead(&found)));
@@ -459,6 +503,18 @@ jsonb_path_query_first(FunctionCallInfo fcinfo)
                PG_RETURN_NULL();
 }
 
+Datum
+jsonb_path_query_first(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_query_first_internal(fcinfo, false);
+}
+
+Datum
+jsonb_path_query_first_tz(PG_FUNCTION_ARGS)
+{
+       return jsonb_path_query_first_internal(fcinfo, true);
+}
+
 /********************Execute functions for JsonPath**************************/
 
 /*
@@ -470,9 +526,10 @@ jsonb_path_query_first(FunctionCallInfo fcinfo)
  * 'throwErrors' - whether we should throw suppressible errors
  * 'result' - list to store result items into
  *
- * Returns an error happens during processing or NULL on no error.
+ * Returns an error if a recoverable error happens during processing, or NULL
+ * on no error.
  *
- * Note, jsonb and jsonpath values should be avaliable and untoasted during
+ * Note, jsonb and jsonpath values should be available and untoasted during
  * work because JsonPathItem, JsonbValue and result item could have pointers
  * into input values.  If caller needs to just check if document matches
  * jsonpath, then it doesn't provide a result arg.  In this case executor
@@ -481,7 +538,7 @@ jsonb_path_query_first(FunctionCallInfo fcinfo)
  */
 static JsonPathExecResult
 executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
-                               JsonValueList *result)
+                               JsonValueList *result, bool useTz)
 {
        JsonPathExecContext cxt;
        JsonPathExecResult res;
@@ -497,8 +554,8 @@ executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
        {
                ereport(ERROR,
                                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-                                errmsg("jsonb containing jsonpath variables "
-                                               "is not an object")));
+                                errmsg("\"vars\" argument is not an object"),
+                                errdetail("Jsonpath parameters should be encoded as key-value pairs of \"vars\" object.")));
        }
 
        cxt.vars = vars;
@@ -511,6 +568,7 @@ executeJsonPath(JsonPath *path, Jsonb *vars, Jsonb *json, bool throwErrors,
        cxt.lastGeneratedObjectId = vars ? 2 : 1;
        cxt.innermostArraySize = -1;
        cxt.throwErrors = throwErrors;
+       cxt.useTz = useTz;
 
        if (jspStrictAbsenseOfErrors(&cxt) && !result)
        {
@@ -607,24 +665,16 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                }
                                else if (!jspIgnoreStructuralErrors(cxt))
                                {
-                                       StringInfoData keybuf;
-                                       char       *keystr;
-
                                        Assert(found);
 
                                        if (!jspThrowErrors(cxt))
                                                return jperError;
 
-                                       initStringInfo(&keybuf);
-
-                                       keystr = pnstrdup(key.val.string.val, key.val.string.len);
-                                       escape_json(&keybuf, keystr);
-
                                        ereport(ERROR,
-                                                       (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND), \
-                                                        errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
-                                                        errdetail("JSON object does not contain key %s",
-                                                                          keybuf.data)));
+                                                       (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND), \
+                                                        errmsg("JSON object does not contain key \"%s\"",
+                                                                       pnstrdup(key.val.string.val,
+                                                                                        key.val.string.len))));
                                }
                        }
                        else if (unwrap && JsonbType(jb) == jbvArray)
@@ -633,10 +683,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                        {
                                Assert(found);
                                RETURN_ERROR(ereport(ERROR,
-                                                                        (errcode(ERRCODE_JSON_MEMBER_NOT_FOUND),
-                                                                         errmsg(ERRMSG_JSON_MEMBER_NOT_FOUND),
-                                                                         errdetail("jsonpath member accessor can "
-                                                                                               "only be applied to an object"))));
+                                                                        (errcode(ERRCODE_SQL_JSON_MEMBER_NOT_FOUND),
+                                                                         errmsg("jsonpath member accessor can only be applied to an object"))));
                        }
                        break;
 
@@ -664,10 +712,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                res = executeNextItem(cxt, jsp, NULL, jb, found, true);
                        else if (!jspIgnoreStructuralErrors(cxt))
                                RETURN_ERROR(ereport(ERROR,
-                                                                        (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
-                                                                         errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
-                                                                         errdetail("jsonpath wildcard array accessor "
-                                                                                               "can only be applied to an array"))));
+                                                                        (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
+                                                                         errmsg("jsonpath wildcard array accessor can only be applied to an array"))));
                        break;
 
                case jpiIndexArray:
@@ -714,10 +760,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                                 index_from > index_to ||
                                                 index_to >= size))
                                                RETURN_ERROR(ereport(ERROR,
-                                                                                        (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
-                                                                                         errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
-                                                                                         errdetail("jsonpath array subscript is "
-                                                                                                               "out of bounds"))));
+                                                                                        (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+                                                                                         errmsg("jsonpath array subscript is out of bounds"))));
 
                                        if (index_from < 0)
                                                index_from = 0;
@@ -773,10 +817,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                        else if (!jspIgnoreStructuralErrors(cxt))
                        {
                                RETURN_ERROR(ereport(ERROR,
-                                                                        (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
-                                                                         errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
-                                                                         errdetail("jsonpath array accessor can "
-                                                                                               "only be applied to an array"))));
+                                                                        (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
+                                                                         errmsg("jsonpath array accessor can only be applied to an array"))));
                        }
                        break;
 
@@ -788,8 +830,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                bool            hasNext = jspGetNext(jsp, &elem);
 
                                if (cxt->innermostArraySize < 0)
-                                       elog(ERROR, "evaluating jsonpath LAST outside of "
-                                                "array subscript");
+                                       elog(ERROR, "evaluating jsonpath LAST outside of array subscript");
 
                                if (!hasNext && !found)
                                {
@@ -830,10 +871,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                        {
                                Assert(found);
                                RETURN_ERROR(ereport(ERROR,
-                                                                        (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
-                                                                         errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
-                                                                         errdetail("jsonpath wildcard member accessor "
-                                                                                               "can only be applied to an object"))));
+                                                                        (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
+                                                                         errmsg("jsonpath wildcard member accessor can only be applied to an object"))));
                        }
                        break;
 
@@ -962,11 +1001,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                        {
                                                if (!jspIgnoreStructuralErrors(cxt))
                                                        RETURN_ERROR(ereport(ERROR,
-                                                                                                (errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
-                                                                                                 errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
-                                                                                                 errdetail("jsonpath item method .%s() "
-                                                                                                                       "can only be applied to an array",
-                                                                                                                       jspOperationName(jsp->type)))));
+                                                                                                (errcode(ERRCODE_SQL_JSON_ARRAY_NOT_FOUND),
+                                                                                                 errmsg("jsonpath item method .%s() can only be applied to an array",
+                                                                                                                jspOperationName(jsp->type)))));
                                                break;
                                        }
 
@@ -1018,12 +1055,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
                                        if (have_error)
                                                RETURN_ERROR(ereport(ERROR,
-                                                                                        (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
-                                                                                         errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
-                                                                                         errdetail("jsonpath item method .%s() "
-                                                                                                               "can only be applied to "
-                                                                                                               "a numeric value",
-                                                                                                               jspOperationName(jsp->type)))));
+                                                                                        (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+                                                                                         errmsg("jsonpath item method .%s() can only be applied to a numeric value",
+                                                                                                        jspOperationName(jsp->type)))));
                                        res = jperOk;
                                }
                                else if (jb->type == jbvString)
@@ -1042,11 +1076,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
                                        if (have_error || isinf(val))
                                                RETURN_ERROR(ereport(ERROR,
-                                                                                        (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
-                                                                                         errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
-                                                                                         errdetail("jsonpath item method .%s() can "
-                                                                                                               "only be applied to a numeric value",
-                                                                                                               jspOperationName(jsp->type)))));
+                                                                                        (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+                                                                                         errmsg("jsonpath item method .%s() can only be applied to a numeric value",
+                                                                                                        jspOperationName(jsp->type)))));
 
                                        jb = &jbv;
                                        jb->type = jbvNumeric;
@@ -1057,17 +1089,20 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
                                if (res == jperNotFound)
                                        RETURN_ERROR(ereport(ERROR,
-                                                                                (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
-                                                                                 errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
-                                                                                 errdetail("jsonpath item method .%s() "
-                                                                                                       "can only be applied to a "
-                                                                                                       "string or numeric value",
-                                                                                                       jspOperationName(jsp->type)))));
+                                                                                (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+                                                                                 errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
+                                                                                                jspOperationName(jsp->type)))));
 
                                res = executeNextItem(cxt, jsp, NULL, jb, found, true);
                        }
                        break;
 
+               case jpiDatetime:
+                       if (unwrap && JsonbType(jb) == jbvArray)
+                               return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
+
+                       return executeDateTimeMethod(cxt, jsp, jb, found);
+
                case jpiKeyValue:
                        if (unwrap && JsonbType(jb) == jbvArray)
                                return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -1254,7 +1289,7 @@ executeBoolItem(JsonPathExecContext *cxt, JsonPathItem *jsp,
                        jspGetLeftArg(jsp, &larg);
                        jspGetRightArg(jsp, &rarg);
                        return executePredicate(cxt, jsp, &larg, &rarg, jb, true,
-                                                                       executeComparison, NULL);
+                                                                       executeComparison, cxt);
 
                case jpiStartsWith:             /* 'whole STARTS WITH initial' */
                        jspGetLeftArg(jsp, &larg);      /* 'whole' */
@@ -1529,7 +1564,7 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
        /*
         * XXX: By standard only operands of multiplicative expressions are
-        * unwrapped.  We extend it to other binary arithmetics expressions too.
+        * unwrapped.  We extend it to other binary arithmetic expressions too.
         */
        jper = executeItemOptUnwrapResult(cxt, &elem, jb, true, &lseq);
        if (jperIsError(jper))
@@ -1544,20 +1579,16 @@ executeBinaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
        if (JsonValueListLength(&lseq) != 1 ||
                !(lval = getScalar(JsonValueListHead(&lseq), jbvNumeric)))
                RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
-                                                         errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
-                                                         errdetail("left operand of binary jsonpath operator %s "
-                                                                               "is not a singleton numeric value",
-                                                                               jspOperationName(jsp->type)))));
+                                                        (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
+                                                         errmsg("left operand of jsonpath operator %s is not a single numeric value",
+                                                                        jspOperationName(jsp->type)))));
 
        if (JsonValueListLength(&rseq) != 1 ||
                !(rval = getScalar(JsonValueListHead(&rseq), jbvNumeric)))
                RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
-                                                         errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
-                                                         errdetail("right operand of binary jsonpath operator %s "
-                                                                               "is not a singleton numeric value",
-                                                                               jspOperationName(jsp->type)))));
+                                                        (errcode(ERRCODE_SINGLETON_SQL_JSON_ITEM_REQUIRED),
+                                                         errmsg("right operand of jsonpath operator %s is not a single numeric value",
+                                                                        jspOperationName(jsp->type)))));
 
        if (jspThrowErrors(cxt))
        {
@@ -1623,11 +1654,9 @@ executeUnaryArithmExpr(JsonPathExecContext *cxt, JsonPathItem *jsp,
                                continue;               /* skip non-numerics processing */
 
                        RETURN_ERROR(ereport(ERROR,
-                                                                (errcode(ERRCODE_JSON_NUMBER_NOT_FOUND),
-                                                                 errmsg(ERRMSG_JSON_NUMBER_NOT_FOUND),
-                                                                 errdetail("operand of unary jsonpath operator %s "
-                                                                                       "is not a numeric value",
-                                                                                       jspOperationName(jsp->type)))));
+                                                                (errcode(ERRCODE_SQL_JSON_NUMBER_NOT_FOUND),
+                                                                 errmsg("operand of unary jsonpath operator %s is not a numeric value",
+                                                                                jspOperationName(jsp->type)))));
                }
 
                if (func)
@@ -1692,23 +1721,10 @@ executeLikeRegex(JsonPathItem *jsp, JsonbValue *str, JsonbValue *rarg,
        /* Cache regex text and converted flags. */
        if (!cxt->regex)
        {
-               uint32          flags = jsp->content.like_regex.flags;
-
                cxt->regex =
                        cstring_to_text_with_len(jsp->content.like_regex.pattern,
                                                                         jsp->content.like_regex.patternlen);
-
-               /* Convert regex flags. */
-               cxt->cflags = REG_ADVANCED;
-
-               if (flags & JSP_REGEX_ICASE)
-                       cxt->cflags |= REG_ICASE;
-               if (flags & JSP_REGEX_MLINE)
-                       cxt->cflags |= REG_NEWLINE;
-               if (flags & JSP_REGEX_SLINE)
-                       cxt->cflags &= ~REG_NEWLINE;
-               if (flags & JSP_REGEX_WSPACE)
-                       cxt->cflags |= REG_EXPANDED;
+               cxt->cflags = jspConvertRegexFlags(jsp->content.like_regex.flags);
        }
 
        if (RE_compile_and_execute(cxt->regex, str->val.string.val,
@@ -1736,14 +1752,11 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
        if (!(jb = getScalar(jb, jbvNumeric)))
                RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_NON_NUMERIC_JSON_ITEM),
-                                                         errmsg(ERRMSG_NON_NUMERIC_JSON_ITEM),
-                                                         errdetail("jsonpath item method .%s() can only "
-                                                                               "be applied to a numeric value",
-                                                                               jspOperationName(jsp->type)))));
+                                                        (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+                                                         errmsg("jsonpath item method .%s() can only be applied to a numeric value",
+                                                                        jspOperationName(jsp->type)))));
 
-       datum = NumericGetDatum(jb->val.numeric);
-       datum = DirectFunctionCall1(func, datum);
+       datum = DirectFunctionCall1(func, NumericGetDatum(jb->val.numeric));
 
        if (!jspGetNext(jsp, &next) && !found)
                return jperOk;
@@ -1755,6 +1768,136 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
        return executeNextItem(cxt, jsp, &next, jb, found, false);
 }
 
+/*
+ * Implementation of the .datetime() method.
+ *
+ * Converts a string into a date/time value. The actual type is determined at run time.
+ * If an argument is provided, this argument is used as a template string.
+ * Otherwise, the first fitting ISO format is selected.
+ */
+static JsonPathExecResult
+executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
+                                         JsonbValue *jb, JsonValueList *found)
+{
+       JsonbValue      jbvbuf;
+       Datum           value;
+       text       *datetime;
+       Oid                     typid;
+       int32           typmod = -1;
+       int                     tz = 0;
+       bool            hasNext;
+       JsonPathExecResult res = jperNotFound;
+       JsonPathItem elem;
+
+       if (!(jb = getScalar(jb, jbvString)))
+               RETURN_ERROR(ereport(ERROR,
+                                                        (errcode(ERRCODE_INVALID_ARGUMENT_FOR_JSON_DATETIME_FUNCTION),
+                                                         errmsg("jsonpath item method .%s() can only be applied to a string",
+                                                                        jspOperationName(jsp->type)))));
+
+       datetime = cstring_to_text_with_len(jb->val.string.val,
+                                                                               jb->val.string.len);
+
+       if (jsp->content.arg)
+       {
+               text       *template;
+               char       *template_str;
+               int                     template_len;
+               bool            have_error = false;
+
+               jspGetArg(jsp, &elem);
+
+               if (elem.type != jpiString)
+                       elog(ERROR, "invalid jsonpath item type for .datetime() argument");
+
+               template_str = jspGetString(&elem, &template_len);
+
+               template = cstring_to_text_with_len(template_str,
+                                                                                       template_len);
+
+               value = parse_datetime(datetime, template, true,
+                                                          &typid, &typmod, &tz,
+                                                          jspThrowErrors(cxt) ? NULL : &have_error);
+
+               if (have_error)
+                       res = jperError;
+               else
+                       res = jperOk;
+       }
+       else
+       {
+               /*
+                * According to SQL/JSON standard enumerate ISO formats for: date,
+                * timetz, time, timestamptz, timestamp.
+                */
+               static const char *fmt_str[] =
+               {
+                       "yyyy-mm-dd",
+                       "HH24:MI:SS TZH:TZM",
+                       "HH24:MI:SS TZH",
+                       "HH24:MI:SS",
+                       "yyyy-mm-dd HH24:MI:SS TZH:TZM",
+                       "yyyy-mm-dd HH24:MI:SS TZH",
+                       "yyyy-mm-dd HH24:MI:SS"
+               };
+
+               /* cache for format texts */
+               static text *fmt_txt[lengthof(fmt_str)] = {0};
+               int                     i;
+
+               /* loop until datetime format fits */
+               for (i = 0; i < lengthof(fmt_str); i++)
+               {
+                       bool            have_error = false;
+
+                       if (!fmt_txt[i])
+                       {
+                               MemoryContext oldcxt =
+                               MemoryContextSwitchTo(TopMemoryContext);
+
+                               fmt_txt[i] = cstring_to_text(fmt_str[i]);
+                               MemoryContextSwitchTo(oldcxt);
+                       }
+
+                       value = parse_datetime(datetime, fmt_txt[i], true,
+                                                                  &typid, &typmod, &tz,
+                                                                  &have_error);
+
+                       if (!have_error)
+                       {
+                               res = jperOk;
+                               break;
+                       }
+               }
+
+               if (res == jperNotFound)
+                       RETURN_ERROR(ereport(ERROR,
+                                                                (errcode(ERRCODE_INVALID_ARGUMENT_FOR_JSON_DATETIME_FUNCTION),
+                                                                 errmsg("datetime format is not unrecognized"),
+                                                                 errhint("use datetime template argument for explicit format specification"))));
+       }
+
+       pfree(datetime);
+
+       if (jperIsError(res))
+               return res;
+
+       hasNext = jspGetNext(jsp, &elem);
+
+       if (!hasNext && !found)
+               return res;
+
+       jb = hasNext ? &jbvbuf : palloc(sizeof(*jb));
+
+       jb->type = jbvDatetime;
+       jb->val.datetime.value = value;
+       jb->val.datetime.typid = typid;
+       jb->val.datetime.typmod = typmod;
+       jb->val.datetime.tz = tz;
+
+       return executeNextItem(cxt, jsp, &elem, jb, found, hasNext);
+}
+
 /*
  * Implementation of .keyvalue() method.
  *
@@ -1798,11 +1941,9 @@ executeKeyValueMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
 
        if (JsonbType(jb) != jbvObject || jb->type != jbvBinary)
                RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_JSON_OBJECT_NOT_FOUND),
-                                                         errmsg(ERRMSG_JSON_OBJECT_NOT_FOUND),
-                                                         errdetail("jsonpath item method .%s() "
-                                                                               "can only be applied to an object",
-                                                                               jspOperationName(jsp->type)))));
+                                                        (errcode(ERRCODE_SQL_JSON_OBJECT_NOT_FOUND),
+                                                         errmsg("jsonpath item method .%s() can only be applied to an object",
+                                                                        jspOperationName(jsp->type)))));
 
        jbc = jb->val.binary.data;
 
@@ -1984,7 +2125,7 @@ getJsonPathVariable(JsonPathExecContext *cxt, JsonPathItem *variable,
        {
                ereport(ERROR,
                                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("cannot find jsonpath variable '%s'",
+                                errmsg("could not find jsonpath variable \"%s\"",
                                                pnstrdup(varName, varNameLength))));
        }
 
@@ -2017,14 +2158,104 @@ JsonbArraySize(JsonbValue *jb)
 static JsonPathBool
 executeComparison(JsonPathItem *cmp, JsonbValue *lv, JsonbValue *rv, void *p)
 {
-       return compareItems(cmp->type, lv, rv);
+       JsonPathExecContext *cxt = (JsonPathExecContext *) p;
+
+       return compareItems(cmp->type, lv, rv, cxt->useTz);
+}
+
+/*
+ * Perform per-byte comparison of two strings.
+ */
+static int
+binaryCompareStrings(const char *s1, int len1,
+                                        const char *s2, int len2)
+{
+       int                     cmp;
+
+       cmp = memcmp(s1, s2, Min(len1, len2));
+
+       if (cmp != 0)
+               return cmp;
+
+       if (len1 == len2)
+               return 0;
+
+       return len1 < len2 ? -1 : 1;
+}
+
+/*
+ * Compare two strings in the current server encoding using Unicode codepoint
+ * collation.
+ */
+static int
+compareStrings(const char *mbstr1, int mblen1,
+                          const char *mbstr2, int mblen2)
+{
+       if (GetDatabaseEncoding() == PG_SQL_ASCII ||
+               GetDatabaseEncoding() == PG_UTF8)
+       {
+               /*
+                * It's known property of UTF-8 strings that their per-byte comparison
+                * result matches codepoints comparison result.  ASCII can be
+                * considered as special case of UTF-8.
+                */
+               return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
+       }
+       else
+       {
+               char       *utf8str1,
+                                  *utf8str2;
+               int                     cmp,
+                                       utf8len1,
+                                       utf8len2;
+
+               /*
+                * We have to convert other encodings to UTF-8 first, then compare.
+                * Input strings may be not null-terminated and pg_server_to_any() may
+                * return them "as is".  So, use strlen() only if there is real
+                * conversion.
+                */
+               utf8str1 = pg_server_to_any(mbstr1, mblen1, PG_UTF8);
+               utf8str2 = pg_server_to_any(mbstr2, mblen2, PG_UTF8);
+               utf8len1 = (mbstr1 == utf8str1) ? mblen1 : strlen(utf8str1);
+               utf8len2 = (mbstr2 == utf8str2) ? mblen2 : strlen(utf8str2);
+
+               cmp = binaryCompareStrings(utf8str1, utf8len1, utf8str2, utf8len2);
+
+               /*
+                * If pg_server_to_any() did no real conversion, then we actually
+                * compared original strings.  So, we already done.
+                */
+               if (mbstr1 == utf8str1 && mbstr2 == utf8str2)
+                       return cmp;
+
+               /* Free memory if needed */
+               if (mbstr1 != utf8str1)
+                       pfree(utf8str1);
+               if (mbstr2 != utf8str2)
+                       pfree(utf8str2);
+
+               /*
+                * When all Unicode codepoints are equal, return result of binary
+                * comparison.  In some edge cases, same characters may have different
+                * representations in encoding.  Then our behavior could diverge from
+                * standard.  However, that allow us to do simple binary comparison
+                * for "==" operator, which is performance critical in typical cases.
+                * In future to implement strict standard conformance, we can do
+                * normalization of input JSON strings.
+                */
+               if (cmp == 0)
+                       return binaryCompareStrings(mbstr1, mblen1, mbstr2, mblen2);
+               else
+                       return cmp;
+       }
 }
 
 /*
  * Compare two SQL/JSON items using comparison operation 'op'.
  */
 static JsonPathBool
-compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
+compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2, bool useTz)
 {
        int                     cmp;
        bool            res;
@@ -2062,9 +2293,23 @@ compareItems(int32 op, JsonbValue *jb1, JsonbValue *jb2)
                                                   jb2->val.string.val,
                                                   jb1->val.string.len) ? jpbFalse : jpbTrue;
 
-                       cmp = varstr_cmp(jb1->val.string.val, jb1->val.string.len,
-                                                        jb2->val.string.val, jb2->val.string.len,
-                                                        DEFAULT_COLLATION_OID);
+                       cmp = compareStrings(jb1->val.string.val, jb1->val.string.len,
+                                                                jb2->val.string.val, jb2->val.string.len);
+                       break;
+               case jbvDatetime:
+                       {
+                               bool            cast_error;
+
+                               cmp = compareDatetime(jb1->val.datetime.value,
+                                                                         jb1->val.datetime.typid,
+                                                                         jb2->val.datetime.value,
+                                                                         jb2->val.datetime.typid,
+                                                                         useTz,
+                                                                         &cast_error);
+
+                               if (cast_error)
+                                       return jpbUnknown;
+                       }
                        break;
 
                case jbvBinary:
@@ -2109,8 +2354,8 @@ static int
 compareNumeric(Numeric a, Numeric b)
 {
        return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
-                                                                                        PointerGetDatum(a),
-                                                                                        PointerGetDatum(b)));
+                                                                                        NumericGetDatum(a),
+                                                                                        NumericGetDatum(b)));
 }
 
 static JsonbValue *
@@ -2143,10 +2388,8 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
        if (JsonValueListLength(&found) != 1 ||
                !(jbv = getScalar(JsonValueListHead(&found), jbvNumeric)))
                RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
-                                                         errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
-                                                         errdetail("jsonpath array subscript is not a "
-                                                                               "singleton numeric value"))));
+                                                        (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+                                                         errmsg("jsonpath array subscript is not a single numeric value"))));
 
        numeric_index = DirectFunctionCall2(numeric_trunc,
                                                                                NumericGetDatum(jbv->val.numeric),
@@ -2157,10 +2400,8 @@ getArrayIndex(JsonPathExecContext *cxt, JsonPathItem *jsp, JsonbValue *jb,
 
        if (have_error)
                RETURN_ERROR(ereport(ERROR,
-                                                        (errcode(ERRCODE_INVALID_JSON_SUBSCRIPT),
-                                                         errmsg(ERRMSG_INVALID_JSON_SUBSCRIPT),
-                                                         errdetail("jsonpath array subscript is "
-                                                                               "out of integer range"))));
+                                                        (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+                                                         errmsg("jsonpath array subscript is out of integer range"))));
 
        return jperOk;
 }
@@ -2225,16 +2466,19 @@ JsonValueListInitIterator(const JsonValueList *jvl, JsonValueListIterator *it)
        if (jvl->singleton)
        {
                it->value = jvl->singleton;
+               it->list = NIL;
                it->next = NULL;
        }
-       else if (list_head(jvl->list) != NULL)
+       else if (jvl->list != NIL)
        {
                it->value = (JsonbValue *) linitial(jvl->list);
-               it->next = lnext(list_head(jvl->list));
+               it->list = jvl->list;
+               it->next = list_second_cell(jvl->list);
        }
        else
        {
                it->value = NULL;
+               it->list = NIL;
                it->next = NULL;
        }
 }
@@ -2250,7 +2494,7 @@ JsonValueListNext(const JsonValueList *jvl, JsonValueListIterator *it)
        if (it->next)
        {
                it->value = lfirst(it->next);
-               it->next = lnext(it->next);
+               it->next = lnext(it->list, it->next);
        }
        else
        {
@@ -2326,3 +2570,277 @@ wrapItemsInArray(const JsonValueList *items)
 
        return pushJsonbValue(&ps, WJB_END_ARRAY, NULL);
 }
+
+/* Check if the timezone required for casting from type1 to type2 is used */
+static void
+checkTimezoneIsUsedForCast(bool useTz, const char *type1, const char *type2)
+{
+       if (!useTz)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("cannot convert value from %s to %s without timezone usage",
+                                               type1, type2),
+                                errhint("Use *_tz() function for timezone support.")));
+}
+
+/* Convert time datum to timetz datum */
+static Datum
+castTimeToTimeTz(Datum time, bool useTz)
+{
+       checkTimezoneIsUsedForCast(useTz, "time", "timetz");
+
+       return DirectFunctionCall1(time_timetz, time);
+}
+
+/*---
+ * Compares 'ts1' and 'ts2' timestamp, assuming that ts1 might be overflowed
+ * during cast from another datatype.
+ *
+ * 'overflow1' specifies overflow of 'ts1' value:
+ *  0 - no overflow,
+ * -1 - exceed lower boundary,
+ *  1 - exceed upper boundary.
+ */
+static int
+cmpTimestampWithOverflow(Timestamp ts1, int overflow1, Timestamp ts2)
+{
+       /*
+        * All the timestamps we deal with in jsonpath are produced by
+        * to_datetime() method.  So, they should be valid.
+        */
+       Assert(IS_VALID_TIMESTAMP(ts2));
+
+       /*
+        * Timestamp, which exceed lower (upper) bound, is always lower (higher)
+        * than any valid timestamp except minus (plus) infinity.
+        */
+       if (overflow1)
+       {
+               if (overflow1 < 0)
+               {
+                       if (TIMESTAMP_IS_NOBEGIN(ts2))
+                               return 1;
+                       else
+                               return -1;
+               }
+               if (overflow1 > 0)
+               {
+                       if (TIMESTAMP_IS_NOEND(ts2))
+                               return -1;
+                       else
+                               return 1;
+               }
+       }
+
+       return timestamp_cmp_internal(ts1, ts2);
+}
+
+/*
+ * Compare date to timestamptz without throwing overflow error during cast.
+ */
+static int
+cmpDateToTimestamp(DateADT date1, Timestamp ts2, bool useTz)
+{
+       TimestampTz ts1;
+       int                     overflow = 0;
+
+       ts1 = date2timestamp_opt_overflow(date1, &overflow);
+
+       return cmpTimestampWithOverflow(ts1, overflow, ts2);
+}
+
+/*
+ * Compare date to timestamptz without throwing overflow error during cast.
+ */
+static int
+cmpDateToTimestampTz(DateADT date1, TimestampTz tstz2, bool useTz)
+{
+       TimestampTz tstz1;
+       int                     overflow = 0;
+
+       checkTimezoneIsUsedForCast(useTz, "date", "timestamptz");
+
+       tstz1 = date2timestamptz_opt_overflow(date1, &overflow);
+
+       return cmpTimestampWithOverflow(tstz1, overflow, tstz2);
+}
+
+/*
+ * Compare timestamp to timestamptz without throwing overflow error during cast.
+ */
+static int
+cmpTimestampToTimestampTz(Timestamp ts1, TimestampTz tstz2, bool useTz)
+{
+       TimestampTz tstz1;
+       int                     overflow = 0;
+
+       checkTimezoneIsUsedForCast(useTz, "timestamp", "timestamptz");
+
+       tstz1 = timestamp2timestamptz_opt_overflow(ts1, &overflow);
+
+       return cmpTimestampWithOverflow(tstz1, overflow, tstz2);
+}
+
+/*
+ * Cross-type comparison of two datetime SQL/JSON items.  If items are
+ * uncomparable *cast_error flag is set, otherwise *cast_error is unset.
+ * If the cast requires timezone and it is not used, then explicit error is thrown.
+ */
+static int
+compareDatetime(Datum val1, Oid typid1, Datum val2, Oid typid2,
+                               bool useTz, bool *cast_error)
+{
+       PGFunction cmpfunc;
+
+       *cast_error = false;
+
+       switch (typid1)
+       {
+               case DATEOID:
+                       switch (typid2)
+                       {
+                               case DATEOID:
+                                       cmpfunc = date_cmp;
+
+                                       break;
+
+                               case TIMESTAMPOID:
+                                       return cmpDateToTimestamp(DatumGetDateADT(val1),
+                                                                                         DatumGetTimestamp(val2),
+                                                                                         useTz);
+
+                               case TIMESTAMPTZOID:
+                                       return cmpDateToTimestampTz(DatumGetDateADT(val1),
+                                                                                               DatumGetTimestampTz(val2),
+                                                                                               useTz);
+
+                               case TIMEOID:
+                               case TIMETZOID:
+                                       *cast_error = true; /* uncomparable types */
+                                       return 0;
+
+                               default:
+                                       elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+                                                typid2);
+                       }
+                       break;
+
+               case TIMEOID:
+                       switch (typid2)
+                       {
+                               case TIMEOID:
+                                       cmpfunc = time_cmp;
+
+                                       break;
+
+                               case TIMETZOID:
+                                       val1 = castTimeToTimeTz(val1, useTz);
+                                       cmpfunc = timetz_cmp;
+
+                                       break;
+
+                               case DATEOID:
+                               case TIMESTAMPOID:
+                               case TIMESTAMPTZOID:
+                                       *cast_error = true; /* uncomparable types */
+                                       return 0;
+
+                               default:
+                                       elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+                                                typid2);
+                       }
+                       break;
+
+               case TIMETZOID:
+                       switch (typid2)
+                       {
+                               case TIMEOID:
+                                       val2 = castTimeToTimeTz(val2, useTz);
+                                       cmpfunc = timetz_cmp;
+
+                                       break;
+
+                               case TIMETZOID:
+                                       cmpfunc = timetz_cmp;
+
+                                       break;
+
+                               case DATEOID:
+                               case TIMESTAMPOID:
+                               case TIMESTAMPTZOID:
+                                       *cast_error = true; /* uncomparable types */
+                                       return 0;
+
+                               default:
+                                       elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+                                                typid2);
+                       }
+                       break;
+
+               case TIMESTAMPOID:
+                       switch (typid2)
+                       {
+                               case DATEOID:
+                                       return -cmpDateToTimestamp(DatumGetDateADT(val2),
+                                                                                          DatumGetTimestamp(val1),
+                                                                                          useTz);
+
+                               case TIMESTAMPOID:
+                                       cmpfunc = timestamp_cmp;
+
+                                       break;
+
+                               case TIMESTAMPTZOID:
+                                       return cmpTimestampToTimestampTz(DatumGetTimestamp(val1),
+                                                                                                        DatumGetTimestampTz(val2),
+                                                                                                        useTz);
+
+                               case TIMEOID:
+                               case TIMETZOID:
+                                       *cast_error = true; /* uncomparable types */
+                                       return 0;
+
+                               default:
+                                       elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+                                                typid2);
+                       }
+                       break;
+
+               case TIMESTAMPTZOID:
+                       switch (typid2)
+                       {
+                               case DATEOID:
+                                       return -cmpDateToTimestampTz(DatumGetDateADT(val2),
+                                                                                                DatumGetTimestampTz(val1),
+                                                                                                useTz);
+
+                               case TIMESTAMPOID:
+                                       return -cmpTimestampToTimestampTz(DatumGetTimestamp(val2),
+                                                                                                         DatumGetTimestampTz(val1),
+                                                                                                         useTz);
+
+                               case TIMESTAMPTZOID:
+                                       cmpfunc = timestamp_cmp;
+
+                                       break;
+
+                               case TIMEOID:
+                               case TIMETZOID:
+                                       *cast_error = true; /* uncomparable types */
+                                       return 0;
+
+                               default:
+                                       elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+                                                typid2);
+                       }
+                       break;
+
+               default:
+                       elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u", typid1);
+       }
+
+       if (*cast_error)
+               return 0;                               /* cast error */
+
+       return DatumGetInt32(DirectFunctionCall2(cmpfunc, val1, val2));
+}