More new SQL/JSON item methods
Hi,
Attached various patches to implement a few more jsonpath item methods.
For context, PostgreSQL already has some item methods, such as .double()
and
.datetime(). The above new methods are just added alongside these.
Here are the brief descriptions for the same.
---
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.
---
v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patch
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.
The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.
---
v1-0003-Implement-jsonpath-.boolean-and-.string-methods.patch
This commit implements jsonpath .boolean() and .string() methods.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert a string to a bool.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
---
v1-0004-Implement-jasonpath-.decimal-precision-scale-meth.patch
This commit implements jsonpath .decimal() method with optional
precision and scale. If precision and scale are provided, then
it is converted to the equivalent numerictypmod and applied to the
numeric number.
---
Suggestions/feedback/comments, please...
Thanks
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
Attachments:
v1-0003-Implement-jsonpath-.boolean-and-.string-methods.patchapplication/x-patch; name=v1-0003-Implement-jsonpath-.boolean-and-.string-methods.patchDownload
From 7281544170cdc4347b7fa02f8802ea68e25329f7 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 28 Aug 2023 18:44:55 +0530
Subject: [PATCH v1 3/4] Implement jsonpath .boolean() and .string() methods
This commit implements jsonpath .boolean() and .string() methods.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert string to a bool.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 32 ++++
src/backend/utils/adt/jsonpath.c | 16 ++
src/backend/utils/adt/jsonpath_exec.c | 146 +++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 6 +-
src/backend/utils/adt/jsonpath_scan.l | 2 +
src/include/utils/jsonpath.h | 2 +
src/test/regress/expected/jsonb_jsonpath.out | 263 +++++++++++++++++++++++++++
src/test/regress/sql/jsonb_jsonpath.sql | 58 ++++++
8 files changed, 524 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index fdf2995..32ca050 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17520,6 +17520,38 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>boolean()</literal>
+ <returnvalue><replaceable>boolean</replaceable></returnvalue>
+ </para>
+ <para>
+ Boolean value converted from a JSON boolean, number, or string
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')</literal>
+ <returnvalue>[true, true, false]</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>string()</literal>
+ <returnvalue><replaceable>string</replaceable></returnvalue>
+ </para>
+ <para>
+ String value converted from a JSON boolean, number, string, or datetime
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')</literal>
+ <returnvalue>["1.23", "xyz", "false"]</returnvalue>
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.datetime().string()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>double()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 70987cd..de7adf5 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -445,6 +445,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiAbs:
case jpiFloor:
case jpiCeiling:
@@ -727,6 +729,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiBoolean:
+ appendStringInfoString(buf, ".boolean()");
+ break;
+ case jpiStringFunc:
+ appendStringInfoString(buf, ".string()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
@@ -824,6 +832,10 @@ jspOperationName(JsonPathItemType type)
return "type";
case jpiSize:
return "size";
+ case jpiBoolean:
+ return "boolean";
+ case jpiStringFunc:
+ return "string";
case jpiKeyValue:
return "keyvalue";
case jpiDouble:
@@ -942,6 +954,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiAbs:
case jpiFloor:
case jpiCeiling:
@@ -1066,6 +1080,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiIsUnknown ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiBoolean ||
+ v->type == jpiStringFunc ||
v->type == jpiAbs ||
v->type == jpiFloor ||
v->type == jpiCeiling ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index dbf4315..f52663b 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1021,6 +1021,152 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBoolean:
+ {
+ JsonbValue jbv;
+ bool bval;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvBool)
+ {
+ bval = jb->val.boolean;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvNumeric)
+ {
+ int ival;
+ Datum datum;
+ bool noerr;
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ jspOperationName(jsp->type)))));
+
+ ival = DatumGetInt32(datum);
+ if (ival == 0)
+ bval = false;
+ else
+ bval = true;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as boolean */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+
+ if (!parse_bool(tmp, &bval))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, or numeric value",
+ jspOperationName(jsp->type)))));
+
+ jb = &jbv;
+ jb->type = jbvBool;
+ jb->val.boolean = bval;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiStringFunc:
+ {
+ JsonbValue jbv;
+ char *tmp;
+
+ switch (JsonbType(jb))
+ {
+ case jbvString:
+ /*
+ * Value is not necessarily null-terminated, so we do
+ * pnstrdup() here.
+ */
+ tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ break;
+ case jbvNumeric:
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ break;
+ case jbvBool:
+ tmp = (jb->val.boolean) ? "true" : "false";
+ break;
+ case jbvDatetime:
+ {
+ switch (jb->val.datetime.typid)
+ {
+ case DATEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(date_out,
+ jb->val.datetime.value));
+ break;
+ case TIMEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(time_out,
+ jb->val.datetime.value));
+ break;
+ case TIMETZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timetz_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamp_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPTZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ jb->val.datetime.value));
+ break;
+ default:
+ elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+ jb->val.datetime.typid);
+ }
+ }
+ break;
+ case jbvNull:
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, numeric, or datetime value",
+ jspOperationName(jsp->type)))));
+ break;
+ }
+
+ res = jperOk;
+
+ jb = &jbv;
+ jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.len = strlen(jb->val.string.val);
+ jb->type = jbvString;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiAbs:
return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
found);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 53baa0e..8c03f9b 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -83,7 +83,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P
%token <str> DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
-%token <str> BIGINT_P INTEGER_P NUMBER_P
+%token <str> BOOLEAN_P STRINGFUNC_P BIGINT_P INTEGER_P NUMBER_P
%type <result> result
@@ -283,6 +283,8 @@ key_name:
| ABS_P
| SIZE_P
| TYPE_P
+ | BOOLEAN_P
+ | STRINGFUNC_P
| FLOOR_P
| DOUBLE_P
| BIGINT_P
@@ -307,6 +309,8 @@ method:
ABS_P { $$ = jpiAbs; }
| SIZE_P { $$ = jpiSize; }
| TYPE_P { $$ = jpiType; }
+ | BOOLEAN_P { $$ = jpiBoolean; }
+ | STRINGFUNC_P { $$ = jpiStringFunc; }
| FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
| BIGINT_P { $$ = jpiBigint; }
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 2847865..1e4fabc 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -418,6 +418,8 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
+ { 6, false, STRINGFUNC_P, "string"},
+ { 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, INTEGER_P, "integer"},
{ 7, false, TIME_TZ_P, "time_tz"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index d73d297..5af6a44 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -85,6 +85,8 @@ typedef enum JsonPathItemType
jpiExists, /* EXISTS (expr) predicate */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiBoolean, /* .boolean() item method */
+ jpiStringFunc, /* .string() item method */
jpiAbs, /* .abs() item method */
jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index d7cfa43..cc81787 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1348,6 +1348,269 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'l
1
(9 rows)
+select jsonb_path_query('null', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('null', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.boolean()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('{}', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"1.23"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('1e1000', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"nan"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"NaN"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"-inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"100"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('true', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('false', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('0', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('-1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('100', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"1"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"0"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"true"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"false"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"TRUE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"FALSE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"yes"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"NO"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"T"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"f"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"y"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"N"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('true', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('123', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+ jsonb_path_query_array
+------------------------
+ [true, true, false]
+(1 row)
+
+select jsonb_path_query('null', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('null', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('{}', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23aaa"
+(1 row)
+
+select jsonb_path_query('1234', '$.string()');
+ jsonb_path_query
+------------------
+ "1234"
+(1 row)
+
+select jsonb_path_query('true', '$.string()');
+ jsonb_path_query
+------------------
+ "true"
+(1 row)
+
+select jsonb_path_query('1234', '$.string().type()');
+ jsonb_path_query
+------------------
+ "string"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+ jsonb_path_query
+----------------------------
+ "Tue Aug 15 00:04:56 2023"
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+ jsonb_path_query_array
+--------------------------
+ ["1.23", "yes", "false"]
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+ jsonb_path_query_array
+--------------------------------
+ ["string", "string", "string"]
+(1 row)
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
jsonb_path_query
------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e3ed7c9..260cba9 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -283,6 +283,64 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 's
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => true);
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');
+select jsonb_path_query('null', '$.boolean()');
+select jsonb_path_query('null', '$.boolean()', silent => true);
+select jsonb_path_query('[]', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()');
+select jsonb_path_query('{}', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+select jsonb_path_query('1.23', '$.boolean()');
+select jsonb_path_query('"1.23"', '$.boolean()');
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+select jsonb_path_query('1e1000', '$.boolean()');
+select jsonb_path_query('"nan"', '$.boolean()');
+select jsonb_path_query('"NaN"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()');
+select jsonb_path_query('"-inf"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"100"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean()');
+select jsonb_path_query('false', '$.boolean()');
+select jsonb_path_query('1', '$.boolean()');
+select jsonb_path_query('0', '$.boolean()');
+select jsonb_path_query('-1', '$.boolean()');
+select jsonb_path_query('100', '$.boolean()');
+select jsonb_path_query('"1"', '$.boolean()');
+select jsonb_path_query('"0"', '$.boolean()');
+select jsonb_path_query('"true"', '$.boolean()');
+select jsonb_path_query('"false"', '$.boolean()');
+select jsonb_path_query('"TRUE"', '$.boolean()');
+select jsonb_path_query('"FALSE"', '$.boolean()');
+select jsonb_path_query('"yes"', '$.boolean()');
+select jsonb_path_query('"NO"', '$.boolean()');
+select jsonb_path_query('"T"', '$.boolean()');
+select jsonb_path_query('"f"', '$.boolean()');
+select jsonb_path_query('"y"', '$.boolean()');
+select jsonb_path_query('"N"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean().type()');
+select jsonb_path_query('123', '$.boolean().type()');
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+
+select jsonb_path_query('null', '$.string()');
+select jsonb_path_query('null', '$.string()', silent => true);
+select jsonb_path_query('[]', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()');
+select jsonb_path_query('{}', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+select jsonb_path_query('{}', '$.string()', silent => true);
+select jsonb_path_query('1.23', '$.string()');
+select jsonb_path_query('"1.23"', '$.string()');
+select jsonb_path_query('"1.23aaa"', '$.string()');
+select jsonb_path_query('1234', '$.string()');
+select jsonb_path_query('true', '$.string()');
+select jsonb_path_query('1234', '$.string().type()');
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()');
--
1.8.3.1
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patchapplication/x-patch; name=v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patchDownload
From 109c9a2da73d72674f18857a843130211127ee40 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 28 Aug 2023 18:34:31 +0530
Subject: [PATCH v1 1/4] Implement jsonpath .bigint(), .integer(), and
.number() methods
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 42 ++++
src/backend/utils/adt/jsonpath.c | 24 ++
src/backend/utils/adt/jsonpath_exec.c | 190 ++++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 7 +
src/backend/utils/adt/jsonpath_scan.l | 3 +
src/include/utils/jsonpath.h | 3 +
src/test/regress/expected/jsonb_jsonpath.out | 329 +++++++++++++++++++++++++++
src/test/regress/sql/jsonb_jsonpath.sql | 86 +++++++
8 files changed, 684 insertions(+)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7a0d4b9..ca9899f 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17535,6 +17535,48 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
+ <returnvalue><replaceable>bigint</replaceable></returnvalue>
+ </para>
+ <para>
+ Big integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')</literal>
+ <returnvalue>9876543219</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>integer()</literal>
+ <returnvalue><replaceable>integer</replaceable></returnvalue>
+ </para>
+ <para>
+ Integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "12345"}', '$.len.integer()')</literal>
+ <returnvalue>12345</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>number()</literal>
+ <returnvalue><replaceable>numeric</replaceable></returnvalue>
+ </para>
+ <para>
+ Numeric value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "123.45"}', '$.len.number()')</literal>
+ <returnvalue>123.45</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index c5ba3b7..f45f919 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -444,6 +444,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiCeiling:
case jpiDouble:
case jpiKeyValue:
+ case jpiBigint:
+ case jpiInteger:
+ case jpiNumber:
break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
@@ -730,6 +733,15 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
+ case jpiBigint:
+ appendStringInfoString(buf, ".bigint()");
+ break;
+ case jpiInteger:
+ appendStringInfoString(buf, ".integer()");
+ break;
+ case jpiNumber:
+ appendStringInfoString(buf, ".number()");
+ break;
case jpiDatetime:
appendStringInfoString(buf, ".datetime(");
if (v->content.arg)
@@ -795,6 +807,12 @@ jspOperationName(JsonPathItemType type)
return "keyvalue";
case jpiDouble:
return "double";
+ case jpiBigint:
+ return "bigint";
+ case jpiInteger:
+ return "integer";
+ case jpiNumber:
+ return "number";
case jpiAbs:
return "abs";
case jpiFloor:
@@ -897,6 +915,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiFloor:
case jpiCeiling:
case jpiDouble:
+ case jpiBigint:
+ case jpiInteger:
+ case jpiNumber:
case jpiKeyValue:
case jpiLast:
break;
@@ -1012,6 +1033,9 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiFloor ||
v->type == jpiCeiling ||
v->type == jpiDouble ||
+ v->type == jpiBigint ||
+ v->type == jpiInteger ||
+ v->type == jpiNumber ||
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 2d0599b..ff7fdaf 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1098,6 +1098,196 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBigint:
+ {
+ JsonbValue jbv;
+ Datum datum;
+ bool noerr;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as bigint */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiInteger:
+ {
+ JsonbValue jbv;
+ Datum datum;
+ bool noerr;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as integer */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiNumber:
+ {
+ JsonbValue jbv;
+ Numeric num;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ num = jb->val.numeric;
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is not a valid representation of a number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as number */
+ Datum datum;
+ bool noerr;
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(numeric_in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(datum);
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = num;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiDatetime:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index adc259d..340caa9 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -82,6 +82,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P
+%token <str> BIGINT_P INTEGER_P NUMBER_P
%type <result> result
@@ -283,6 +284,9 @@ key_name:
| TYPE_P
| FLOOR_P
| DOUBLE_P
+ | BIGINT_P
+ | INTEGER_P
+ | NUMBER_P
| CEILING_P
| DATETIME_P
| KEYVALUE_P
@@ -299,6 +303,9 @@ method:
| TYPE_P { $$ = jpiType; }
| FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
+ | BIGINT_P { $$ = jpiBigint; }
+ | INTEGER_P { $$ = jpiInteger; }
+ | NUMBER_P { $$ = jpiNumber; }
| CEILING_P { $$ = jpiCeiling; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 29c26af..1abcea3 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -410,11 +410,14 @@ static const JsonPathKeyword keywords[] = {
{ 4, false, WITH_P, "with"},
{ 5, true, FALSE_P, "false"},
{ 5, false, FLOOR_P, "floor"},
+ { 6, false, BIGINT_P, "bigint"},
{ 6, false, DOUBLE_P, "double"},
{ 6, false, EXISTS_P, "exists"},
+ { 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, INTEGER_P, "integer"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index f0181e0..9fe161f 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -89,6 +89,9 @@ typedef enum JsonPathItemType
jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
jpiDouble, /* .double() item method */
+ jpiBigint, /* .bigint() item method */
+ jpiInteger, /* .integer() item method */
+ jpiNumber, /* .number() item method */
jpiDatetime, /* .datetime() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 6659bc9..c7d1a4e 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1517,6 +1517,335 @@ select jsonb_path_query('"-inf"', '$.double()', silent => true);
------------------
(0 rows)
+select jsonb_path_query('null', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.bigint()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"1.23"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('1e1000', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"nan"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"NaN"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"-inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1234567890123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"+123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.bigint() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.integer()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"1.23"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('1e1000', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"nan"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"NaN"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"-inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"12345678901"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"+123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.integer() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.number()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+select jsonb_path_query('1e1000', '$.number()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+select jsonb_path_query('"NaN"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+select jsonb_path_query('"inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+select jsonb_path_query('"-inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.number() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
select jsonb_path_query('{}', '$.abs()');
ERROR: jsonpath item method .abs() can only be applied to a numeric value
select jsonb_path_query('true', '$.floor()');
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e0ce509..a9240c2 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -320,6 +320,92 @@ select jsonb_path_query('"-inf"', '$.double()');
select jsonb_path_query('"inf"', '$.double()', silent => true);
select jsonb_path_query('"-inf"', '$.double()', silent => true);
+select jsonb_path_query('null', '$.bigint()');
+select jsonb_path_query('true', '$.bigint()');
+select jsonb_path_query('null', '$.bigint()', silent => true);
+select jsonb_path_query('true', '$.bigint()', silent => true);
+select jsonb_path_query('[]', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()');
+select jsonb_path_query('{}', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+select jsonb_path_query('1.23', '$.bigint()');
+select jsonb_path_query('"1.23"', '$.bigint()');
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+select jsonb_path_query('1e1000', '$.bigint()');
+select jsonb_path_query('"nan"', '$.bigint()');
+select jsonb_path_query('"NaN"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()');
+select jsonb_path_query('"-inf"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+select jsonb_path_query('123', '$.bigint()');
+select jsonb_path_query('"123"', '$.bigint()');
+select jsonb_path_query('1234567890123', '$.bigint()');
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+select jsonb_path_query('"+123"', '$.bigint()');
+select jsonb_path_query('-123', '$.bigint()');
+select jsonb_path_query('"-123"', '$.bigint()');
+select jsonb_path_query('123', '$.bigint() * 2');
+
+select jsonb_path_query('null', '$.integer()');
+select jsonb_path_query('true', '$.integer()');
+select jsonb_path_query('null', '$.integer()', silent => true);
+select jsonb_path_query('true', '$.integer()', silent => true);
+select jsonb_path_query('[]', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()');
+select jsonb_path_query('{}', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+select jsonb_path_query('{}', '$.integer()', silent => true);
+select jsonb_path_query('1.23', '$.integer()');
+select jsonb_path_query('"1.23"', '$.integer()');
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+select jsonb_path_query('1e1000', '$.integer()');
+select jsonb_path_query('"nan"', '$.integer()');
+select jsonb_path_query('"NaN"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()');
+select jsonb_path_query('"-inf"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+select jsonb_path_query('123', '$.integer()');
+select jsonb_path_query('"123"', '$.integer()');
+select jsonb_path_query('12345678901', '$.integer()');
+select jsonb_path_query('"12345678901"', '$.integer()');
+select jsonb_path_query('"+123"', '$.integer()');
+select jsonb_path_query('-123', '$.integer()');
+select jsonb_path_query('"-123"', '$.integer()');
+select jsonb_path_query('123', '$.integer() * 2');
+
+select jsonb_path_query('null', '$.number()');
+select jsonb_path_query('true', '$.number()');
+select jsonb_path_query('null', '$.number()', silent => true);
+select jsonb_path_query('true', '$.number()', silent => true);
+select jsonb_path_query('[]', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()');
+select jsonb_path_query('{}', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+select jsonb_path_query('{}', '$.number()', silent => true);
+select jsonb_path_query('1.23', '$.number()');
+select jsonb_path_query('"1.23"', '$.number()');
+select jsonb_path_query('"1.23aaa"', '$.number()');
+select jsonb_path_query('1e1000', '$.number()');
+select jsonb_path_query('"nan"', '$.number()');
+select jsonb_path_query('"NaN"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()');
+select jsonb_path_query('"-inf"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+select jsonb_path_query('123', '$.number()');
+select jsonb_path_query('"123"', '$.number()');
+select jsonb_path_query('12345678901234567890', '$.number()');
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+select jsonb_path_query('"+12.3"', '$.number()');
+select jsonb_path_query('-12.3', '$.number()');
+select jsonb_path_query('"-12.3"', '$.number()');
+select jsonb_path_query('12.3', '$.number() * 2');
+
select jsonb_path_query('{}', '$.abs()');
select jsonb_path_query('true', '$.floor()');
select jsonb_path_query('"1.2"', '$.ceiling()');
--
1.8.3.1
v1-0004-Implement-jasonpath-.decimal-precision-scale-meth.patchapplication/x-patch; name=v1-0004-Implement-jasonpath-.decimal-precision-scale-meth.patchDownload
From 775f3a4be3420378cb619f5a8716995f8eaa8421 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 28 Aug 2023 18:46:32 +0530
Subject: [PATCH v1 4/4] Implement jasonpath .decimal([precision [, scale]])
method
This commit implements jsonpath .decimal() method with optional
precision and scale. If precision and scale are provided, then
it is converted to the equivalent numerictypmod and applied to the
numeric number.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 14 ++
src/backend/utils/adt/jsonpath.c | 22 +++
src/backend/utils/adt/jsonpath_exec.c | 93 ++++++++++++-
src/backend/utils/adt/jsonpath_gram.y | 40 +++++-
src/backend/utils/adt/jsonpath_scan.l | 1 +
src/include/utils/jsonpath.h | 1 +
src/test/regress/expected/jsonb_jsonpath.out | 191 ++++++++++++++++++++++++++-
src/test/regress/sql/jsonb_jsonpath.sql | 43 ++++++
8 files changed, 391 insertions(+), 14 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 32ca050..41a3525 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17567,6 +17567,20 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>decimal( [ <replaceable>precision</replaceable> [ , <replaceable>scale</replaceable> ] ] )</literal>
+ <returnvalue><replaceable>decimal</replaceable></returnvalue>
+ </para>
+ <para>
+ Rounded decimal value converted from a JSON number or string. <literal>precision</literal> and <literal>scale</literal> must be integer values.
+ </para>
+ <para>
+ <literal>jsonb_path_query('1234.5678', '$.decimal(6, 2)')</literal>
+ <returnvalue>1234.57</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
<returnvalue><replaceable>bigint</replaceable></returnvalue>
</para>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index de7adf5..42f325b 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -294,6 +294,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMul:
case jpiDiv:
case jpiMod:
+ case jpiDecimal:
case jpiStartsWith:
{
/*
@@ -747,6 +748,21 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
+ case jpiDecimal:
+ appendStringInfoString(buf, ".decimal(");
+ if (v->content.args.left)
+ {
+ jspGetLeftArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ if (v->content.args.right)
+ {
+ appendStringInfoChar(buf, ',');
+ jspGetRightArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
case jpiBigint:
appendStringInfoString(buf, ".bigint()");
break;
@@ -840,6 +856,8 @@ jspOperationName(JsonPathItemType type)
return "keyvalue";
case jpiDouble:
return "double";
+ case jpiDecimal:
+ return "decimal";
case jpiBigint:
return "bigint";
case jpiInteger:
@@ -988,6 +1006,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiGreater:
case jpiLessOrEqual:
case jpiGreaterOrEqual:
+ case jpiDecimal:
case jpiStartsWith:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
@@ -1086,6 +1105,7 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiFloor ||
v->type == jpiCeiling ||
v->type == jpiDouble ||
+ v->type == jpiDecimal ||
v->type == jpiBigint ||
v->type == jpiInteger ||
v->type == jpiNumber ||
@@ -1123,6 +1143,7 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.left);
@@ -1144,6 +1165,7 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.right);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index f52663b..94f1052 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1371,9 +1371,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
case jpiNumber:
+ case jpiDecimal:
{
JsonbValue jbv;
Numeric num;
+ char *numstr = NULL;
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
@@ -1385,9 +1387,12 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is not a valid representation of a number",
+ errmsg("numeric argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
jspOperationName(jsp->type)))));
+ if (jsp->type == jpiDecimal)
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
res = jperOk;
}
else if (jb->type == jbvString)
@@ -1395,11 +1400,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
/* cast string as number */
Datum datum;
bool noerr;
- char *tmp = pnstrdup(jb->val.string.val,
- jb->val.string.len);
+ char *numstr = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
ErrorSaveContext escontext = {T_ErrorSaveContext};
- noerr = DirectInputFunctionCallSafe(numeric_in, tmp,
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
InvalidOid, -1,
(Node *) &escontext,
&datum);
@@ -1407,14 +1412,14 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a number",
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
jspOperationName(jsp->type)))));
num = DatumGetNumeric(datum);
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a number",
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
jspOperationName(jsp->type)))));
res = jperOk;
@@ -1426,6 +1431,82 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
errmsg("jsonpath item method .%s() can only be applied to a string or numeric value",
jspOperationName(jsp->type)))));
+ /*
+ * If we have arguments, then they must be the precision and
+ * optional scale used in .decimal(). Convert them to the
+ * typmod equivalent and then truncate the numeric value per
+ * this typmod details.
+ */
+ if (jsp->type == jpiDecimal && jsp->content.args.left)
+ {
+ JsonPathItem elem;
+ Datum numdatum;
+ Datum dtypmod;
+ int32 precision;
+ int32 scale = 0;
+ bool have_error;
+ bool noerr;
+ ArrayType *arrtypmod;
+ Datum datums[2];
+ char pstr[12]; /* sign, 10 digits and '\0' */
+ char sstr[12]; /* sign, 10 digits and '\0' */
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ jspGetLeftArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() precision");
+
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+ errmsg("precision is out of integer range"))));
+
+ if (jsp->content.args.right)
+ {
+ jspGetRightArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() scale");
+
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+ errmsg("scale is out of integer range"))));
+ }
+
+ /*
+ * numerictypmodin() takes the precision and scale in the
+ * form of CString arrays.
+ */
+ pg_ltoa(precision, pstr);
+ datums[0] = CStringGetDatum(pstr);
+ pg_ltoa(scale, sstr);
+ datums[1] = CStringGetDatum(sstr);
+ arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
+
+ dtypmod = DirectFunctionCall1(numerictypmodin,
+ PointerGetDatum(arrtypmod));
+
+ /* Convert numstr to Numeric with typmod */
+ Assert(numstr != NULL);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, dtypmod,
+ (Node *) &escontext,
+ &numdatum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(numdatum);
+ pfree(arrtypmod);
+ }
+
jb = &jbv;
jb->type = jbvNumeric;
jb->val.numeric = num;
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 8c03f9b..da031e3 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -83,16 +83,16 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P
%token <str> DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
-%token <str> BOOLEAN_P STRINGFUNC_P BIGINT_P INTEGER_P NUMBER_P
+%token <str> BOOLEAN_P STRINGFUNC_P BIGINT_P INTEGER_P NUMBER_P DECIMAL_P
%type <result> result
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template
+ datetime_template opt_datetime_template csv_elem
-%type <elems> accessor_expr
+%type <elems> accessor_expr csv_list opt_csv_list
%type <indexs> index_list
@@ -250,11 +250,44 @@ accessor_op:
| array_accessor { $$ = $1; }
| '.' any_path { $$ = $2; }
| '.' method '(' ')' { $$ = makeItemType($2); }
+ | '.' DECIMAL_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiDecimal, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
+ else if (list_length($4) == 2)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".decimal() can only have an optional precision[,scale].")));
+ }
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+csv_elem:
+ INT_P
+ { $$ = makeItemNumeric(&$1); }
+ | '+' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
+ | '-' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
+ ;
+
+csv_list:
+ csv_elem { $$ = list_make1($1); }
+ | csv_list ',' csv_elem { $$ = lappend($1, $3); }
+ ;
+
+opt_csv_list:
+ csv_list { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -287,6 +320,7 @@ key_name:
| STRINGFUNC_P
| FLOOR_P
| DOUBLE_P
+ | DECIMAL_P
| BIGINT_P
| INTEGER_P
| NUMBER_P
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 1e4fabc..d87da14 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -421,6 +421,7 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, STRINGFUNC_P, "string"},
{ 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
{ 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 5af6a44..7058563 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -91,6 +91,7 @@ typedef enum JsonPathItemType
jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
jpiDouble, /* .double() item method */
+ jpiDecimal, /* .decimal() item method */
jpiBigint, /* .bigint() item method */
jpiInteger, /* .integer() item method */
jpiNumber, /* .number() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index cc81787..4185d2a 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2036,7 +2036,7 @@ select jsonb_path_query('"1.23"', '$.number()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
select jsonb_path_query('1e1000', '$.number()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2044,13 +2044,13 @@ select jsonb_path_query('1e1000', '$.number()');
(1 row)
select jsonb_path_query('"nan"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
select jsonb_path_query('"NaN"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
select jsonb_path_query('"inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
select jsonb_path_query('"-inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
select jsonb_path_query('"inf"', '$.number()', silent => true);
jsonb_path_query
------------------
@@ -2109,6 +2109,187 @@ select jsonb_path_query('12.3', '$.number() * 2');
24.6
(1 row)
+select jsonb_path_query('null', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.decimal()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.decimal()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+ jsonb_path_query
+------------------
+ 12345.7
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+ERROR: NUMERIC precision 0 must be between 1 and 1000
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+ERROR: NUMERIC precision 1001 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ 1200
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+ERROR: NUMERIC precision -6 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+ERROR: NUMERIC scale -1001 must be between -1000 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+ERROR: NUMERIC scale 1001 must be between -1000 and 1000
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ -1200
+(1 row)
+
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+ jsonb_path_query
+------------------
+ 0.01
+(1 row)
+
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+ jsonb_path_query
+------------------
+ 0.0012
+(1 row)
+
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+ jsonb_path_query
+------------------
+ 0
+(1 row)
+
select jsonb_path_query('{}', '$.abs()');
ERROR: jsonpath item method .abs() can only be applied to a numeric value
select jsonb_path_query('true', '$.floor()');
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 260cba9..c5416c1 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -464,6 +464,49 @@ select jsonb_path_query('-12.3', '$.number()');
select jsonb_path_query('"-12.3"', '$.number()');
select jsonb_path_query('12.3', '$.number() * 2');
+select jsonb_path_query('null', '$.decimal()');
+select jsonb_path_query('true', '$.decimal()');
+select jsonb_path_query('null', '$.decimal()', silent => true);
+select jsonb_path_query('true', '$.decimal()', silent => true);
+select jsonb_path_query('[]', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()');
+select jsonb_path_query('{}', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+select jsonb_path_query('1.23', '$.decimal()');
+select jsonb_path_query('"1.23"', '$.decimal()');
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+select jsonb_path_query('1e1000', '$.decimal()');
+select jsonb_path_query('"nan"', '$.decimal()');
+select jsonb_path_query('"NaN"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()');
+select jsonb_path_query('"-inf"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+select jsonb_path_query('123', '$.decimal()');
+select jsonb_path_query('"123"', '$.decimal()');
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+select jsonb_path_query('"+12.3"', '$.decimal()');
+select jsonb_path_query('-12.3', '$.decimal()');
+select jsonb_path_query('"-12.3"', '$.decimal()');
+select jsonb_path_query('12.3', '$.decimal() * 2');
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+
select jsonb_path_query('{}', '$.abs()');
select jsonb_path_query('true', '$.floor()');
select jsonb_path_query('"1.2"', '$.ceiling()');
--
1.8.3.1
v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patchapplication/x-patch; name=v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patchDownload
From b4daf5b14a31c3eb004a7429e0e96cce71ac2c25 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 28 Aug 2023 18:43:06 +0530
Subject: [PATCH v1 2/4] Implement .date(), .time(), .time_tz(), .timestamp(),
and .timestamp_tz() methods
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.
The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.
Jeevan Chalke
---
doc/src/sgml/func.sgml | 70 +++
src/backend/utils/adt/jsonpath.c | 42 ++
src/backend/utils/adt/jsonpath_exec.c | 197 ++++++-
src/backend/utils/adt/jsonpath_gram.y | 11 +
src/backend/utils/adt/jsonpath_scan.l | 5 +
src/include/utils/jsonpath.h | 5 +
src/test/regress/expected/jsonb_jsonpath.out | 804 ++++++++++++++++++++++++++-
src/test/regress/expected/jsonpath.out | 30 +
src/test/regress/sql/jsonb_jsonpath.sql | 245 ++++++++
src/test/regress/sql/jsonpath.sql | 5 +
10 files changed, 1401 insertions(+), 13 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index ca9899f..fdf2995 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17650,6 +17650,76 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>date()</literal>
+ <returnvalue><replaceable>date</replaceable></returnvalue>
+ </para>
+ <para>
+ Date value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.date()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time()</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56"', '$.time()')</literal>
+ <returnvalue>"12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz()</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')</literal>
+ <returnvalue>"12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp()</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')</literal>
+ <returnvalue>"2023-08-15T12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz()</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')</literal>
+ <returnvalue>"2023-08-15T12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>object</replaceable> <literal>.</literal> <literal>keyvalue()</literal>
<returnvalue><replaceable>array</replaceable></returnvalue>
</para>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index f45f919..70987cd 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -368,6 +368,12 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
*(int32 *) (buf->data + arg) = chld - pos;
}
break;
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
+ break;
case jpiNull:
break;
case jpiRoot:
@@ -751,6 +757,21 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
}
appendStringInfoChar(buf, ')');
break;
+ case jpiDate:
+ appendStringInfoString(buf, ".date()");
+ break;
+ case jpiTime:
+ appendStringInfoString(buf, ".time()");
+ break;
+ case jpiTimeTz:
+ appendStringInfoString(buf, ".time_tz()");
+ break;
+ case jpiTimestamp:
+ appendStringInfoString(buf, ".timestamp()");
+ break;
+ case jpiTimestampTz:
+ appendStringInfoString(buf, ".timestamp_tz()");
+ break;
case jpiKeyValue:
appendStringInfoString(buf, ".keyvalue()");
break;
@@ -821,6 +842,16 @@ jspOperationName(JsonPathItemType type)
return "ceiling";
case jpiDatetime:
return "datetime";
+ case jpiDate:
+ return "date";
+ case jpiTime:
+ return "time";
+ case jpiTimeTz:
+ return "time_tz";
+ case jpiTimestamp:
+ return "timestamp";
+ case jpiTimestampTz:
+ return "timestamp_tz";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -962,6 +993,12 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiDatetime:
read_int32(v->content.arg, base, pos);
break;
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
+ break;
case jpiIndexArray:
read_int32(v->content.array.nelems, base, pos);
read_int32_n(v->content.array.elems, base, pos,
@@ -1037,6 +1074,11 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiInteger ||
v->type == jpiNumber ||
v->type == jpiDatetime ||
+ v->type == jpiDate ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
v->type == jpiLikeRegex);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index ff7fdaf..dbf4315 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1289,6 +1289,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
case jpiDatetime:
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -1961,11 +1966,14 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
/*
- * Implementation of the .datetime() method.
+ * Implementation of the .datetime() and related methods.
*
* 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.
+ *
+ * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
+ * have a format, so ISO format is used.
*/
static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
@@ -1998,7 +2006,11 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
collid = DEFAULT_COLLATION_OID;
- if (jsp->content.arg)
+ /*
+ * .datetime(template) has an argument, the rest of the methods don't have
+ * an argument. So we handle that separately.
+ */
+ if (jsp->type == jpiDatetime && jsp->content.arg)
{
text *template;
char *template_str;
@@ -2086,11 +2098,182 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
if (res == jperNotFound)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
- errhint("Use a datetime template argument to specify the input data format."))));
+ {
+ if (jsp->type == jpiDatetime)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("datetime format is not recognized: \"%s\"",
+ text_to_cstring(datetime)),
+ errhint("Use a datetime template argument to specify the input data format."))));
+ else
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("%s format is not recognized: \"%s\"",
+ jspOperationName(jsp->type), text_to_cstring(datetime)))));
+
+ }
+ }
+
+ /*
+ * parse_datetime() processes the entire input string per the template or
+ * ISO format and returns the Datum in best fitted datetime type. So, if
+ * this call is for a specific datatype, then we do the conversion here.
+ * Throw an error for incompatible types.
+ */
+ switch (jsp->type)
+ {
+ case jpiDatetime: /* Nothing to do for DATETIME */
+ break;
+ case jpiDate:
+ {
+ /* Convert result type to date */
+ switch (typid)
+ {
+ case DATEOID: /* Nothing to do for DATE */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("date format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_date,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_date,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = DATEOID;
+ }
+ break;
+ case jpiTime:
+ {
+ /* Convert result type to time without time zone */
+ switch (typid)
+ {
+ case TIMEOID: /* Nothing to do for TIME */
+ break;
+ case DATEOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMETZOID:
+ value = DirectFunctionCall1(timetz_time,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_time,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_time,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = TIMEOID;
+ }
+ break;
+ case jpiTimeTz:
+ {
+ /* Convert result type to time with time zone */
+ switch (typid)
+ {
+ case TIMETZOID: /* Nothing to do for TIMETZ */
+ break;
+ case DATEOID:
+ case TIMESTAMPOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID:
+ value = DirectFunctionCall1(time_timetz,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timetz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = TIMETZOID;
+ }
+ break;
+ case jpiTimestamp:
+ {
+ /* Convert result type to timestamp without time zone */
+ switch (typid)
+ {
+ case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamp,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timestamp,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = TIMESTAMPOID;
+ }
+ break;
+ case jpiTimestampTz:
+ {
+ /* Convert result type to timestamp with time zone */
+ switch (typid)
+ {
+ case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamptz,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_timestamptz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = TIMESTAMPTZOID;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
pfree(datetime);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 340caa9..53baa0e 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -82,6 +82,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P
+%token <str> DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
%token <str> BIGINT_P INTEGER_P NUMBER_P
%type <result> result
@@ -289,6 +290,11 @@ key_name:
| NUMBER_P
| CEILING_P
| DATETIME_P
+ | DATE_P
+ | TIME_P
+ | TIME_TZ_P
+ | TIMESTAMP_P
+ | TIMESTAMP_TZ_P
| KEYVALUE_P
| LAST_P
| STARTS_P
@@ -308,6 +314,11 @@ method:
| NUMBER_P { $$ = jpiNumber; }
| CEILING_P { $$ = jpiCeiling; }
| KEYVALUE_P { $$ = jpiKeyValue; }
+ | DATE_P { $$ = jpiDate; }
+ | TIME_P { $$ = jpiTime; }
+ | TIME_TZ_P { $$ = jpiTimeTz; }
+ | TIMESTAMP_P { $$ = jpiTimestamp; }
+ | TIMESTAMP_TZ_P { $$ = jpiTimestampTz; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 1abcea3..2847865 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -401,10 +401,12 @@ static const JsonPathKeyword keywords[] = {
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
+ { 4, false, DATE_P, "date"},
{ 4, false, FLAG_P, "flag"},
{ 4, false, LAST_P, "last"},
{ 4, true, NULL_P, "null"},
{ 4, false, SIZE_P, "size"},
+ { 4, false, TIME_P, "time"},
{ 4, true, TRUE_P, "true"},
{ 4, false, TYPE_P, "type"},
{ 4, false, WITH_P, "with"},
@@ -418,10 +420,13 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, STRICT_P, "strict"},
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, INTEGER_P, "integer"},
+ { 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
+ { 9, false, TIMESTAMP_P, "timestamp"},
{ 10,false, LIKE_REGEX_P, "like_regex"},
+ { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
/* Check if current scanstring value is a keyword */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 9fe161f..d73d297 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -93,6 +93,11 @@ typedef enum JsonPathItemType
jpiInteger, /* .integer() item method */
jpiNumber, /* .number() item method */
jpiDatetime, /* .datetime() item method */
+ jpiDate, /* .date() item method */
+ jpiTime, /* .time() item method */
+ jpiTimeTz, /* .time_tz() item method */
+ jpiTimestamp, /* .timestamp() item method */
+ jpiTimestampTz, /* .timestamp_tz() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
jpiLast, /* LAST array subscript */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index c7d1a4e..d7cfa43 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2061,7 +2061,271 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
ERROR: unmatched format character "T"
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
ERROR: unmatched format character "T"
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('true', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('1', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('[]', '$.date()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('{}', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.date()');
+ERROR: date format is not recognized: "bogus"
+select jsonb '"2023-08-15"' @? '$.date()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+ jsonb_path_query
+------------------
+ "date"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+ERROR: date format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+ERROR: date format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('true', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('1', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('[]', '$.time()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('{}', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time()');
+ERROR: time format is not recognized: "bogus"
+select jsonb '"12:34:56"' @? '$.time()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+ jsonb_path_query
+--------------------------
+ "time without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+ERROR: time format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('true', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('1', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.time_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "bogus"
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+ jsonb_path_query
+-----------------------
+ "time with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15 12:34:56"
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+ jsonb_path_query
+-------------------------------
+ "timestamp without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56 +05:30"
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+ jsonb_path_query
+----------------------------
+ "timestamp with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T07:00:00+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56 +05:30"
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "07:04:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+00:00"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2127,6 +2391,36 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "17:04:56+10:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T02:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2192,6 +2486,30 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "00:04:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
jsonb_path_query
------------------
@@ -2348,6 +2666,101 @@ select jsonb_path_query_tz(
"2017-03-10T01:02:03+04:00"
(2 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-10T00:00:00"
+ "2017-03-10T03:00:00+03:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10T00:00:00"
+ "2017-03-10T12:34:56"
+ "2017-03-10T03:00:00+03:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-09"
+ "2017-03-10T01:02:03+04:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -2393,12 +2806,107 @@ select jsonb_path_query_tz(
"13:35:00+01:00"
(3 rows)
--- timetz comparison
-select jsonb_path_query(
- '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
- '$[*].datetime() ? (@ == "12:35 +1".datetime("HH24:MI TZH"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+-- timetz comparison
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35 +1".datetime("HH24:MI TZH"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ >= "12:35 +1".datetime("HH24:MI TZH"))');
@@ -2439,6 +2947,98 @@ select jsonb_path_query_tz(
"10:35:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00"
+ "12:35:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2486,6 +3086,99 @@ select jsonb_path_query_tz(
"2017-03-10"
(3 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00+01:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T13:35:00+01:00"
+ "2017-03-10T12:35:00-01:00"
+ "2017-03-11"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2535,6 +3228,105 @@ select jsonb_path_query_tz(
"2017-03-10"
(4 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T12:35:00"
+ "2017-03-11"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
jsonb_path_query
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index eeffb38..0a666a3 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -405,6 +405,36 @@ select '$.datetime("datetime template")'::jsonpath;
$.datetime("datetime template")
(1 row)
+select '$.date()'::jsonpath;
+ jsonpath
+----------
+ $.date()
+(1 row)
+
+select '$.time()'::jsonpath;
+ jsonpath
+----------
+ $.time()
+(1 row)
+
+select '$.time_tz()'::jsonpath;
+ jsonpath
+-------------
+ $.time_tz()
+(1 row)
+
+select '$.timestamp()'::jsonpath;
+ jsonpath
+---------------
+ $.timestamp()
+(1 row)
+
+select '$.timestamp_tz()'::jsonpath;
+ jsonpath
+------------------
+ $.timestamp_tz()
+(1 row)
+
select '$ ? (@ starts with "abc")'::jsonpath;
jsonpath
-------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index a9240c2..e3ed7c9 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -458,8 +458,100 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+select jsonb_path_query('true', '$.date()');
+select jsonb_path_query('1', '$.date()');
+select jsonb_path_query('[]', '$.date()');
+select jsonb_path_query('[]', 'strict $.date()');
+select jsonb_path_query('{}', '$.date()');
+select jsonb_path_query('"bogus"', '$.date()');
+
+select jsonb '"2023-08-15"' @? '$.date()';
+select jsonb_path_query('"2023-08-15"', '$.date()');
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+select jsonb_path_query('true', '$.time()');
+select jsonb_path_query('1', '$.time()');
+select jsonb_path_query('[]', '$.time()');
+select jsonb_path_query('[]', 'strict $.time()');
+select jsonb_path_query('{}', '$.time()');
+select jsonb_path_query('"bogus"', '$.time()');
+
+select jsonb '"12:34:56"' @? '$.time()';
+select jsonb_path_query('"12:34:56"', '$.time()');
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+select jsonb_path_query('true', '$.time_tz()');
+select jsonb_path_query('1', '$.time_tz()');
+select jsonb_path_query('[]', '$.time_tz()');
+select jsonb_path_query('[]', 'strict $.time_tz()');
+select jsonb_path_query('{}', '$.time_tz()');
+select jsonb_path_query('"bogus"', '$.time_tz()');
+
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+select jsonb_path_query('true', '$.timestamp()');
+select jsonb_path_query('1', '$.timestamp()');
+select jsonb_path_query('[]', '$.timestamp()');
+select jsonb_path_query('[]', 'strict $.timestamp()');
+select jsonb_path_query('{}', '$.timestamp()');
+select jsonb_path_query('"bogus"', '$.timestamp()');
+
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+select jsonb_path_query('true', '$.timestamp_tz()');
+select jsonb_path_query('1', '$.timestamp_tz()');
+select jsonb_path_query('[]', '$.timestamp_tz()');
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+select jsonb_path_query('{}', '$.timestamp_tz()');
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -475,6 +567,12 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -490,6 +588,11 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
select jsonb_path_query('"2017-03-10"', '$.datetime()');
select jsonb_path_query('"2017-03-10 12:34:56"', '$.datetime().type()');
@@ -532,6 +635,34 @@ select jsonb_path_query_tz(
'["2017-03-10", "2017-03-11", "2017-03-09", "12:34:56", "01:02:03+04", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
'$[*].datetime() ? (@ < "10.03.2017".datetime("dd.mm.yyyy"))');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -552,6 +683,35 @@ select jsonb_path_query_tz(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
'$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -572,6 +732,34 @@ select jsonb_path_query_tz(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ < "12:35 +1".datetime("HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -592,6 +780,34 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35".datetime("dd.mm.yyyy HH24:MI"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -612,6 +828,35 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35 +1".datetime("dd.mm.yyyy HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 56e0bef..b39aefd 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -73,6 +73,11 @@ select '$.double().floor().ceiling().abs()'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
+select '$.date()'::jsonpath;
+select '$.time()'::jsonpath;
+select '$.time_tz()'::jsonpath;
+select '$.timestamp()'::jsonpath;
+select '$.timestamp_tz()'::jsonpath;
select '$ ? (@ starts with "abc")'::jsonpath;
select '$ ? (@ starts with $var)'::jsonpath;
--
1.8.3.1
Hi,
On 2023-08-29 03:05, Jeevan Chalke wrote:
This commit implements jsonpath .bigint(), .integer(), and .number()
---
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods.
---
This commit implements jsonpath .boolean() and .string() methods.
Writing as an interested outsider to the jsonpath spec, my first
question would be, is there a published jsonpath spec independent
of PostgreSQL, and are these methods in it, and are the semantics
identical?
The question comes out of my experience on a PostgreSQL integration
of XQuery/XPath, which was nontrivial because the w3 specs for those
languages give rigorous definitions of their data types, independently
of SQL, and a good bit of the work was squinting at those types and at
the corresponding PostgreSQL types to see in what ways they were
different, and what the constraints on converting them were. (Some of
that squinting was already done by the SQL committee in the SQL/XML
spec, which has plural pages on how those conversions have to happen,
especially for the date/time types.)
If I look in [1]https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html, am I looking in the right place for the most
current jsonpath draft?
(I'm a little squeamish reading as a goal "cover only essential
parts of XPath 1.0", given that XPath 1.0 is the one w3 threw away
so XPath 2.0 wouldn't have the same problems.)
On details of the patch itself, I only have quick first impressions,
like:
- surely there's a more direct way to make boolean from numeric
than to serialize the numeric and parse an int?
- I notice that .bigint() and .integer() finish up by casting the
value to numeric so the existing jbv->val.numeric can hold it.
That may leave some opportunity on the table: there is another
patch under way [2]https://commitfest.postgresql.org/44/4476/ that concerns quickly getting such result
values from json operations to the surrounding SQL query. That
could avoid the trip through numeric completely if the query
wants a bigint, if there were a val.bigint in JsonbValue.
But of course that would complicate everything else that
touches JsonbValue. Is there a way for a jsonpath operator to
determine that it's the terminal operation in the path, and
leave a value in val.bigint if it is, or build a numeric if
it's not? Then most other jsonpath code could go on expecting
a numeric value is always in val.numeric, and the only code
checking for a val.bigint would be code involved with
getting the result value out to the SQL caller.
Regards,
-Chap
[1]: https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html
https://www.ietf.org/archive/id/draft-goessner-dispatch-jsonpath-00.html
[2]: https://commitfest.postgresql.org/44/4476/
On 2023-08-30 11:18, Chapman Flack wrote:
If I look in [1], am I looking in the right place for the most
current jsonpath draft?
My bad, I see that it is not. Um if I look in [1'], am I then looking
at the same spec you are?
[1'] https://www.ietf.org/archive/id/draft-ietf-jsonpath-base-20.html
Regards,
-Chap
On 2023-Aug-30, Chapman Flack wrote:
Hi,
On 2023-08-29 03:05, Jeevan Chalke wrote:
This commit implements jsonpath .bigint(), .integer(), and .number()
---
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods.
---
This commit implements jsonpath .boolean() and .string() methods.Writing as an interested outsider to the jsonpath spec, my first
question would be, is there a published jsonpath spec independent
of PostgreSQL, and are these methods in it, and are the semantics
identical?
Looking at the SQL standard itself, in the 2023 edition section 9.46
"SQL/JSON path language: syntax and semantics", it shows this:
<JSON method> ::=
type <left paren> <right paren>
| size <left paren> <right paren>
| double <left paren> <right paren>
| ceiling <left paren> <right paren>
| floor <left paren> <right paren>
| abs <left paren> <right paren>
| datetime <left paren> [ <JSON datetime template> ] <right paren>
| keyvalue <left paren> <right paren>
| bigint <left paren> <right paren>
| boolean <left paren> <right paren>
| date <left paren> <right paren>
| decimal <left paren> [ <precision> [ <comma> <scale> ] ] <right paren>
| integer <left paren> <right paren>
| number <left paren> <right paren>
| string <left paren> <right paren>
| time <left paren> [ <time precision> ] <right paren>
| time_tz <left paren> [ <time precision> ] <right paren>
| timestamp <left paren> [ <timestamp precision> ] <right paren>
| timestamp_tz <left paren> [ <timestamp precision> ] <right paren>
and then details, for each of those, rules like
III) If JM specifies <double>, then:
1) For all j, 1 (one) ≤ j ≤ n,
Case:
a) If I_j is not a number or character string, then let ST be data
exception — non-numeric SQL/JSON item (22036).
b) Otherwise, let X be an SQL variable whose value is I_j.
Let V_j be the result of
CAST (X AS DOUBLE PRECISION)
If this conversion results in an exception condition, then
let ST be that exception condition.
2) Case:
a) If ST is not successful completion, then the result of JAE
is ST.
b) Otherwise, the result of JAE is the SQL/JSON sequence V_1,
..., V_n.
so at least superficially our implementation is constrained by what the
SQL standard says to do, and we should verify that this implementation
matches those rules. We don't necessarily need to watch what do other
specs such as jsonpath itself.
The question comes out of my experience on a PostgreSQL integration
of XQuery/XPath, which was nontrivial because the w3 specs for those
languages give rigorous definitions of their data types, independently
of SQL, and a good bit of the work was squinting at those types and at
the corresponding PostgreSQL types to see in what ways they were
different, and what the constraints on converting them were.
Yeah, I think the experience of the SQL committee with XML was pretty
bad, as you carefully documented. I hope they don't make such a mess
with JSON.
--
Álvaro Herrera 48°01'N 7°57'E — https://www.EnterpriseDB.com/
On 2023-08-30 12:28, Alvaro Herrera wrote:
Yeah, I think the experience of the SQL committee with XML was pretty
bad, as you carefully documented. I hope they don't make such a mess
with JSON.
I guess the SQL committee was taken by surprise after basing something
on Infoset and XPath 1.0 for 2003, and then w3 deciding those things
needed to be scrapped and redone with the lessons learned. So the
SQL committee had to come out with a rather different SQL/XML for 2006,
but I'd say the 2003-2006 difference is the only real 'mess', and other
than going back in time to unpublish 2003, I'm not sure how they'd have
done better.
b) Otherwise, the result of JAE is the SQL/JSON sequence V_1,
..., V_n.
This has my Spidey sense tingling, as it seems very parallel to SQL/XML
where the result of XMLQUERY is to have type XML(SEQUENCE), which is a
type we do not have, and I'm not sure we have a type for "JSON sequence"
either, unless SQL/JSON makes it equivalent to a JSON array (which
I guess is conceivable, more easily than with XML). What does SQL/JSON
say about this SQL/JSON sequence type and how it should behave?
Regards,
-Chap
On 8/30/23 19:20, Chapman Flack wrote:
On 2023-08-30 12:28, Alvaro Herrera wrote:
b) Otherwise, the result of JAE is the SQL/JSON sequence V_1,
..., V_n.This has my Spidey sense tingling, as it seems very parallel to SQL/XML
where the result of XMLQUERY is to have type XML(SEQUENCE), which is a
type we do not have, and I'm not sure we have a type for "JSON sequence"
either, unless SQL/JSON makes it equivalent to a JSON array (which
I guess is conceivable, more easily than with XML). What does SQL/JSON
say about this SQL/JSON sequence type and how it should behave?
The SQL/JSON data model comprises SQL/JSON items and SQL/JSON sequences.
The components of the SQL/JSON data model are:
— An SQL/JSON item is defined recursively as any of the following:
• An SQL/JSON scalar, defined as a non-null value of any of the
following predefined (SQL) types: character string with character
set Unicode, numeric, Boolean, or datetime.
• An SQL/JSON null, defined as a value that is distinct from any
value of any SQL type. NOTE 109 — An SQL/JSON null is distinct
from the SQL null value.
• An SQL/JSON array, defined as an ordered list of zero or more
SQL/JSON items, called the SQL/JSON elements of the SQL/JSON
array.
• An SQL/JSON object, defined as an unordered collection of zero or
more SQL/JSON members, where an SQL/JSON member is a pair whose
first value is a character string with character set Unicode and
whose second value is an SQL/JSON item. The first value of an
SQL/JSON member is called the key and the second value is called
the bound value.
— An SQL/JSON sequence is an ordered list of zero or more SQL/JSON
items.
--
Vik Fearing
On 2023-08-31 20:50, Vik Fearing wrote:
— An SQL/JSON item is defined recursively as any of the following:
...
• An SQL/JSON array, defined as an ordered list of zero or more
SQL/JSON items, called the SQL/JSON elements of the SQL/JSON
array.
...
— An SQL/JSON sequence is an ordered list of zero or more SQL/JSON
items.
As I was thinking, because "an ordered list of zero or more SQL/JSON
items" is also exactly what an SQL/JSON array is, it seems at least
possible to implement things that are specified to return "SQL/JSON
sequence" by having them return an SQL/JSON array (the kind of thing
that isn't possible for XML(SEQUENCE), because there isn't any other
XML construct that can subsume it).
Still, it seems noteworthy that both terms are used in the spec, rather
than saying the function in question should return a JSON array. Makes
me wonder if there are some other details that make the two distinct.
Regards,
-Chap
Looking at the SQL standard itself, in the 2023 edition section 9.46
"SQL/JSON path language: syntax and semantics", it shows this:<JSON method> ::=
type <left paren> <right paren>
| size <left paren> <right paren>
| double <left paren> <right paren>
| ceiling <left paren> <right paren>
| floor <left paren> <right paren>
| abs <left paren> <right paren>
| datetime <left paren> [ <JSON datetime template> ] <right paren>
| keyvalue <left paren> <right paren>
| bigint <left paren> <right paren>
| boolean <left paren> <right paren>
| date <left paren> <right paren>
| decimal <left paren> [ <precision> [ <comma> <scale> ] ] <right paren>
| integer <left paren> <right paren>
| number <left paren> <right paren>
| string <left paren> <right paren>
| time <left paren> [ <time precision> ] <right paren>
| time_tz <left paren> [ <time precision> ] <right paren>
| timestamp <left paren> [ <timestamp precision> ] <right paren>
| timestamp_tz <left paren> [ <timestamp precision> ] <right paren>and then details, for each of those, rules like
III) If JM specifies <double>, then:
1) For all j, 1 (one) ≤ j ≤ n,
Case:
a) If I_j is not a number or character string, then let ST be data
exception — non-numeric SQL/JSON item (22036).
b) Otherwise, let X be an SQL variable whose value is I_j.
Let V_j be the result of
CAST (X AS DOUBLE PRECISION)
If this conversion results in an exception condition, then
let ST be that exception condition.
2) Case:
a) If ST is not successful completion, then the result of JAE
is ST.
b) Otherwise, the result of JAE is the SQL/JSON sequence V_1,
..., V_n.so at least superficially our implementation is constrained by what the
SQL standard says to do, and we should verify that this implementation
matches those rules. We don't necessarily need to watch what do other
specs such as jsonpath itself.
I believe our current implementation of the .double() method is in line with
this. And these new methods are following the same suit.
- surely there's a more direct way to make boolean from numeric
than to serialize the numeric and parse an int?
Yeah, we can directly check the value = 0 for false, true otherwise.
But looking at the PostgreSQL conversion to bool, it doesn't allow floating
point values to be converted to boolean and only accepts int4. That's why I
did the int4 conversion.
Thanks
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.
A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?
v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patch
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.
These should accept an optional precision argument. Did you plan to add
that?
v1-0003-Implement-jsonpath-.boolean-and-.string-methods.patch
This commit implements jsonpath .boolean() and .string() methods.
This contains a compiler warning:
../src/backend/utils/adt/jsonpath_exec.c: In function
'executeItemOptUnwrapTarget':
../src/backend/utils/adt/jsonpath_exec.c:1162:86: error: 'tmp' may be
used uninitialized [-Werror=maybe-uninitialized]
v1-0004-Implement-jasonpath-.decimal-precision-scale-meth.patch
This commit implements jsonpath .decimal() method with optional
precision and scale. If precision and scale are provided, then
it is converted to the equivalent numerictypmod and applied to the
numeric number.
This also contains compiler warnings:
../src/backend/utils/adt/jsonpath_exec.c: In function
'executeItemOptUnwrapTarget':
../src/backend/utils/adt/jsonpath_exec.c:1403:53: error: declaration of
'numstr' shadows a previous local [-Werror=shadow=compatible-local]
../src/backend/utils/adt/jsonpath_exec.c:1442:54: error: declaration of
'elem' shadows a previous local [-Werror=shadow=compatible-local]
There is a typo in the commit message: "Implement jasonpath"
Any reason this patch is separate from 0002? Isn't number() and
decimal() pretty similar?
You could also update src/backend/catalog/sql_features.txt in each patch
(features T865 through T878).
On Fri, Oct 6, 2023 at 7:47 PM Peter Eisentraut <peter@eisentraut.org> wrote:
On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patch
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.These should accept an optional precision argument. Did you plan to add
that?
compiler warnings issue resolved.
I figured out how to use the precision argument.
But I don't know how to get the precision argument in the parse stage.
attached is my attempt to implement: select
jsonb_path_query('"2017-03-10 11:11:01.123"', '$.timestamp(2)');
not that familiar with src/backend/utils/adt/jsonpath_gram.y. imitate
decimal method failed. decimal has precision and scale two arguments.
here only one argument.
looking for hints.
Attachments:
test.diffapplication/x-patch; name=test.diffDownload
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 42f325bd..460e43cb 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -1144,6 +1144,7 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiDiv ||
v->type == jpiMod ||
v->type == jpiDecimal ||
+ v->type == jpiTimestamp ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.left);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 94f1052d..4241837f 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1096,7 +1096,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
case jpiStringFunc:
{
JsonbValue jbv;
- char *tmp;
+ char *tmp = NULL;
switch (JsonbType(jb))
{
@@ -1159,6 +1159,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
res = jperOk;
jb = &jbv;
+ Assert(tmp != NULL); /* above switch case, covered all the case jbvType */
jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
jb->val.string.len = strlen(jb->val.string.val);
jb->type = jbvString;
@@ -1400,9 +1401,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
/* cast string as number */
Datum datum;
bool noerr;
- char *numstr = pnstrdup(jb->val.string.val,
- jb->val.string.len);
ErrorSaveContext escontext = {T_ErrorSaveContext};
+ numstr = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
InvalidOid, -1,
@@ -1439,7 +1440,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
if (jsp->type == jpiDecimal && jsp->content.args.left)
{
- JsonPathItem elem;
Datum numdatum;
Datum dtypmod;
int32 precision;
@@ -2443,6 +2443,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
case jpiTimestamp:
{
+ Timestamp tmp;
/* Convert result type to timestamp without time zone */
switch (typid)
{
@@ -2468,6 +2469,9 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
typid = TIMESTAMPOID;
+ tmp = DatumGetTimestamp(value);
+ AdjustTimestampForTypmod(&tmp, 2, NULL);
+ value = TimestampGetDatum(tmp);
}
break;
case jpiTimestampTz:
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index da031e35..ec188472 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -264,6 +264,18 @@ accessor_op:
errmsg("invalid input syntax for type %s", "jsonpath"),
errdetail(".decimal() can only have an optional precision[,scale].")));
}
+ | '.' TIMESTAMP_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiTimestamp, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiTimestamp, linitial($4), NULL);
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".jpiTimestamp() can only have an optional precision.")));
+ }
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
@@ -355,7 +367,6 @@ method:
| DATE_P { $$ = jpiDate; }
| TIME_P { $$ = jpiTime; }
| TIME_TZ_P { $$ = jpiTimeTz; }
- | TIMESTAMP_P { $$ = jpiTimestamp; }
| TIMESTAMP_TZ_P { $$ = jpiTimestampTz; }
;
%%
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut <peter@eisentraut.org>
wrote:
On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?
Yeah, that's the better suggestion. While implementing these methods, I was
confused about where to put them exactly and tried keeping them in some
logical place.
I think once these methods get in, we can have a follow-up patch
reorganizing all of these.
v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patch
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.These should accept an optional precision argument. Did you plan to add
that?
Yeah, will add that.
v1-0003-Implement-jsonpath-.boolean-and-.string-methods.patch
This commit implements jsonpath .boolean() and .string() methods.
This contains a compiler warning:
../src/backend/utils/adt/jsonpath_exec.c: In function
'executeItemOptUnwrapTarget':
../src/backend/utils/adt/jsonpath_exec.c:1162:86: error: 'tmp' may be
used uninitialized [-Werror=maybe-uninitialized]v1-0004-Implement-jasonpath-.decimal-precision-scale-meth.patch
This commit implements jsonpath .decimal() method with optional
precision and scale. If precision and scale are provided, then
it is converted to the equivalent numerictypmod and applied to the
numeric number.This also contains compiler warnings:
Thanks, for reporting these warnings. I don't get those on my machine, thus
missed them. Will fix them.
../src/backend/utils/adt/jsonpath_exec.c: In function
'executeItemOptUnwrapTarget':
../src/backend/utils/adt/jsonpath_exec.c:1403:53: error: declaration of
'numstr' shadows a previous local [-Werror=shadow=compatible-local]
../src/backend/utils/adt/jsonpath_exec.c:1442:54: error: declaration of
'elem' shadows a previous local [-Werror=shadow=compatible-local]There is a typo in the commit message: "Implement jasonpath"
Will fix.
Any reason this patch is separate from 0002? Isn't number() and
decimal() pretty similar?
Since DECIMAL has precision and scale arguments, I have implemented that at
the end. I tried merging that with 0001, but other patches ended up with
the conflicts and thus I didn't merge that and kept it as a separate patch.
But yes, logically it belongs to the 0001 group. My bad that I haven't put
in that extra effort. Will do that in the next version. Sorry for the same.
You could also update src/backend/catalog/sql_features.txt in each patch
(features T865 through T878).
OK.
Thanks
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
On Wed, Oct 18, 2023 at 4:50 PM jian he <jian.universality@gmail.com> wrote:
On Fri, Oct 6, 2023 at 7:47 PM Peter Eisentraut <peter@eisentraut.org>
wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patch
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.These should accept an optional precision argument. Did you plan to add
that?compiler warnings issue resolved.
Thanks for pitching in, Jian.
I was slightly busy with other stuff and thus could not spend time on this.
I will start looking into it and expect a patch in a couple of days.
I figured out how to use the precision argument.
But I don't know how to get the precision argument in the parse stage.attached is my attempt to implement: select
jsonb_path_query('"2017-03-10 11:11:01.123"', '$.timestamp(2)');
not that familiar with src/backend/utils/adt/jsonpath_gram.y. imitate
decimal method failed. decimal has precision and scale two arguments.
here only one argument.looking for hints.
You may refer to how .datetime(<format>) is implemented.
Thanks
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
On Thu, Oct 19, 2023 at 11:36 AM Jeevan Chalke <
jeevan.chalke@enterprisedb.com> wrote:
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut <peter@eisentraut.org>
wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?Yeah, that's the better suggestion. While implementing these methods, I
was confused about where to put them exactly and tried keeping them in some
logical place.
I think once these methods get in, we can have a follow-up patch
reorganizing all of these.v1-0002-Implement-.date-.time-.time_tz-.timestamp-and-.ti.patch
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. All these methods
accept no argument and use ISO datetime formats.These should accept an optional precision argument. Did you plan to add
that?Yeah, will add that.
v1-0003-Implement-jsonpath-.boolean-and-.string-methods.patch
This commit implements jsonpath .boolean() and .string() methods.
This contains a compiler warning:
../src/backend/utils/adt/jsonpath_exec.c: In function
'executeItemOptUnwrapTarget':
../src/backend/utils/adt/jsonpath_exec.c:1162:86: error: 'tmp' may be
used uninitialized [-Werror=maybe-uninitialized]v1-0004-Implement-jasonpath-.decimal-precision-scale-meth.patch
This commit implements jsonpath .decimal() method with optional
precision and scale. If precision and scale are provided, then
it is converted to the equivalent numerictypmod and applied to the
numeric number.This also contains compiler warnings:
Thanks, for reporting these warnings. I don't get those on my machine,
thus missed them. Will fix them.../src/backend/utils/adt/jsonpath_exec.c: In function
'executeItemOptUnwrapTarget':
../src/backend/utils/adt/jsonpath_exec.c:1403:53: error: declaration of
'numstr' shadows a previous local [-Werror=shadow=compatible-local]
../src/backend/utils/adt/jsonpath_exec.c:1442:54: error: declaration of
'elem' shadows a previous local [-Werror=shadow=compatible-local]There is a typo in the commit message: "Implement jasonpath"
Will fix.
Any reason this patch is separate from 0002? Isn't number() and
decimal() pretty similar?Since DECIMAL has precision and scale arguments, I have implemented that
at the end. I tried merging that with 0001, but other patches ended up with
the conflicts and thus I didn't merge that and kept it as a separate patch.
But yes, logically it belongs to the 0001 group. My bad that I haven't put
in that extra effort. Will do that in the next version. Sorry for the same.You could also update src/backend/catalog/sql_features.txt in each patch
(features T865 through T878).OK.
Attached are all three patches fixing the above comments.
Thanks
Thanks
--
Jeevan Chalke*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
Attachments:
v2-0001-Implement-jsonpath-.bigint-.integer-.number-and-..patchapplication/octet-stream; name=v2-0001-Implement-jsonpath-.bigint-.integer-.number-and-..patchDownload
From 9751a1bf7b64c8d564a1d53d4f24f169b73e2230 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 23 Oct 2023 12:38:44 +0530
Subject: [PATCH v2 1/3] Implement jsonpath .bigint(), .integer(), .number()
and .decimal([precision [, scale]]) methods
This commit implements jsonpath .bigint(), .integer(), .number(), and
.decimal() with optional precision and scale methods. The JSON string
or a numeric value is converted to the bigint, int4, and numeric type
representation. If precision and scale are provided for .decimal(),
then it is converted to the equivalent numerictypmod and applied to
the numeric number.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 56 +++
src/backend/catalog/sql_features.txt | 10 +-
src/backend/utils/adt/jsonpath.c | 46 +++
src/backend/utils/adt/jsonpath_exec.c | 270 ++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 45 ++-
src/backend/utils/adt/jsonpath_scan.l | 4 +
src/include/utils/jsonpath.h | 4 +
src/test/regress/expected/jsonb_jsonpath.out | 510 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 +
src/test/regress/sql/jsonb_jsonpath.sql | 129 +++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 1081 insertions(+), 7 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 7c3e940..c2c5305 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17641,6 +17641,62 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>decimal( [ <replaceable>precision</replaceable> [ , <replaceable>scale</replaceable> ] ] )</literal>
+ <returnvalue><replaceable>decimal</replaceable></returnvalue>
+ </para>
+ <para>
+ Rounded decimal value converted from a JSON number or string. <literal>precision</literal> and <literal>scale</literal> must be integer values.
+ </para>
+ <para>
+ <literal>jsonb_path_query('1234.5678', '$.decimal(6, 2)')</literal>
+ <returnvalue>1234.57</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
+ <returnvalue><replaceable>bigint</replaceable></returnvalue>
+ </para>
+ <para>
+ Big integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')</literal>
+ <returnvalue>9876543219</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>integer()</literal>
+ <returnvalue><replaceable>integer</replaceable></returnvalue>
+ </para>
+ <para>
+ Integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "12345"}', '$.len.integer()')</literal>
+ <returnvalue>12345</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>number()</literal>
+ <returnvalue><replaceable>numeric</replaceable></returnvalue>
+ </para>
+ <para>
+ Numeric value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "123.45"}', '$.len.number()')</literal>
+ <returnvalue>123.45</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index b33065d..b8b1b8b 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -574,13 +574,13 @@ T861 SQL/JSON simplified accessor: case-sensitive JSON member accessor NO
T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
-T865 SQL/JSON item method: bigint() NO
+T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
T867 SQL/JSON item method: date() NO
-T868 SQL/JSON item method: decimal() NO
-T869 SQL/JSON item method: decimal() with precision and scale NO
-T870 SQL/JSON item method: integer() NO
-T871 SQL/JSON item method: number() NO
+T868 SQL/JSON item method: decimal() YES
+T869 SQL/JSON item method: decimal() with precision and scale YES
+T870 SQL/JSON item method: integer() YES
+T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
T873 SQL/JSON item method: time() NO
T874 SQL/JSON item method: time_tz() NO
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index c5ba3b7..ba23ade 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -294,6 +294,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMul:
case jpiDiv:
case jpiMod:
+ case jpiDecimal:
case jpiStartsWith:
{
/*
@@ -444,6 +445,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiCeiling:
case jpiDouble:
case jpiKeyValue:
+ case jpiBigint:
+ case jpiInteger:
+ case jpiNumber:
break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
@@ -730,6 +734,30 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
+ case jpiDecimal:
+ appendStringInfoString(buf, ".decimal(");
+ if (v->content.args.left)
+ {
+ jspGetLeftArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ if (v->content.args.right)
+ {
+ appendStringInfoChar(buf, ',');
+ jspGetRightArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiBigint:
+ appendStringInfoString(buf, ".bigint()");
+ break;
+ case jpiInteger:
+ appendStringInfoString(buf, ".integer()");
+ break;
+ case jpiNumber:
+ appendStringInfoString(buf, ".number()");
+ break;
case jpiDatetime:
appendStringInfoString(buf, ".datetime(");
if (v->content.arg)
@@ -795,6 +823,14 @@ jspOperationName(JsonPathItemType type)
return "keyvalue";
case jpiDouble:
return "double";
+ case jpiDecimal:
+ return "decimal";
+ case jpiBigint:
+ return "bigint";
+ case jpiInteger:
+ return "integer";
+ case jpiNumber:
+ return "number";
case jpiAbs:
return "abs";
case jpiFloor:
@@ -897,6 +933,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiFloor:
case jpiCeiling:
case jpiDouble:
+ case jpiBigint:
+ case jpiInteger:
+ case jpiNumber:
case jpiKeyValue:
case jpiLast:
break;
@@ -922,6 +961,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiGreater:
case jpiLessOrEqual:
case jpiGreaterOrEqual:
+ case jpiDecimal:
case jpiStartsWith:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
@@ -1012,6 +1052,10 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiFloor ||
v->type == jpiCeiling ||
v->type == jpiDouble ||
+ v->type == jpiDecimal ||
+ v->type == jpiBigint ||
+ v->type == jpiInteger ||
+ v->type == jpiNumber ||
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
@@ -1041,6 +1085,7 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.left);
@@ -1062,6 +1107,7 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.right);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 2d0599b..f5b7f72 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1098,6 +1098,276 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBigint:
+ {
+ JsonbValue jbv;
+ Datum datum;
+ bool noerr;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as bigint */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiInteger:
+ {
+ JsonbValue jbv;
+ Datum datum;
+ bool noerr;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as integer */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiNumber:
+ case jpiDecimal:
+ {
+ JsonbValue jbv;
+ Numeric num;
+ char *numstr = NULL;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ num = jb->val.numeric;
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->type == jpiDecimal)
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as number */
+ Datum datum;
+ bool noerr;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
+
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(datum);
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ /*
+ * If we have arguments, then they must be the precision and
+ * optional scale used in .decimal(). Convert them to the
+ * typmod equivalent and then truncate the numeric value per
+ * this typmod details.
+ */
+ if (jsp->type == jpiDecimal && jsp->content.args.left)
+ {
+ Datum numdatum;
+ Datum dtypmod;
+ int32 precision;
+ int32 scale = 0;
+ bool have_error;
+ bool noerr;
+ ArrayType *arrtypmod;
+ Datum datums[2];
+ char pstr[12]; /* sign, 10 digits and '\0' */
+ char sstr[12]; /* sign, 10 digits and '\0' */
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ jspGetLeftArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() precision");
+
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+ errmsg("precision is out of integer range"))));
+
+ if (jsp->content.args.right)
+ {
+ jspGetRightArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() scale");
+
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+ errmsg("scale is out of integer range"))));
+ }
+
+ /*
+ * numerictypmodin() takes the precision and scale in the
+ * form of CString arrays.
+ */
+ pg_ltoa(precision, pstr);
+ datums[0] = CStringGetDatum(pstr);
+ pg_ltoa(scale, sstr);
+ datums[1] = CStringGetDatum(sstr);
+ arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
+
+ dtypmod = DirectFunctionCall1(numerictypmodin,
+ PointerGetDatum(arrtypmod));
+
+ /* Convert numstr to Numeric with typmod */
+ Assert(numstr != NULL);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, dtypmod,
+ (Node *) &escontext,
+ &numdatum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(numdatum);
+ pfree(arrtypmod);
+ }
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = num;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiDatetime:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index adc259d..074a00a 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -82,15 +82,16 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P
+%token <str> BIGINT_P INTEGER_P NUMBER_P DECIMAL_P
%type <result> result
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template
+ datetime_template opt_datetime_template csv_elem
-%type <elems> accessor_expr
+%type <elems> accessor_expr csv_list opt_csv_list
%type <indexs> index_list
@@ -248,11 +249,44 @@ accessor_op:
| array_accessor { $$ = $1; }
| '.' any_path { $$ = $2; }
| '.' method '(' ')' { $$ = makeItemType($2); }
+ | '.' DECIMAL_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiDecimal, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
+ else if (list_length($4) == 2)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".decimal() can only have an optional precision[,scale].")));
+ }
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+csv_elem:
+ INT_P
+ { $$ = makeItemNumeric(&$1); }
+ | '+' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
+ | '-' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
+ ;
+
+csv_list:
+ csv_elem { $$ = list_make1($1); }
+ | csv_list ',' csv_elem { $$ = lappend($1, $3); }
+ ;
+
+opt_csv_list:
+ csv_list { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -283,6 +317,10 @@ key_name:
| TYPE_P
| FLOOR_P
| DOUBLE_P
+ | DECIMAL_P
+ | BIGINT_P
+ | INTEGER_P
+ | NUMBER_P
| CEILING_P
| DATETIME_P
| KEYVALUE_P
@@ -299,6 +337,9 @@ method:
| TYPE_P { $$ = jpiType; }
| FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
+ | BIGINT_P { $$ = jpiBigint; }
+ | INTEGER_P { $$ = jpiInteger; }
+ | NUMBER_P { $$ = jpiNumber; }
| CEILING_P { $$ = jpiCeiling; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 29c26af..b8922ee 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -410,11 +410,15 @@ static const JsonPathKeyword keywords[] = {
{ 4, false, WITH_P, "with"},
{ 5, true, FALSE_P, "false"},
{ 5, false, FLOOR_P, "floor"},
+ { 6, false, BIGINT_P, "bigint"},
{ 6, false, DOUBLE_P, "double"},
{ 6, false, EXISTS_P, "exists"},
+ { 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, DECIMAL_P, "decimal"},
+ { 7, false, INTEGER_P, "integer"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index f0181e0..dd44036 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -89,6 +89,10 @@ typedef enum JsonPathItemType
jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
jpiDouble, /* .double() item method */
+ jpiDecimal, /* .decimal() item method */
+ jpiBigint, /* .bigint() item method */
+ jpiInteger, /* .integer() item method */
+ jpiNumber, /* .number() item method */
jpiDatetime, /* .datetime() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 6659bc9..c51a9df 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1517,6 +1517,516 @@ select jsonb_path_query('"-inf"', '$.double()', silent => true);
------------------
(0 rows)
+select jsonb_path_query('null', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.bigint()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"1.23"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('1e1000', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"nan"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"NaN"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"-inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1234567890123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"+123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.bigint() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.integer()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"1.23"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('1e1000', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"nan"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"NaN"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"-inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"12345678901"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"+123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.integer() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.number()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.number()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.number() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('null', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.decimal()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.decimal()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+ jsonb_path_query
+------------------
+ 12345.7
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+ERROR: NUMERIC precision 0 must be between 1 and 1000
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+ERROR: NUMERIC precision 1001 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ 1200
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+ERROR: NUMERIC precision -6 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+ERROR: NUMERIC scale -1001 must be between -1000 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+ERROR: NUMERIC scale 1001 must be between -1000 and 1000
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ -1200
+(1 row)
+
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+ jsonb_path_query
+------------------
+ 0.01
+(1 row)
+
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+ jsonb_path_query
+------------------
+ 0.0012
+(1 row)
+
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+ jsonb_path_query
+------------------
+ 0
+(1 row)
+
select jsonb_path_query('{}', '$.abs()');
ERROR: jsonpath item method .abs() can only be applied to a numeric value
select jsonb_path_query('true', '$.floor()');
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index eeffb38..15fb717 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -387,6 +387,18 @@ select '$.double().floor().ceiling().abs()'::jsonpath;
$.double().floor().ceiling().abs()
(1 row)
+select '$.bigint().integer().number().decimal()'::jsonpath;
+ jsonpath
+-----------------------------------------
+ $.bigint().integer().number().decimal()
+(1 row)
+
+select '$.decimal(4,2)'::jsonpath;
+ jsonpath
+----------------
+ $.decimal(4,2)
+(1 row)
+
select '$.keyvalue().key'::jsonpath;
jsonpath
--------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e0ce509..2d4a1e7 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -320,6 +320,135 @@ select jsonb_path_query('"-inf"', '$.double()');
select jsonb_path_query('"inf"', '$.double()', silent => true);
select jsonb_path_query('"-inf"', '$.double()', silent => true);
+select jsonb_path_query('null', '$.bigint()');
+select jsonb_path_query('true', '$.bigint()');
+select jsonb_path_query('null', '$.bigint()', silent => true);
+select jsonb_path_query('true', '$.bigint()', silent => true);
+select jsonb_path_query('[]', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()');
+select jsonb_path_query('{}', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+select jsonb_path_query('1.23', '$.bigint()');
+select jsonb_path_query('"1.23"', '$.bigint()');
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+select jsonb_path_query('1e1000', '$.bigint()');
+select jsonb_path_query('"nan"', '$.bigint()');
+select jsonb_path_query('"NaN"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()');
+select jsonb_path_query('"-inf"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+select jsonb_path_query('123', '$.bigint()');
+select jsonb_path_query('"123"', '$.bigint()');
+select jsonb_path_query('1234567890123', '$.bigint()');
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+select jsonb_path_query('"+123"', '$.bigint()');
+select jsonb_path_query('-123', '$.bigint()');
+select jsonb_path_query('"-123"', '$.bigint()');
+select jsonb_path_query('123', '$.bigint() * 2');
+
+select jsonb_path_query('null', '$.integer()');
+select jsonb_path_query('true', '$.integer()');
+select jsonb_path_query('null', '$.integer()', silent => true);
+select jsonb_path_query('true', '$.integer()', silent => true);
+select jsonb_path_query('[]', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()');
+select jsonb_path_query('{}', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+select jsonb_path_query('{}', '$.integer()', silent => true);
+select jsonb_path_query('1.23', '$.integer()');
+select jsonb_path_query('"1.23"', '$.integer()');
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+select jsonb_path_query('1e1000', '$.integer()');
+select jsonb_path_query('"nan"', '$.integer()');
+select jsonb_path_query('"NaN"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()');
+select jsonb_path_query('"-inf"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+select jsonb_path_query('123', '$.integer()');
+select jsonb_path_query('"123"', '$.integer()');
+select jsonb_path_query('12345678901', '$.integer()');
+select jsonb_path_query('"12345678901"', '$.integer()');
+select jsonb_path_query('"+123"', '$.integer()');
+select jsonb_path_query('-123', '$.integer()');
+select jsonb_path_query('"-123"', '$.integer()');
+select jsonb_path_query('123', '$.integer() * 2');
+
+select jsonb_path_query('null', '$.number()');
+select jsonb_path_query('true', '$.number()');
+select jsonb_path_query('null', '$.number()', silent => true);
+select jsonb_path_query('true', '$.number()', silent => true);
+select jsonb_path_query('[]', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()');
+select jsonb_path_query('{}', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+select jsonb_path_query('{}', '$.number()', silent => true);
+select jsonb_path_query('1.23', '$.number()');
+select jsonb_path_query('"1.23"', '$.number()');
+select jsonb_path_query('"1.23aaa"', '$.number()');
+select jsonb_path_query('1e1000', '$.number()');
+select jsonb_path_query('"nan"', '$.number()');
+select jsonb_path_query('"NaN"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()');
+select jsonb_path_query('"-inf"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+select jsonb_path_query('123', '$.number()');
+select jsonb_path_query('"123"', '$.number()');
+select jsonb_path_query('12345678901234567890', '$.number()');
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+select jsonb_path_query('"+12.3"', '$.number()');
+select jsonb_path_query('-12.3', '$.number()');
+select jsonb_path_query('"-12.3"', '$.number()');
+select jsonb_path_query('12.3', '$.number() * 2');
+
+select jsonb_path_query('null', '$.decimal()');
+select jsonb_path_query('true', '$.decimal()');
+select jsonb_path_query('null', '$.decimal()', silent => true);
+select jsonb_path_query('true', '$.decimal()', silent => true);
+select jsonb_path_query('[]', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()');
+select jsonb_path_query('{}', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+select jsonb_path_query('1.23', '$.decimal()');
+select jsonb_path_query('"1.23"', '$.decimal()');
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+select jsonb_path_query('1e1000', '$.decimal()');
+select jsonb_path_query('"nan"', '$.decimal()');
+select jsonb_path_query('"NaN"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()');
+select jsonb_path_query('"-inf"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+select jsonb_path_query('123', '$.decimal()');
+select jsonb_path_query('"123"', '$.decimal()');
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+select jsonb_path_query('"+12.3"', '$.decimal()');
+select jsonb_path_query('-12.3', '$.decimal()');
+select jsonb_path_query('"-12.3"', '$.decimal()');
+select jsonb_path_query('12.3', '$.decimal() * 2');
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+
select jsonb_path_query('{}', '$.abs()');
select jsonb_path_query('true', '$.floor()');
select jsonb_path_query('"1.2"', '$.ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 56e0bef..1f25f89 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -70,6 +70,8 @@ select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
+select '$.bigint().integer().number().decimal()'::jsonpath;
+select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
--
1.8.3.1
v2-0003-Implement-jsonpath-.boolean-and-.string-methods.patchapplication/octet-stream; name=v2-0003-Implement-jsonpath-.boolean-and-.string-methods.patchDownload
From b7e97e2471e8fc6b09affc885da04d4067601167 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 23 Oct 2023 12:38:44 +0530
Subject: [PATCH v2 3/3] Implement jsonpath .boolean() and .string() methods
This commit implements jsonpath .boolean() and .string() methods.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert string to a bool.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 32 ++++
src/backend/catalog/sql_features.txt | 4 +-
src/backend/utils/adt/jsonpath.c | 16 ++
src/backend/utils/adt/jsonpath_exec.c | 147 +++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 6 +-
src/backend/utils/adt/jsonpath_scan.l | 2 +
src/include/utils/jsonpath.h | 2 +
src/test/regress/expected/jsonb_jsonpath.out | 263 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 ++
src/test/regress/sql/jsonb_jsonpath.sql | 58 ++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 541 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index dc8aab3..9610a75 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17626,6 +17626,38 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>boolean()</literal>
+ <returnvalue><replaceable>boolean</replaceable></returnvalue>
+ </para>
+ <para>
+ Boolean value converted from a JSON boolean, number, or string
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')</literal>
+ <returnvalue>[true, true, false]</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>string()</literal>
+ <returnvalue><replaceable>string</replaceable></returnvalue>
+ </para>
+ <para>
+ String value converted from a JSON boolean, number, string, or datetime
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')</literal>
+ <returnvalue>["1.23", "xyz", "false"]</returnvalue>
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.datetime().string()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>double()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 93f2d54..2118d54 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -575,13 +575,13 @@ T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
-T866 SQL/JSON item method: boolean() NO
+T866 SQL/JSON item method: boolean() YES
T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
-T872 SQL/JSON item method: string() NO
+T872 SQL/JSON item method: string() YES
T873 SQL/JSON item method: time() YES
T874 SQL/JSON item method: time_tz() YES
T875 SQL/JSON item method: time precision YES
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 52d8c2e..a494051 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -446,6 +446,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiAbs:
case jpiFloor:
case jpiCeiling:
@@ -728,6 +730,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiBoolean:
+ appendStringInfoString(buf, ".boolean()");
+ break;
+ case jpiStringFunc:
+ appendStringInfoString(buf, ".string()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
@@ -864,6 +872,10 @@ jspOperationName(JsonPathItemType type)
return "type";
case jpiSize:
return "size";
+ case jpiBoolean:
+ return "boolean";
+ case jpiStringFunc:
+ return "string";
case jpiKeyValue:
return "keyvalue";
case jpiDouble:
@@ -984,6 +996,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiAbs:
case jpiFloor:
case jpiCeiling:
@@ -1112,6 +1126,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiIsUnknown ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiBoolean ||
+ v->type == jpiStringFunc ||
v->type == jpiAbs ||
v->type == jpiFloor ||
v->type == jpiCeiling ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 32f5ed7..4a9f79d 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1021,6 +1021,153 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBoolean:
+ {
+ JsonbValue jbv;
+ bool bval;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvBool)
+ {
+ bval = jb->val.boolean;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvNumeric)
+ {
+ int ival;
+ Datum datum;
+ bool noerr;
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ jspOperationName(jsp->type)))));
+
+ ival = DatumGetInt32(datum);
+ if (ival == 0)
+ bval = false;
+ else
+ bval = true;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as boolean */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+
+ if (!parse_bool(tmp, &bval))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, or numeric value",
+ jspOperationName(jsp->type)))));
+
+ jb = &jbv;
+ jb->type = jbvBool;
+ jb->val.boolean = bval;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiStringFunc:
+ {
+ JsonbValue jbv;
+ char *tmp = NULL;
+
+ switch (JsonbType(jb))
+ {
+ case jbvString:
+ /*
+ * Value is not necessarily null-terminated, so we do
+ * pnstrdup() here.
+ */
+ tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ break;
+ case jbvNumeric:
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ break;
+ case jbvBool:
+ tmp = (jb->val.boolean) ? "true" : "false";
+ break;
+ case jbvDatetime:
+ {
+ switch (jb->val.datetime.typid)
+ {
+ case DATEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(date_out,
+ jb->val.datetime.value));
+ break;
+ case TIMEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(time_out,
+ jb->val.datetime.value));
+ break;
+ case TIMETZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timetz_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamp_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPTZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ jb->val.datetime.value));
+ break;
+ default:
+ elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+ jb->val.datetime.typid);
+ }
+ }
+ break;
+ case jbvNull:
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, numeric, or datetime value",
+ jspOperationName(jsp->type)))));
+ break;
+ }
+
+ res = jperOk;
+
+ jb = &jbv;
+ Assert(tmp != NULL); /* We must have set tmp above */
+ jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.len = strlen(jb->val.string.val);
+ jb->type = jbvString;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiAbs:
return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
found);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index f23ed2b..2bfef8b 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -82,7 +82,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
-%token <str> BIGINT_P INTEGER_P NUMBER_P DECIMAL_P
+%token <str> BIGINT_P INTEGER_P NUMBER_P DECIMAL_P BOOLEAN_P STRINGFUNC_P
%type <result> result
@@ -337,6 +337,8 @@ key_name:
| ABS_P
| SIZE_P
| TYPE_P
+ | BOOLEAN_P
+ | STRINGFUNC_P
| FLOOR_P
| DOUBLE_P
| DECIMAL_P
@@ -362,6 +364,8 @@ method:
ABS_P { $$ = jpiAbs; }
| SIZE_P { $$ = jpiSize; }
| TYPE_P { $$ = jpiType; }
+ | BOOLEAN_P { $$ = jpiBoolean; }
+ | STRINGFUNC_P { $$ = jpiStringFunc; }
| FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
| BIGINT_P { $$ = jpiBigint; }
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 6a7d72a..d87da14 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -418,6 +418,8 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
+ { 6, false, STRINGFUNC_P, "string"},
+ { 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index f695451..7058563 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -85,6 +85,8 @@ typedef enum JsonPathItemType
jpiExists, /* EXISTS (expr) predicate */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiBoolean, /* .boolean() item method */
+ jpiStringFunc, /* .string() item method */
jpiAbs, /* .abs() item method */
jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index a4e9ca3..00d2c85 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1348,6 +1348,269 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'l
1
(9 rows)
+select jsonb_path_query('null', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('null', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.boolean()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('{}', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"1.23"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('1e1000', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"nan"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"NaN"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"-inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"100"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('true', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('false', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('0', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('-1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('100', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"1"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"0"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"true"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"false"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"TRUE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"FALSE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"yes"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"NO"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"T"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"f"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"y"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"N"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('true', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('123', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+ jsonb_path_query_array
+------------------------
+ [true, true, false]
+(1 row)
+
+select jsonb_path_query('null', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('null', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('{}', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23aaa"
+(1 row)
+
+select jsonb_path_query('1234', '$.string()');
+ jsonb_path_query
+------------------
+ "1234"
+(1 row)
+
+select jsonb_path_query('true', '$.string()');
+ jsonb_path_query
+------------------
+ "true"
+(1 row)
+
+select jsonb_path_query('1234', '$.string().type()');
+ jsonb_path_query
+------------------
+ "string"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+ jsonb_path_query
+----------------------------
+ "Tue Aug 15 00:04:56 2023"
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+ jsonb_path_query_array
+--------------------------
+ ["1.23", "yes", "false"]
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+ jsonb_path_query_array
+--------------------------------
+ ["string", "string", "string"]
+(1 row)
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
jsonb_path_query
------------------
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index b47b2e9..6b16cda 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -381,6 +381,18 @@ select 'true.type()'::jsonpath;
true.type()
(1 row)
+select '$.boolean()'::jsonpath;
+ jsonpath
+-------------
+ $.boolean()
+(1 row)
+
+select '$.string()'::jsonpath;
+ jsonpath
+------------
+ $.string()
+(1 row)
+
select '$.double().floor().ceiling().abs()'::jsonpath;
jsonpath
------------------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index bab6c95..6ff6419 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -283,6 +283,64 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 's
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => true);
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');
+select jsonb_path_query('null', '$.boolean()');
+select jsonb_path_query('null', '$.boolean()', silent => true);
+select jsonb_path_query('[]', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()');
+select jsonb_path_query('{}', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+select jsonb_path_query('1.23', '$.boolean()');
+select jsonb_path_query('"1.23"', '$.boolean()');
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+select jsonb_path_query('1e1000', '$.boolean()');
+select jsonb_path_query('"nan"', '$.boolean()');
+select jsonb_path_query('"NaN"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()');
+select jsonb_path_query('"-inf"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"100"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean()');
+select jsonb_path_query('false', '$.boolean()');
+select jsonb_path_query('1', '$.boolean()');
+select jsonb_path_query('0', '$.boolean()');
+select jsonb_path_query('-1', '$.boolean()');
+select jsonb_path_query('100', '$.boolean()');
+select jsonb_path_query('"1"', '$.boolean()');
+select jsonb_path_query('"0"', '$.boolean()');
+select jsonb_path_query('"true"', '$.boolean()');
+select jsonb_path_query('"false"', '$.boolean()');
+select jsonb_path_query('"TRUE"', '$.boolean()');
+select jsonb_path_query('"FALSE"', '$.boolean()');
+select jsonb_path_query('"yes"', '$.boolean()');
+select jsonb_path_query('"NO"', '$.boolean()');
+select jsonb_path_query('"T"', '$.boolean()');
+select jsonb_path_query('"f"', '$.boolean()');
+select jsonb_path_query('"y"', '$.boolean()');
+select jsonb_path_query('"N"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean().type()');
+select jsonb_path_query('123', '$.boolean().type()');
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+
+select jsonb_path_query('null', '$.string()');
+select jsonb_path_query('null', '$.string()', silent => true);
+select jsonb_path_query('[]', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()');
+select jsonb_path_query('{}', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+select jsonb_path_query('{}', '$.string()', silent => true);
+select jsonb_path_query('1.23', '$.string()');
+select jsonb_path_query('"1.23"', '$.string()');
+select jsonb_path_query('"1.23aaa"', '$.string()');
+select jsonb_path_query('1234', '$.string()');
+select jsonb_path_query('true', '$.string()');
+select jsonb_path_query('1234', '$.string().type()');
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 44275c9..8f07b71 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -69,6 +69,8 @@ select '(1).type()'::jsonpath;
select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
+select '$.boolean()'::jsonpath;
+select '$.string()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
select '$.bigint().integer().number().decimal()'::jsonpath;
select '$.decimal(4,2)'::jsonpath;
--
1.8.3.1
v2-0002-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchapplication/octet-stream; name=v2-0002-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchDownload
From 4430740c888636d400f7ab69872040fc966894c4 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 23 Oct 2023 12:38:44 +0530
Subject: [PATCH v2 2/3] Implement jsonpath .date(), .time(), .time_tz(),
.timestamp(), and .timestamp_tz() methods
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.
The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. Unlike the
.datetime() method, all these methods don't accept format templates
and use ISO DateTime formats instead. However, except the .date()
method, these methods take an optional precision to adjust the
fractional seconds.
Jeevan Chalke
---
doc/src/sgml/func.sgml | 130 ++++
src/backend/catalog/sql_features.txt | 14 +-
src/backend/utils/adt/jsonpath.c | 71 +-
src/backend/utils/adt/jsonpath_exec.c | 301 +++++++-
src/backend/utils/adt/jsonpath_gram.y | 34 +-
src/backend/utils/adt/jsonpath_scan.l | 5 +
src/include/utils/jsonpath.h | 5 +
src/test/regress/expected/jsonb_jsonpath.out | 1053 +++++++++++++++++++++++++-
src/test/regress/expected/jsonpath.out | 54 ++
src/test/regress/sql/jsonb_jsonpath.sql | 295 ++++++++
src/test/regress/sql/jsonpath.sql | 9 +
11 files changed, 1932 insertions(+), 39 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c2c5305..dc8aab3 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17770,6 +17770,136 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>date()</literal>
+ <returnvalue><replaceable>date</replaceable></returnvalue>
+ </para>
+ <para>
+ Date value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.date()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time()</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56"', '$.time()')</literal>
+ <returnvalue>"12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789"', '$.time(2)')</literal>
+ <returnvalue>"12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz()</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')</literal>
+ <returnvalue>"12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)')</literal>
+ <returnvalue>"12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp()</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')</literal>
+ <returnvalue>"2023-08-15T12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string, with
+ fractional seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz()</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')</literal>
+ <returnvalue>"2023-08-15T12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>object</replaceable> <literal>.</literal> <literal>keyvalue()</literal>
<returnvalue><replaceable>array</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index b8b1b8b..93f2d54 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -576,18 +576,18 @@ T863 SQL/JSON simplified accessor: single-quoted string literal as member access
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
-T867 SQL/JSON item method: date() NO
+T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
-T873 SQL/JSON item method: time() NO
-T874 SQL/JSON item method: time_tz() NO
-T875 SQL/JSON item method: time precision NO
-T876 SQL/JSON item method: timestamp() NO
-T877 SQL/JSON item method: timestamp_tz() NO
-T878 SQL/JSON item method: timestamp precision NO
+T873 SQL/JSON item method: time() YES
+T874 SQL/JSON item method: time_tz() YES
+T875 SQL/JSON item method: time precision YES
+T876 SQL/JSON item method: timestamp() YES
+T877 SQL/JSON item method: timestamp_tz() YES
+T878 SQL/JSON item method: timestamp precision YES
T879 JSON in equality operations YES with jsonb
T880 JSON in grouping operations YES with jsonb
T881 JSON in ordering operations NO with jsonb, partially supported
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index ba23ade..52d8c2e 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -356,6 +356,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMinus:
case jpiExists:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
{
int32 arg = reserveSpaceForItemPointer(buf);
@@ -369,6 +373,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
*(int32 *) (buf->data + arg) = chld - pos;
}
break;
+ case jpiDate:
+ break;
case jpiNull:
break;
case jpiRoot:
@@ -767,6 +773,45 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
}
appendStringInfoChar(buf, ')');
break;
+ case jpiDate:
+ appendStringInfoString(buf, ".date()");
+ break;
+ case jpiTime:
+ appendStringInfoString(buf, ".time(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimeTz:
+ appendStringInfoString(buf, ".time_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestamp:
+ appendStringInfoString(buf, ".timestamp(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestampTz:
+ appendStringInfoString(buf, ".timestamp_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
case jpiKeyValue:
appendStringInfoString(buf, ".keyvalue()");
break;
@@ -839,6 +884,16 @@ jspOperationName(JsonPathItemType type)
return "ceiling";
case jpiDatetime:
return "datetime";
+ case jpiDate:
+ return "date";
+ case jpiTime:
+ return "time";
+ case jpiTimeTz:
+ return "time_tz";
+ case jpiTimestamp:
+ return "timestamp";
+ case jpiTimestampTz:
+ return "timestamp_tz";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -938,6 +993,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiNumber:
case jpiKeyValue:
case jpiLast:
+ case jpiDate:
break;
case jpiKey:
case jpiString:
@@ -979,6 +1035,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiMinus:
case jpiFilter:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
read_int32(v->content.arg, base, pos);
break;
case jpiIndexArray:
@@ -1004,7 +1064,11 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiExists ||
v->type == jpiPlus ||
v->type == jpiMinus ||
- v->type == jpiDatetime);
+ v->type == jpiDatetime ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
jspInitByBuffer(a, v->base, v->content.arg);
}
@@ -1057,6 +1121,11 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiInteger ||
v->type == jpiNumber ||
v->type == jpiDatetime ||
+ v->type == jpiDate ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
v->type == jpiLikeRegex);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index f5b7f72..32f5ed7 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1369,6 +1369,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
case jpiDatetime:
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -2041,11 +2046,15 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
/*
- * Implementation of the .datetime() method.
+ * Implementation of the .datetime() and related methods.
*
* 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.
+ *
+ * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
+ * have a format, so ISO format is used. However, except .date(), they all
+ * take an optional time precision.
*/
static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
@@ -2061,6 +2070,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
bool hasNext;
JsonPathExecResult res = jperNotFound;
JsonPathItem elem;
+ int32 time_precision = -1;
if (!(jb = getScalar(jb, jbvString)))
RETURN_ERROR(ereport(ERROR,
@@ -2078,7 +2088,11 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
collid = DEFAULT_COLLATION_OID;
- if (jsp->content.arg)
+ /*
+ * .datetime(template) has an argument, the rest of the methods don't have
+ * an argument. So we handle that separately.
+ */
+ if (jsp->type == jpiDatetime && jsp->content.arg)
{
text *template;
char *template_str;
@@ -2140,6 +2154,30 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
static text *fmt_txt[lengthof(fmt_str)] = {0};
int i;
+ /*
+ * Check for optional precision for methods other than .datetime() and
+ * .date()
+ */
+ if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
+ jsp->content.arg)
+ {
+ bool have_error;
+
+ jspGetArg(jsp, &elem);
+
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for %s argument",
+ jspOperationName(jsp->type));
+
+ time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
/* loop until datetime format fits */
for (i = 0; i < lengthof(fmt_str); i++)
{
@@ -2166,11 +2204,260 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
if (res == jperNotFound)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
- errhint("Use a datetime template argument to specify the input data format."))));
+ {
+ if (jsp->type == jpiDatetime)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("datetime format is not recognized: \"%s\"",
+ text_to_cstring(datetime)),
+ errhint("Use a datetime template argument to specify the input data format."))));
+ else
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("%s format is not recognized: \"%s\"",
+ jspOperationName(jsp->type), text_to_cstring(datetime)))));
+
+ }
+ }
+
+ /*
+ * parse_datetime() processes the entire input string per the template or
+ * ISO format and returns the Datum in best fitted datetime type. So, if
+ * this call is for a specific datatype, then we do the conversion here.
+ * Throw an error for incompatible types.
+ */
+ switch (jsp->type)
+ {
+ case jpiDatetime: /* Nothing to do for DATETIME */
+ break;
+ case jpiDate:
+ {
+ /* Convert result type to date */
+ switch (typid)
+ {
+ case DATEOID: /* Nothing to do for DATE */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("date format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_date,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_date,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = DATEOID;
+ }
+ break;
+ case jpiTime:
+ {
+ /* Convert result type to time without time zone */
+ switch (typid)
+ {
+ case TIMEOID: /* Nothing to do for TIME */
+ break;
+ case DATEOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMETZOID:
+ value = DirectFunctionCall1(timetz_time,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_time,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_time,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeADT result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(false,
+ time_precision);
+ result = DatumGetTimeADT(value);
+ AdjustTimeForTypmod(&result, time_precision);
+ value = TimeADTGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMEOID;
+ }
+ break;
+ case jpiTimeTz:
+ {
+ /* Convert result type to time with time zone */
+ switch (typid)
+ {
+ case TIMETZOID: /* Nothing to do for TIMETZ */
+ break;
+ case DATEOID:
+ case TIMESTAMPOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID:
+ value = DirectFunctionCall1(time_timetz,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timetz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeTzADT *result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(true,
+ time_precision);
+ result = DatumGetTimeTzADTP(value);
+ AdjustTimeForTypmod(&result->time, time_precision);
+ value = TimeTzADTPGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMETZOID;
+ }
+ break;
+ case jpiTimestamp:
+ {
+ /* Convert result type to timestamp without time zone */
+ switch (typid)
+ {
+ case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamp,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timestamp,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(false,
+ time_precision);
+ result = DatumGetTimestamp(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPOID;
+ }
+ break;
+ case jpiTimestampTz:
+ {
+ /* Convert result type to timestamp with time zone */
+ switch (typid)
+ {
+ case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamptz,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_timestamptz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(true,
+ time_precision);
+ result = DatumGetTimestampTz(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampTzGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPTZOID;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
pfree(datetime);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 074a00a..f23ed2b 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -81,7 +81,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
-%token <str> DATETIME_P
+%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
%token <str> BIGINT_P INTEGER_P NUMBER_P DECIMAL_P
%type <result> result
@@ -90,6 +90,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
datetime_template opt_datetime_template csv_elem
+ datetime_method datetime_precision opt_datetime_precision
%type <elems> accessor_expr csv_list opt_csv_list
@@ -263,11 +264,23 @@ accessor_op:
errmsg("invalid input syntax for type %s", "jsonpath"),
errdetail(".decimal() can only have an optional precision[,scale].")));
}
- | '.' DATETIME_P '(' opt_datetime_template ')'
- { $$ = makeItemUnary(jpiDatetime, $4); }
+ | '.' datetime_method { $$ = $2; }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+datetime_method:
+ DATETIME_P '(' opt_datetime_template ')'
+ { $$ = makeItemUnary(jpiDatetime, $3); }
+ | TIME_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTime, $3); }
+ | TIME_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimeTz, $3); }
+ | TIMESTAMP_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestamp, $3); }
+ | TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestampTz, $3); }
+ ;
+
csv_elem:
INT_P
{ $$ = makeItemNumeric(&$1); }
@@ -287,6 +300,15 @@ opt_csv_list:
| /* EMPTY */ { $$ = NULL; }
;
+datetime_precision:
+ INT_P { $$ = makeItemNumeric(&$1); }
+ ;
+
+opt_datetime_precision:
+ datetime_precision { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -323,6 +345,11 @@ key_name:
| NUMBER_P
| CEILING_P
| DATETIME_P
+ | DATE_P
+ | TIME_P
+ | TIME_TZ_P
+ | TIMESTAMP_P
+ | TIMESTAMP_TZ_P
| KEYVALUE_P
| LAST_P
| STARTS_P
@@ -342,6 +369,7 @@ method:
| NUMBER_P { $$ = jpiNumber; }
| CEILING_P { $$ = jpiCeiling; }
| KEYVALUE_P { $$ = jpiKeyValue; }
+ | DATE_P { $$ = jpiDate; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index b8922ee..6a7d72a 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -401,10 +401,12 @@ static const JsonPathKeyword keywords[] = {
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
+ { 4, false, DATE_P, "date"},
{ 4, false, FLAG_P, "flag"},
{ 4, false, LAST_P, "last"},
{ 4, true, NULL_P, "null"},
{ 4, false, SIZE_P, "size"},
+ { 4, false, TIME_P, "time"},
{ 4, true, TRUE_P, "true"},
{ 4, false, TYPE_P, "type"},
{ 4, false, WITH_P, "with"},
@@ -419,10 +421,13 @@ static const JsonPathKeyword keywords[] = {
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
+ { 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
+ { 9, false, TIMESTAMP_P, "timestamp"},
{ 10,false, LIKE_REGEX_P, "like_regex"},
+ { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
/* Check if current scanstring value is a keyword */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index dd44036..f695451 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -94,6 +94,11 @@ typedef enum JsonPathItemType
jpiInteger, /* .integer() item method */
jpiNumber, /* .number() item method */
jpiDatetime, /* .datetime() item method */
+ jpiDate, /* .date() item method */
+ jpiTime, /* .time() item method */
+ jpiTimeTz, /* .time_tz() item method */
+ jpiTimestamp, /* .timestamp() item method */
+ jpiTimestampTz, /* .timestamp_tz() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
jpiLast, /* LAST array subscript */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index c51a9df..a4e9ca3 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2242,7 +2242,443 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
ERROR: unmatched format character "T"
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
ERROR: unmatched format character "T"
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('true', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('1', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('[]', '$.date()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('{}', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.date()');
+ERROR: date format is not recognized: "bogus"
+select jsonb '"2023-08-15"' @? '$.date()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+ jsonb_path_query
+------------------
+ "date"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+ERROR: date format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+ERROR: date format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ERROR: syntax error at or near "2" of jsonpath input
+LINE 1: select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ ^
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('true', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('1', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('[]', '$.time()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('{}', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time()');
+ERROR: time format is not recognized: "bogus"
+select jsonb '"12:34:56"' @? '$.time()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+ jsonb_path_query
+--------------------------
+ "time without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+ERROR: time format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+ERROR: numeric argument of jsonpath item method .time() is out of range for type integer
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+ jsonb_path_query
+------------------
+ "12:34:57"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+ jsonb_path_query
+------------------
+ "12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+WARNING: TIME(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+WARNING: TIME(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------
+ "12:34:56.789012"
+(1 row)
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('true', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('1', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.time_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "bogus"
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+ jsonb_path_query
+-----------------------
+ "time with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15 12:34:56"
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+ERROR: numeric argument of jsonpath item method .time_tz() is out of range for type integer
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+ jsonb_path_query
+------------------
+ "12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+ jsonb_path_query
+---------------------
+ "12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+WARNING: TIME(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+WARNING: TIME(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------------
+ "12:34:56.789012+05:30"
+(1 row)
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+ jsonb_path_query
+-------------------------------
+ "timestamp without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+ERROR: numeric argument of jsonpath item method .timestamp() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:57"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+ jsonb_path_query
+--------------------------
+ "2023-08-15T12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+WARNING: TIMESTAMP(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+WARNING: TIMESTAMP(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------
+ "2023-08-15T12:34:56.789012"
+(1 row)
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+ jsonb_path_query
+----------------------------
+ "timestamp with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T07:00:00+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+ERROR: numeric argument of jsonpath item method .timestamp_tz() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+ jsonb_path_query
+--------------------------------
+ "2023-08-15T12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+WARNING: TIMESTAMP(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+WARNING: TIMESTAMP(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------------
+ "2023-08-15T12:34:56.789012+05:30"
+(1 row)
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "07:04:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+00:00"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2308,6 +2744,36 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "17:04:56+10:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T02:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2373,6 +2839,30 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "00:04:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
jsonb_path_query
------------------
@@ -2529,28 +3019,123 @@ select jsonb_path_query_tz(
"2017-03-10T01:02:03+04:00"
(2 rows)
--- time comparison
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
- jsonb_path_query_tz
----------------------
- "12:35:00"
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-10T00:00:00"
+ "2017-03-10T03:00:00+03:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10T00:00:00"
+ "2017-03-10T12:34:56"
+ "2017-03-10T03:00:00+03:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-09"
+ "2017-03-10T01:02:03+04:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+-- time comparison
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
"12:35:00+00:00"
(2 rows)
@@ -2574,6 +3159,112 @@ select jsonb_path_query_tz(
"13:35:00+01:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+ jsonb_path_query
+------------------
+ "12:35:00.12"
+ "12:36:00.11"
+ "12:35:00.12"
+ "13:35:00.12"
+(4 rows)
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -2620,6 +3311,110 @@ select jsonb_path_query_tz(
"10:35:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00"
+ "12:35:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+ jsonb_path_query
+---------------------
+ "12:35:00.12+01:00"
+ "12:36:00.11+01:00"
+ "12:35:00.12-02:00"
+ "12:35:00.12+00:00"
+ "11:35:00.12+00:00"
+(5 rows)
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2667,6 +3462,111 @@ select jsonb_path_query_tz(
"2017-03-10"
(3 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00+01:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T13:35:00+01:00"
+ "2017-03-10T12:35:00-01:00"
+ "2017-03-11"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+ jsonb_path_query
+--------------------------
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T12:36:00.11"
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T13:35:00.1"
+ "2017-03-11T00:00:00"
+(5 rows)
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2716,6 +3616,117 @@ select jsonb_path_query_tz(
"2017-03-10"
(4 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T12:35:00"
+ "2017-03-11"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+ jsonb_path_query
+--------------------------------
+ "2017-03-10T12:35:00.12+01:00"
+ "2017-03-10T12:36:00.11+01:00"
+ "2017-03-10T12:35:00.12-02:00"
+ "2017-03-10T12:35:00.12+00:00"
+ "2017-03-11T00:00:00+00:00"
+(5 rows)
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
jsonb_path_query
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index 15fb717..b47b2e9 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -417,6 +417,60 @@ select '$.datetime("datetime template")'::jsonpath;
$.datetime("datetime template")
(1 row)
+select '$.date()'::jsonpath;
+ jsonpath
+----------
+ $.date()
+(1 row)
+
+select '$.time()'::jsonpath;
+ jsonpath
+----------
+ $.time()
+(1 row)
+
+select '$.time(6)'::jsonpath;
+ jsonpath
+-----------
+ $.time(6)
+(1 row)
+
+select '$.time_tz()'::jsonpath;
+ jsonpath
+-------------
+ $.time_tz()
+(1 row)
+
+select '$.time_tz(4)'::jsonpath;
+ jsonpath
+--------------
+ $.time_tz(4)
+(1 row)
+
+select '$.timestamp()'::jsonpath;
+ jsonpath
+---------------
+ $.timestamp()
+(1 row)
+
+select '$.timestamp(2)'::jsonpath;
+ jsonpath
+----------------
+ $.timestamp(2)
+(1 row)
+
+select '$.timestamp_tz()'::jsonpath;
+ jsonpath
+------------------
+ $.timestamp_tz()
+(1 row)
+
+select '$.timestamp_tz(0)'::jsonpath;
+ jsonpath
+-------------------
+ $.timestamp_tz(0)
+(1 row)
+
select '$ ? (@ starts with "abc")'::jsonpath;
jsonpath
-------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 2d4a1e7..bab6c95 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -501,8 +501,138 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+select jsonb_path_query('true', '$.date()');
+select jsonb_path_query('1', '$.date()');
+select jsonb_path_query('[]', '$.date()');
+select jsonb_path_query('[]', 'strict $.date()');
+select jsonb_path_query('{}', '$.date()');
+select jsonb_path_query('"bogus"', '$.date()');
+
+select jsonb '"2023-08-15"' @? '$.date()';
+select jsonb_path_query('"2023-08-15"', '$.date()');
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+select jsonb_path_query('true', '$.time()');
+select jsonb_path_query('1', '$.time()');
+select jsonb_path_query('[]', '$.time()');
+select jsonb_path_query('[]', 'strict $.time()');
+select jsonb_path_query('{}', '$.time()');
+select jsonb_path_query('"bogus"', '$.time()');
+
+select jsonb '"12:34:56"' @? '$.time()';
+select jsonb_path_query('"12:34:56"', '$.time()');
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+select jsonb_path_query('true', '$.time_tz()');
+select jsonb_path_query('1', '$.time_tz()');
+select jsonb_path_query('[]', '$.time_tz()');
+select jsonb_path_query('[]', 'strict $.time_tz()');
+select jsonb_path_query('{}', '$.time_tz()');
+select jsonb_path_query('"bogus"', '$.time_tz()');
+
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+select jsonb_path_query('true', '$.timestamp()');
+select jsonb_path_query('1', '$.timestamp()');
+select jsonb_path_query('[]', '$.timestamp()');
+select jsonb_path_query('[]', 'strict $.timestamp()');
+select jsonb_path_query('{}', '$.timestamp()');
+select jsonb_path_query('"bogus"', '$.timestamp()');
+
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+select jsonb_path_query('true', '$.timestamp_tz()');
+select jsonb_path_query('1', '$.timestamp_tz()');
+select jsonb_path_query('[]', '$.timestamp_tz()');
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+select jsonb_path_query('{}', '$.timestamp_tz()');
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -518,6 +648,12 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -533,6 +669,11 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
select jsonb_path_query('"2017-03-10"', '$.datetime()');
select jsonb_path_query('"2017-03-10 12:34:56"', '$.datetime().type()');
@@ -575,6 +716,34 @@ select jsonb_path_query_tz(
'["2017-03-10", "2017-03-11", "2017-03-09", "12:34:56", "01:02:03+04", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
'$[*].datetime() ? (@ < "10.03.2017".datetime("dd.mm.yyyy"))');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -595,6 +764,38 @@ select jsonb_path_query_tz(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
'$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -615,6 +816,37 @@ select jsonb_path_query_tz(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ < "12:35 +1".datetime("HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -635,6 +867,37 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35".datetime("dd.mm.yyyy HH24:MI"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -655,6 +918,38 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35 +1".datetime("dd.mm.yyyy HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 1f25f89..44275c9 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -75,6 +75,15 @@ select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
+select '$.date()'::jsonpath;
+select '$.time()'::jsonpath;
+select '$.time(6)'::jsonpath;
+select '$.time_tz()'::jsonpath;
+select '$.time_tz(4)'::jsonpath;
+select '$.timestamp()'::jsonpath;
+select '$.timestamp(2)'::jsonpath;
+select '$.timestamp_tz()'::jsonpath;
+select '$.timestamp_tz(0)'::jsonpath;
select '$ ? (@ starts with "abc")'::jsonpath;
select '$ ? (@ starts with $var)'::jsonpath;
--
1.8.3.1
On Mon, Oct 23, 2023 at 3:29 PM Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote:
Attached are all three patches fixing the above comments.
minor issue:
/src/backend/utils/adt/jsonpath_exec.c
2531: Timestamp result;
2532: ErrorSaveContext escontext = {T_ErrorSaveContext};
2533:
2534: /* Get a warning when precision is reduced */
2535: time_precision = anytimestamp_typmod_check(false,
2536: time_precision);
2537: result = DatumGetTimestamp(value);
2538: AdjustTimestampForTypmod(&result, time_precision,
2539: (Node *) &escontext);
2540: if (escontext.error_occurred)
2541: RETURN_ERROR(ereport(ERROR,
2542: (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
2543: errmsg("numeric argument of jsonpath item method .%s() is out
of range for type integer",
2544: jspOperationName(jsp->type)))));
you already did anytimestamp_typmod_check. So this "if
(escontext.error_occurred)" is unnecessary?
A similar case applies to another function called anytimestamp_typmod_check.
/src/backend/utils/adt/jsonpath_exec.c
1493: /* Convert numstr to Numeric with typmod */
1494: Assert(numstr != NULL);
1495: noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
1496: InvalidOid, dtypmod,
1497: (Node *) &escontext,
1498: &numdatum);
1499:
1500: if (!noerr || escontext.error_occurred)
1501: RETURN_ERROR(ereport(ERROR,
1502: (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
1503: errmsg("string argument of jsonpath item method .%s() is not a
valid representation of a decimal or number",
1504: jspOperationName(jsp->type)))));
inside DirectInputFunctionCallSafe already "if (SOFT_ERROR_OCCURRED(escontext))"
so "if (!noerr || escontext.error_occurred)" change to "if (!noerr)"
should be fine?
On 2023-10-19 Th 02:06, Jeevan Chalke wrote:
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut <peter@eisentraut.org>
wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better
maintainability
or readability?Yeah, that's the better suggestion. While implementing these methods,
I was confused about where to put them exactly and tried keeping them
in some logical place.
I think once these methods get in, we can have a follow-up patch
reorganizing all of these.
I think it would be better to organize things how we want them before
adding in more stuff.
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
Hello,
On Tue, Oct 24, 2023 at 6:41 PM Andrew Dunstan <andrew@dunslane.net> wrote:
On 2023-10-19 Th 02:06, Jeevan Chalke wrote:
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut <peter@eisentraut.org>
wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?Yeah, that's the better suggestion. While implementing these methods, I
was confused about where to put them exactly and tried keeping them in some
logical place.
I think once these methods get in, we can have a follow-up patch
reorganizing all of these.I think it would be better to organize things how we want them before
adding in more stuff.
I have tried reordering all the jsonpath Operators and Methods
consistently. With this patch, they all appear in the same order when
together in the group.
In some switch cases, they are still divided, like in
flattenJsonPathParseItem(), where 2-arg, 1-arg, and no-arg cases are
clubbed together. But I have tried to keep them in order in those subgroups.
I will rebase my patches for this task on this patch, but before doing so,
I would like to get your views on this reordering.
Thanks
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
Attachments:
reorder-jsonpath-Operators-Methods.patchapplication/octet-stream; name=reorder-jsonpath-Operators-Methods.patchDownload
commit b5146b327385e50bbb10f509d9d6b59f4b60755c
Author: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Tue Oct 31 20:41:30 2023 +0530
Reorganise jsonpath Operators and Methods
Various jsonpath operators and methods add various keywords,
switch cases, and documentation entries in some order. However,
they are not consistent; reorder them for better maintainability or
readability.
Jeevan Chalke, per suggestion from Peter Eisentraut.
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c76ec52..4f319c1 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17641,43 +17641,43 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Nearest integer greater than or equal to the given number
+ Absolute value of the given number
</para>
<para>
- <literal>jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')</literal>
- <returnvalue>2</returnvalue>
+ <literal>jsonb_path_query('{"z": -0.3}', '$.z.abs()')</literal>
+ <returnvalue>0.3</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>floor()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Nearest integer less than or equal to the given number
+ Nearest integer greater than or equal to the given number
</para>
<para>
- <literal>jsonb_path_query('{"h": 1.7}', '$.h.floor()')</literal>
- <returnvalue>1</returnvalue>
+ <literal>jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')</literal>
+ <returnvalue>2</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>floor()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Absolute value of the given number
+ Nearest integer less than or equal to the given number
</para>
<para>
- <literal>jsonb_path_query('{"z": -0.3}', '$.z.abs()')</literal>
- <returnvalue>0.3</returnvalue>
+ <literal>jsonb_path_query('{"h": 1.7}', '$.h.floor()')</literal>
+ <returnvalue>1</returnvalue>
</para></entry>
</row>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index c5ba3b7..8ff9b56 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -439,10 +439,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiDouble:
case jpiAbs:
- case jpiFloor:
case jpiCeiling:
- case jpiDouble:
+ case jpiFloor:
case jpiKeyValue:
break;
default:
@@ -610,18 +610,6 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
if (printBracketes)
appendStringInfoChar(buf, ')');
break;
- case jpiPlus:
- case jpiMinus:
- if (printBracketes)
- appendStringInfoChar(buf, '(');
- appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
- jspGetArg(v, &elem);
- printJsonPathItem(buf, &elem, false,
- operationPriority(elem.type) <=
- operationPriority(v->type));
- if (printBracketes)
- appendStringInfoChar(buf, ')');
- break;
case jpiFilter:
appendStringInfoString(buf, "?(");
jspGetArg(v, &elem);
@@ -712,23 +700,35 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
v->content.anybounds.first,
v->content.anybounds.last);
break;
+ case jpiPlus:
+ case jpiMinus:
+ if (printBracketes)
+ appendStringInfoChar(buf, '(');
+ appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false,
+ operationPriority(elem.type) <=
+ operationPriority(v->type));
+ if (printBracketes)
+ appendStringInfoChar(buf, ')');
+ break;
case jpiType:
appendStringInfoString(buf, ".type()");
break;
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiDouble:
+ appendStringInfoString(buf, ".double()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
- case jpiFloor:
- appendStringInfoString(buf, ".floor()");
- break;
case jpiCeiling:
appendStringInfoString(buf, ".ceiling()");
break;
- case jpiDouble:
- appendStringInfoString(buf, ".double()");
+ case jpiFloor:
+ appendStringInfoString(buf, ".floor()");
break;
case jpiDatetime:
appendStringInfoString(buf, ".datetime(");
@@ -771,11 +771,11 @@ jspOperationName(JsonPathItemType type)
return "<=";
case jpiGreaterOrEqual:
return ">=";
- case jpiPlus:
case jpiAdd:
+ case jpiPlus:
return "+";
- case jpiMinus:
case jpiSub:
+ case jpiMinus:
return "-";
case jpiMul:
return "*";
@@ -783,26 +783,26 @@ jspOperationName(JsonPathItemType type)
return "/";
case jpiMod:
return "%";
- case jpiStartsWith:
- return "starts with";
- case jpiLikeRegex:
- return "like_regex";
case jpiType:
return "type";
case jpiSize:
return "size";
- case jpiKeyValue:
- return "keyvalue";
case jpiDouble:
return "double";
case jpiAbs:
return "abs";
- case jpiFloor:
- return "floor";
case jpiCeiling:
return "ceiling";
+ case jpiFloor:
+ return "floor";
case jpiDatetime:
return "datetime";
+ case jpiKeyValue:
+ return "keyvalue";
+ case jpiStartsWith:
+ return "starts with";
+ case jpiLikeRegex:
+ return "like_regex";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -893,10 +893,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiDouble:
case jpiAbs:
- case jpiFloor:
case jpiCeiling:
- case jpiDouble:
+ case jpiFloor:
case jpiKeyValue:
case jpiLast:
break;
@@ -935,9 +935,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiNot:
case jpiExists:
case jpiIsUnknown:
+ case jpiFilter:
case jpiPlus:
case jpiMinus:
- case jpiFilter:
case jpiDatetime:
read_int32(v->content.arg, base, pos);
break;
@@ -989,13 +989,6 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiRoot ||
v->type == jpiVariable ||
v->type == jpiLast ||
- v->type == jpiAdd ||
- v->type == jpiSub ||
- v->type == jpiMul ||
- v->type == jpiDiv ||
- v->type == jpiMod ||
- v->type == jpiPlus ||
- v->type == jpiMinus ||
v->type == jpiEqual ||
v->type == jpiNotEqual ||
v->type == jpiGreater ||
@@ -1006,12 +999,19 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiOr ||
v->type == jpiNot ||
v->type == jpiIsUnknown ||
+ v->type == jpiAdd ||
+ v->type == jpiPlus ||
+ v->type == jpiSub ||
+ v->type == jpiMinus ||
+ v->type == jpiMul ||
+ v->type == jpiDiv ||
+ v->type == jpiMod ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiDouble ||
v->type == jpiAbs ||
- v->type == jpiFloor ||
v->type == jpiCeiling ||
- v->type == jpiDouble ||
+ v->type == jpiFloor ||
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 2d0599b..766335e 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -874,33 +874,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
- case jpiAdd:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_add_opt_error, found);
-
- case jpiSub:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_sub_opt_error, found);
-
- case jpiMul:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_mul_opt_error, found);
-
- case jpiDiv:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_div_opt_error, found);
-
- case jpiMod:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_mod_opt_error, found);
-
- case jpiPlus:
- return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
-
- case jpiMinus:
- return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
- found);
-
case jpiFilter:
{
JsonPathBool st;
@@ -980,6 +953,33 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiAdd:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_add_opt_error, found);
+
+ case jpiPlus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
+
+ case jpiSub:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_sub_opt_error, found);
+
+ case jpiMinus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
+ found);
+
+ case jpiMul:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mul_opt_error, found);
+
+ case jpiDiv:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_div_opt_error, found);
+
+ case jpiMod:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mod_opt_error, found);
+
case jpiType:
{
JsonbValue *jbv = palloc(sizeof(*jbv));
@@ -1021,18 +1021,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
- case jpiAbs:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
- found);
-
- case jpiFloor:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
- found);
-
- case jpiCeiling:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
- found);
-
case jpiDouble:
{
JsonbValue jbv;
@@ -1098,6 +1086,18 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiAbs:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
+ found);
+
+ case jpiCeiling:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
+ found);
+
+ case jpiFloor:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
+ found);
+
case jpiDatetime:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index adc259d..4233eed 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
+%token <str> TYPE_P SIZE_P DOUBLE_P ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P
%type <result> result
@@ -206,10 +206,10 @@ accessor_expr:
expr:
accessor_expr { $$ = makeItemList($1); }
| '(' expr ')' { $$ = $2; }
- | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); }
- | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); }
| expr '+' expr { $$ = makeItemBinary(jpiAdd, $1, $3); }
+ | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); }
| expr '-' expr { $$ = makeItemBinary(jpiSub, $1, $3); }
+ | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); }
| expr '*' expr { $$ = makeItemBinary(jpiMul, $1, $3); }
| expr '/' expr { $$ = makeItemBinary(jpiDiv, $1, $3); }
| expr '%' expr { $$ = makeItemBinary(jpiMod, $1, $3); }
@@ -278,28 +278,28 @@ key_name:
| EXISTS_P
| STRICT_P
| LAX_P
- | ABS_P
- | SIZE_P
+ | LAST_P
+ | FLAG_P
| TYPE_P
- | FLOOR_P
+ | SIZE_P
| DOUBLE_P
+ | ABS_P
| CEILING_P
+ | FLOOR_P
| DATETIME_P
| KEYVALUE_P
- | LAST_P
| STARTS_P
| WITH_P
| LIKE_REGEX_P
- | FLAG_P
;
method:
- ABS_P { $$ = jpiAbs; }
+ TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
- | TYPE_P { $$ = jpiType; }
- | FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
+ | ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
+ | FLOOR_P { $$ = jpiFloor; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
%%
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index f0181e0..871d05b 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -66,13 +66,6 @@ typedef enum JsonPathItemType
jpiGreater, /* expr > expr */
jpiLessOrEqual, /* expr <= expr */
jpiGreaterOrEqual, /* expr >= expr */
- jpiAdd, /* expr + expr */
- jpiSub, /* expr - expr */
- jpiMul, /* expr * expr */
- jpiDiv, /* expr / expr */
- jpiMod, /* expr % expr */
- jpiPlus, /* + expr */
- jpiMinus, /* - expr */
jpiAnyArray, /* [*] */
jpiAnyKey, /* .* */
jpiIndexArray, /* [subscript, ...] */
@@ -83,12 +76,19 @@ typedef enum JsonPathItemType
jpiVariable, /* $variable */
jpiFilter, /* ? (predicate) */
jpiExists, /* EXISTS (expr) predicate */
+ jpiAdd, /* expr + expr */
+ jpiPlus, /* + expr */
+ jpiSub, /* expr - expr */
+ jpiMinus, /* - expr */
+ jpiMul, /* expr * expr */
+ jpiDiv, /* expr / expr */
+ jpiMod, /* expr % expr */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiDouble, /* .double() item method */
jpiAbs, /* .abs() item method */
- jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
- jpiDouble, /* .double() item method */
+ jpiFloor, /* .floor() item method */
jpiDatetime, /* .datetime() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
On 2023-11-01 We 03:00, Jeevan Chalke wrote:
Hello,
On Tue, Oct 24, 2023 at 6:41 PM Andrew Dunstan <andrew@dunslane.net>
wrote:On 2023-10-19 Th 02:06, Jeevan Chalke wrote:
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut
<peter@eisentraut.org> wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and
.number()
methods. The JSON string or a numeric value is converted
to the
bigint, int4, and numeric type representation.
A comment that applies to all of these: These add various
keywords,
switch cases, documentation entries in some order. Are we
happy with
that? Should we try to reorder all of that for better
maintainability
or readability?Yeah, that's the better suggestion. While implementing these
methods, I was confused about where to put them exactly and tried
keeping them in some logical place.
I think once these methods get in, we can have a follow-up patch
reorganizing all of these.I think it would be better to organize things how we want them
before adding in more stuff.I have tried reordering all the jsonpath Operators and Methods
consistently. With this patch, they all appear in the same order when
together in the group.In some switch cases, they are still divided, like in
flattenJsonPathParseItem(), where 2-arg, 1-arg, and no-arg cases are
clubbed together. But I have tried to keep them in order in those
subgroups.I will rebase my patches for this task on this patch, but before doing
so, I would like to get your views on this reordering.
This appears to be reasonable. Maybe we need to add a note in one or two
places about maintaining the consistency?
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
On Wed, Nov 1, 2023 at 3:49 PM Andrew Dunstan <andrew@dunslane.net> wrote:
On 2023-11-01 We 03:00, Jeevan Chalke wrote:
Hello,
On Tue, Oct 24, 2023 at 6:41 PM Andrew Dunstan <andrew@dunslane.net>
wrote:On 2023-10-19 Th 02:06, Jeevan Chalke wrote:
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut <peter@eisentraut.org>
wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(), and .number()
methods. The JSON string or a numeric value is converted to the
bigint, int4, and numeric type representation.A comment that applies to all of these: These add various keywords,
switch cases, documentation entries in some order. Are we happy with
that? Should we try to reorder all of that for better maintainability
or readability?Yeah, that's the better suggestion. While implementing these methods, I
was confused about where to put them exactly and tried keeping them in some
logical place.
I think once these methods get in, we can have a follow-up patch
reorganizing all of these.I think it would be better to organize things how we want them before
adding in more stuff.I have tried reordering all the jsonpath Operators and Methods
consistently. With this patch, they all appear in the same order when
together in the group.In some switch cases, they are still divided, like in
flattenJsonPathParseItem(), where 2-arg, 1-arg, and no-arg cases are
clubbed together. But I have tried to keep them in order in those subgroups.I will rebase my patches for this task on this patch, but before doing so,
I would like to get your views on this reordering.This appears to be reasonable. Maybe we need to add a note in one or two
places about maintaining the consistency?
+1
Added a note in jsonpath.h where enums are defined.
I have rebased all three patches over this reordering patch making 4
patches in the set.
Let me know your views on the same.
Thanks
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
Attachments:
v3-0002-Implement-jsonpath-.number-.decimal-precision-sca.patchapplication/octet-stream; name=v3-0002-Implement-jsonpath-.number-.decimal-precision-sca.patchDownload
From d68e5383b6280c68ef9bc010623c63c8e2376ca1 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 6 Nov 2023 18:09:10 +0530
Subject: [PATCH v3 2/4] Implement jsonpath .number(), .decimal([precision [,
scale]]), .bigint(), and .integer() methods
This commit implements jsonpath .number(), .decimal() with optional
precision and scale, .bigint(), and .integer() methods. The JSON
string or a numeric value is converted to the numeric, numeric,
bigint, and int4 type representation respectively. If precision and
scale are provided for .decimal(), then it is converted to the
equivalent numerictypmod and applied to the numeric number.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 56 +++
src/backend/catalog/sql_features.txt | 10 +-
src/backend/utils/adt/jsonpath.c | 46 +++
src/backend/utils/adt/jsonpath_exec.c | 270 ++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 48 ++-
src/backend/utils/adt/jsonpath_scan.l | 4 +
src/include/utils/jsonpath.h | 4 +
src/test/regress/expected/jsonb_jsonpath.out | 510 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 +
src/test/regress/sql/jsonb_jsonpath.sql | 129 +++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 1083 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index f18f05b..ec90c10 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17687,6 +17687,62 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>number()</literal>
+ <returnvalue><replaceable>numeric</replaceable></returnvalue>
+ </para>
+ <para>
+ Numeric value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "123.45"}', '$.len.number()')</literal>
+ <returnvalue>123.45</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>decimal( [ <replaceable>precision</replaceable> [ , <replaceable>scale</replaceable> ] ] )</literal>
+ <returnvalue><replaceable>decimal</replaceable></returnvalue>
+ </para>
+ <para>
+ Rounded decimal value converted from a JSON number or string. <literal>precision</literal> and <literal>scale</literal> must be integer values.
+ </para>
+ <para>
+ <literal>jsonb_path_query('1234.5678', '$.decimal(6, 2)')</literal>
+ <returnvalue>1234.57</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
+ <returnvalue><replaceable>bigint</replaceable></returnvalue>
+ </para>
+ <para>
+ Big integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')</literal>
+ <returnvalue>9876543219</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>integer()</literal>
+ <returnvalue><replaceable>integer</replaceable></returnvalue>
+ </para>
+ <para>
+ Integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "12345"}', '$.len.integer()')</literal>
+ <returnvalue>12345</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 80c40ea..6a76579 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -574,13 +574,13 @@ T861 SQL/JSON simplified accessor: case-sensitive JSON member accessor NO
T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
-T865 SQL/JSON item method: bigint() NO
+T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
T867 SQL/JSON item method: date() NO
-T868 SQL/JSON item method: decimal() NO
-T869 SQL/JSON item method: decimal() with precision and scale NO
-T870 SQL/JSON item method: integer() NO
-T871 SQL/JSON item method: number() NO
+T868 SQL/JSON item method: decimal() YES
+T869 SQL/JSON item method: decimal() with precision and scale YES
+T870 SQL/JSON item method: integer() YES
+T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
T873 SQL/JSON item method: time() NO
T874 SQL/JSON item method: time_tz() NO
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 8ff9b56..41e596f 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -294,6 +294,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMul:
case jpiDiv:
case jpiMod:
+ case jpiDecimal:
case jpiStartsWith:
{
/*
@@ -440,6 +441,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiType:
case jpiSize:
case jpiDouble:
+ case jpiNumber:
+ case jpiBigint:
+ case jpiInteger:
case jpiAbs:
case jpiCeiling:
case jpiFloor:
@@ -721,6 +725,30 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
+ case jpiNumber:
+ appendStringInfoString(buf, ".number()");
+ break;
+ case jpiDecimal:
+ appendStringInfoString(buf, ".decimal(");
+ if (v->content.args.left)
+ {
+ jspGetLeftArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ if (v->content.args.right)
+ {
+ appendStringInfoChar(buf, ',');
+ jspGetRightArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiBigint:
+ appendStringInfoString(buf, ".bigint()");
+ break;
+ case jpiInteger:
+ appendStringInfoString(buf, ".integer()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
@@ -789,6 +817,14 @@ jspOperationName(JsonPathItemType type)
return "size";
case jpiDouble:
return "double";
+ case jpiNumber:
+ return "number";
+ case jpiDecimal:
+ return "decimal";
+ case jpiBigint:
+ return "bigint";
+ case jpiInteger:
+ return "integer";
case jpiAbs:
return "abs";
case jpiCeiling:
@@ -894,6 +930,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiType:
case jpiSize:
case jpiDouble:
+ case jpiNumber:
+ case jpiBigint:
+ case jpiInteger:
case jpiAbs:
case jpiCeiling:
case jpiFloor:
@@ -922,6 +961,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiGreater:
case jpiLessOrEqual:
case jpiGreaterOrEqual:
+ case jpiDecimal:
case jpiStartsWith:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
@@ -1009,6 +1049,10 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiType ||
v->type == jpiSize ||
v->type == jpiDouble ||
+ v->type == jpiNumber ||
+ v->type == jpiDecimal ||
+ v->type == jpiBigint ||
+ v->type == jpiInteger ||
v->type == jpiAbs ||
v->type == jpiCeiling ||
v->type == jpiFloor ||
@@ -1041,6 +1085,7 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.left);
@@ -1062,6 +1107,7 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.right);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 766335e..4f18eba 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1086,6 +1086,276 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiNumber:
+ case jpiDecimal:
+ {
+ JsonbValue jbv;
+ Numeric num;
+ char *numstr = NULL;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ num = jb->val.numeric;
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->type == jpiDecimal)
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as number */
+ Datum datum;
+ bool noerr;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
+
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(datum);
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ /*
+ * If we have arguments, then they must be the precision and
+ * optional scale used in .decimal(). Convert them to the
+ * typmod equivalent and then truncate the numeric value per
+ * this typmod details.
+ */
+ if (jsp->type == jpiDecimal && jsp->content.args.left)
+ {
+ Datum numdatum;
+ Datum dtypmod;
+ int32 precision;
+ int32 scale = 0;
+ bool have_error;
+ bool noerr;
+ ArrayType *arrtypmod;
+ Datum datums[2];
+ char pstr[12]; /* sign, 10 digits and '\0' */
+ char sstr[12]; /* sign, 10 digits and '\0' */
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ jspGetLeftArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() precision");
+
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+ errmsg("precision is out of integer range"))));
+
+ if (jsp->content.args.right)
+ {
+ jspGetRightArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() scale");
+
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_SQL_JSON_SUBSCRIPT),
+ errmsg("scale is out of integer range"))));
+ }
+
+ /*
+ * numerictypmodin() takes the precision and scale in the
+ * form of CString arrays.
+ */
+ pg_ltoa(precision, pstr);
+ datums[0] = CStringGetDatum(pstr);
+ pg_ltoa(scale, sstr);
+ datums[1] = CStringGetDatum(sstr);
+ arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
+
+ dtypmod = DirectFunctionCall1(numerictypmodin,
+ PointerGetDatum(arrtypmod));
+
+ /* Convert numstr to Numeric with typmod */
+ Assert(numstr != NULL);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, dtypmod,
+ (Node *) &escontext,
+ &numdatum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(numdatum);
+ pfree(arrtypmod);
+ }
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = num;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiBigint:
+ {
+ JsonbValue jbv;
+ Datum datum;
+ bool noerr;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as bigint */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiInteger:
+ {
+ JsonbValue jbv;
+ Datum datum;
+ bool noerr;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as integer */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiAbs:
return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
found);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 4233eed..a0484e9 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,9 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> TYPE_P SIZE_P DOUBLE_P ABS_P CEILING_P FLOOR_P KEYVALUE_P
+%token <str> TYPE_P SIZE_P
+%token <str> DOUBLE_P NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
+%token <str> ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P
%type <result> result
@@ -88,9 +90,9 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template
+ datetime_template opt_datetime_template csv_elem
-%type <elems> accessor_expr
+%type <elems> accessor_expr csv_list opt_csv_list
%type <indexs> index_list
@@ -248,11 +250,44 @@ accessor_op:
| array_accessor { $$ = $1; }
| '.' any_path { $$ = $2; }
| '.' method '(' ')' { $$ = makeItemType($2); }
+ | '.' DECIMAL_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiDecimal, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
+ else if (list_length($4) == 2)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".decimal() can only have an optional precision[,scale].")));
+ }
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+csv_elem:
+ INT_P
+ { $$ = makeItemNumeric(&$1); }
+ | '+' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
+ | '-' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
+ ;
+
+csv_list:
+ csv_elem { $$ = list_make1($1); }
+ | csv_list ',' csv_elem { $$ = lappend($1, $3); }
+ ;
+
+opt_csv_list:
+ csv_list { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -283,6 +318,10 @@ key_name:
| TYPE_P
| SIZE_P
| DOUBLE_P
+ | NUMBER_P
+ | DECIMAL_P
+ | BIGINT_P
+ | INTEGER_P
| ABS_P
| CEILING_P
| FLOOR_P
@@ -297,6 +336,9 @@ method:
TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
| DOUBLE_P { $$ = jpiDouble; }
+ | NUMBER_P { $$ = jpiNumber; }
+ | BIGINT_P { $$ = jpiBigint; }
+ | INTEGER_P { $$ = jpiInteger; }
| ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
| FLOOR_P { $$ = jpiFloor; }
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 29c26af..b8922ee 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -410,11 +410,15 @@ static const JsonPathKeyword keywords[] = {
{ 4, false, WITH_P, "with"},
{ 5, true, FALSE_P, "false"},
{ 5, false, FLOOR_P, "floor"},
+ { 6, false, BIGINT_P, "bigint"},
{ 6, false, DOUBLE_P, "double"},
{ 6, false, EXISTS_P, "exists"},
+ { 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, DECIMAL_P, "decimal"},
+ { 7, false, INTEGER_P, "integer"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 59dc233..ddc272a 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -92,6 +92,10 @@ typedef enum JsonPathItemType
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
jpiDouble, /* .double() item method */
+ jpiNumber, /* .number() item method */
+ jpiDecimal, /* .decimal() item method */
+ jpiBigint, /* .bigint() item method */
+ jpiInteger, /* .integer() item method */
jpiAbs, /* .abs() item method */
jpiCeiling, /* .ceiling() item method */
jpiFloor, /* .floor() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 6659bc9..c51a9df 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1517,6 +1517,516 @@ select jsonb_path_query('"-inf"', '$.double()', silent => true);
------------------
(0 rows)
+select jsonb_path_query('null', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.bigint()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"1.23"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('1e1000', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"nan"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"NaN"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"-inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1234567890123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"+123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.bigint() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.integer()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"1.23"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('1e1000', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"nan"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"NaN"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"-inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"12345678901"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"+123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.integer() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.number()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.number()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.number() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('null', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.decimal()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.decimal()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+ jsonb_path_query
+------------------
+ 12345.7
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+ERROR: NUMERIC precision 0 must be between 1 and 1000
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+ERROR: NUMERIC precision 1001 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ 1200
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+ERROR: NUMERIC precision -6 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+ERROR: NUMERIC scale -1001 must be between -1000 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+ERROR: NUMERIC scale 1001 must be between -1000 and 1000
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ -1200
+(1 row)
+
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+ jsonb_path_query
+------------------
+ 0.01
+(1 row)
+
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+ jsonb_path_query
+------------------
+ 0.0012
+(1 row)
+
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+ jsonb_path_query
+------------------
+ 0
+(1 row)
+
select jsonb_path_query('{}', '$.abs()');
ERROR: jsonpath item method .abs() can only be applied to a numeric value
select jsonb_path_query('true', '$.floor()');
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index eeffb38..15fb717 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -387,6 +387,18 @@ select '$.double().floor().ceiling().abs()'::jsonpath;
$.double().floor().ceiling().abs()
(1 row)
+select '$.bigint().integer().number().decimal()'::jsonpath;
+ jsonpath
+-----------------------------------------
+ $.bigint().integer().number().decimal()
+(1 row)
+
+select '$.decimal(4,2)'::jsonpath;
+ jsonpath
+----------------
+ $.decimal(4,2)
+(1 row)
+
select '$.keyvalue().key'::jsonpath;
jsonpath
--------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e0ce509..2d4a1e7 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -320,6 +320,135 @@ select jsonb_path_query('"-inf"', '$.double()');
select jsonb_path_query('"inf"', '$.double()', silent => true);
select jsonb_path_query('"-inf"', '$.double()', silent => true);
+select jsonb_path_query('null', '$.bigint()');
+select jsonb_path_query('true', '$.bigint()');
+select jsonb_path_query('null', '$.bigint()', silent => true);
+select jsonb_path_query('true', '$.bigint()', silent => true);
+select jsonb_path_query('[]', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()');
+select jsonb_path_query('{}', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+select jsonb_path_query('1.23', '$.bigint()');
+select jsonb_path_query('"1.23"', '$.bigint()');
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+select jsonb_path_query('1e1000', '$.bigint()');
+select jsonb_path_query('"nan"', '$.bigint()');
+select jsonb_path_query('"NaN"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()');
+select jsonb_path_query('"-inf"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+select jsonb_path_query('123', '$.bigint()');
+select jsonb_path_query('"123"', '$.bigint()');
+select jsonb_path_query('1234567890123', '$.bigint()');
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+select jsonb_path_query('"+123"', '$.bigint()');
+select jsonb_path_query('-123', '$.bigint()');
+select jsonb_path_query('"-123"', '$.bigint()');
+select jsonb_path_query('123', '$.bigint() * 2');
+
+select jsonb_path_query('null', '$.integer()');
+select jsonb_path_query('true', '$.integer()');
+select jsonb_path_query('null', '$.integer()', silent => true);
+select jsonb_path_query('true', '$.integer()', silent => true);
+select jsonb_path_query('[]', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()');
+select jsonb_path_query('{}', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+select jsonb_path_query('{}', '$.integer()', silent => true);
+select jsonb_path_query('1.23', '$.integer()');
+select jsonb_path_query('"1.23"', '$.integer()');
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+select jsonb_path_query('1e1000', '$.integer()');
+select jsonb_path_query('"nan"', '$.integer()');
+select jsonb_path_query('"NaN"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()');
+select jsonb_path_query('"-inf"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+select jsonb_path_query('123', '$.integer()');
+select jsonb_path_query('"123"', '$.integer()');
+select jsonb_path_query('12345678901', '$.integer()');
+select jsonb_path_query('"12345678901"', '$.integer()');
+select jsonb_path_query('"+123"', '$.integer()');
+select jsonb_path_query('-123', '$.integer()');
+select jsonb_path_query('"-123"', '$.integer()');
+select jsonb_path_query('123', '$.integer() * 2');
+
+select jsonb_path_query('null', '$.number()');
+select jsonb_path_query('true', '$.number()');
+select jsonb_path_query('null', '$.number()', silent => true);
+select jsonb_path_query('true', '$.number()', silent => true);
+select jsonb_path_query('[]', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()');
+select jsonb_path_query('{}', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+select jsonb_path_query('{}', '$.number()', silent => true);
+select jsonb_path_query('1.23', '$.number()');
+select jsonb_path_query('"1.23"', '$.number()');
+select jsonb_path_query('"1.23aaa"', '$.number()');
+select jsonb_path_query('1e1000', '$.number()');
+select jsonb_path_query('"nan"', '$.number()');
+select jsonb_path_query('"NaN"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()');
+select jsonb_path_query('"-inf"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+select jsonb_path_query('123', '$.number()');
+select jsonb_path_query('"123"', '$.number()');
+select jsonb_path_query('12345678901234567890', '$.number()');
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+select jsonb_path_query('"+12.3"', '$.number()');
+select jsonb_path_query('-12.3', '$.number()');
+select jsonb_path_query('"-12.3"', '$.number()');
+select jsonb_path_query('12.3', '$.number() * 2');
+
+select jsonb_path_query('null', '$.decimal()');
+select jsonb_path_query('true', '$.decimal()');
+select jsonb_path_query('null', '$.decimal()', silent => true);
+select jsonb_path_query('true', '$.decimal()', silent => true);
+select jsonb_path_query('[]', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()');
+select jsonb_path_query('{}', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+select jsonb_path_query('1.23', '$.decimal()');
+select jsonb_path_query('"1.23"', '$.decimal()');
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+select jsonb_path_query('1e1000', '$.decimal()');
+select jsonb_path_query('"nan"', '$.decimal()');
+select jsonb_path_query('"NaN"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()');
+select jsonb_path_query('"-inf"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+select jsonb_path_query('123', '$.decimal()');
+select jsonb_path_query('"123"', '$.decimal()');
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+select jsonb_path_query('"+12.3"', '$.decimal()');
+select jsonb_path_query('-12.3', '$.decimal()');
+select jsonb_path_query('"-12.3"', '$.decimal()');
+select jsonb_path_query('12.3', '$.decimal() * 2');
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+
select jsonb_path_query('{}', '$.abs()');
select jsonb_path_query('true', '$.floor()');
select jsonb_path_query('"1.2"', '$.ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 56e0bef..1f25f89 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -70,6 +70,8 @@ select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
+select '$.bigint().integer().number().decimal()'::jsonpath;
+select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
--
1.8.3.1
v3-0001-Reorganise-jsonpath-Operators-and-Methods.patchapplication/octet-stream; name=v3-0001-Reorganise-jsonpath-Operators-and-Methods.patchDownload
From 7ee6ded28aa7873adb976096c6aac2f3fe0c30cd Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 6 Nov 2023 18:09:09 +0530
Subject: [PATCH v3 1/4] Reorganise jsonpath Operators and Methods
Various jsonpath operators and methods add various keywords,
switch cases, and documentation entries in some order. However,
they are not consistent; reorder them for better maintainability or
readability.
Jeevan Chalke, per suggestion from Peter Eisentraut.
---
doc/src/sgml/func.sgml | 24 +++++-----
src/backend/utils/adt/jsonpath.c | 82 +++++++++++++++++------------------
src/backend/utils/adt/jsonpath_exec.c | 78 ++++++++++++++++-----------------
src/backend/utils/adt/jsonpath_gram.y | 22 +++++-----
src/include/utils/jsonpath.h | 25 +++++++----
5 files changed, 119 insertions(+), 112 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index d963f0a..f18f05b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17687,43 +17687,43 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Nearest integer greater than or equal to the given number
+ Absolute value of the given number
</para>
<para>
- <literal>jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')</literal>
- <returnvalue>2</returnvalue>
+ <literal>jsonb_path_query('{"z": -0.3}', '$.z.abs()')</literal>
+ <returnvalue>0.3</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>floor()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Nearest integer less than or equal to the given number
+ Nearest integer greater than or equal to the given number
</para>
<para>
- <literal>jsonb_path_query('{"h": 1.7}', '$.h.floor()')</literal>
- <returnvalue>1</returnvalue>
+ <literal>jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')</literal>
+ <returnvalue>2</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>floor()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Absolute value of the given number
+ Nearest integer less than or equal to the given number
</para>
<para>
- <literal>jsonb_path_query('{"z": -0.3}', '$.z.abs()')</literal>
- <returnvalue>0.3</returnvalue>
+ <literal>jsonb_path_query('{"h": 1.7}', '$.h.floor()')</literal>
+ <returnvalue>1</returnvalue>
</para></entry>
</row>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index c5ba3b7..8ff9b56 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -439,10 +439,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiDouble:
case jpiAbs:
- case jpiFloor:
case jpiCeiling:
- case jpiDouble:
+ case jpiFloor:
case jpiKeyValue:
break;
default:
@@ -610,18 +610,6 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
if (printBracketes)
appendStringInfoChar(buf, ')');
break;
- case jpiPlus:
- case jpiMinus:
- if (printBracketes)
- appendStringInfoChar(buf, '(');
- appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
- jspGetArg(v, &elem);
- printJsonPathItem(buf, &elem, false,
- operationPriority(elem.type) <=
- operationPriority(v->type));
- if (printBracketes)
- appendStringInfoChar(buf, ')');
- break;
case jpiFilter:
appendStringInfoString(buf, "?(");
jspGetArg(v, &elem);
@@ -712,23 +700,35 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
v->content.anybounds.first,
v->content.anybounds.last);
break;
+ case jpiPlus:
+ case jpiMinus:
+ if (printBracketes)
+ appendStringInfoChar(buf, '(');
+ appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false,
+ operationPriority(elem.type) <=
+ operationPriority(v->type));
+ if (printBracketes)
+ appendStringInfoChar(buf, ')');
+ break;
case jpiType:
appendStringInfoString(buf, ".type()");
break;
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiDouble:
+ appendStringInfoString(buf, ".double()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
- case jpiFloor:
- appendStringInfoString(buf, ".floor()");
- break;
case jpiCeiling:
appendStringInfoString(buf, ".ceiling()");
break;
- case jpiDouble:
- appendStringInfoString(buf, ".double()");
+ case jpiFloor:
+ appendStringInfoString(buf, ".floor()");
break;
case jpiDatetime:
appendStringInfoString(buf, ".datetime(");
@@ -771,11 +771,11 @@ jspOperationName(JsonPathItemType type)
return "<=";
case jpiGreaterOrEqual:
return ">=";
- case jpiPlus:
case jpiAdd:
+ case jpiPlus:
return "+";
- case jpiMinus:
case jpiSub:
+ case jpiMinus:
return "-";
case jpiMul:
return "*";
@@ -783,26 +783,26 @@ jspOperationName(JsonPathItemType type)
return "/";
case jpiMod:
return "%";
- case jpiStartsWith:
- return "starts with";
- case jpiLikeRegex:
- return "like_regex";
case jpiType:
return "type";
case jpiSize:
return "size";
- case jpiKeyValue:
- return "keyvalue";
case jpiDouble:
return "double";
case jpiAbs:
return "abs";
- case jpiFloor:
- return "floor";
case jpiCeiling:
return "ceiling";
+ case jpiFloor:
+ return "floor";
case jpiDatetime:
return "datetime";
+ case jpiKeyValue:
+ return "keyvalue";
+ case jpiStartsWith:
+ return "starts with";
+ case jpiLikeRegex:
+ return "like_regex";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -893,10 +893,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiDouble:
case jpiAbs:
- case jpiFloor:
case jpiCeiling:
- case jpiDouble:
+ case jpiFloor:
case jpiKeyValue:
case jpiLast:
break;
@@ -935,9 +935,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiNot:
case jpiExists:
case jpiIsUnknown:
+ case jpiFilter:
case jpiPlus:
case jpiMinus:
- case jpiFilter:
case jpiDatetime:
read_int32(v->content.arg, base, pos);
break;
@@ -989,13 +989,6 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiRoot ||
v->type == jpiVariable ||
v->type == jpiLast ||
- v->type == jpiAdd ||
- v->type == jpiSub ||
- v->type == jpiMul ||
- v->type == jpiDiv ||
- v->type == jpiMod ||
- v->type == jpiPlus ||
- v->type == jpiMinus ||
v->type == jpiEqual ||
v->type == jpiNotEqual ||
v->type == jpiGreater ||
@@ -1006,12 +999,19 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiOr ||
v->type == jpiNot ||
v->type == jpiIsUnknown ||
+ v->type == jpiAdd ||
+ v->type == jpiPlus ||
+ v->type == jpiSub ||
+ v->type == jpiMinus ||
+ v->type == jpiMul ||
+ v->type == jpiDiv ||
+ v->type == jpiMod ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiDouble ||
v->type == jpiAbs ||
- v->type == jpiFloor ||
v->type == jpiCeiling ||
- v->type == jpiDouble ||
+ v->type == jpiFloor ||
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 2d0599b..766335e 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -874,33 +874,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
- case jpiAdd:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_add_opt_error, found);
-
- case jpiSub:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_sub_opt_error, found);
-
- case jpiMul:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_mul_opt_error, found);
-
- case jpiDiv:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_div_opt_error, found);
-
- case jpiMod:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_mod_opt_error, found);
-
- case jpiPlus:
- return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
-
- case jpiMinus:
- return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
- found);
-
case jpiFilter:
{
JsonPathBool st;
@@ -980,6 +953,33 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiAdd:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_add_opt_error, found);
+
+ case jpiPlus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
+
+ case jpiSub:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_sub_opt_error, found);
+
+ case jpiMinus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
+ found);
+
+ case jpiMul:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mul_opt_error, found);
+
+ case jpiDiv:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_div_opt_error, found);
+
+ case jpiMod:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mod_opt_error, found);
+
case jpiType:
{
JsonbValue *jbv = palloc(sizeof(*jbv));
@@ -1021,18 +1021,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
- case jpiAbs:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
- found);
-
- case jpiFloor:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
- found);
-
- case jpiCeiling:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
- found);
-
case jpiDouble:
{
JsonbValue jbv;
@@ -1098,6 +1086,18 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiAbs:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
+ found);
+
+ case jpiCeiling:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
+ found);
+
+ case jpiFloor:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
+ found);
+
case jpiDatetime:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index adc259d..4233eed 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
+%token <str> TYPE_P SIZE_P DOUBLE_P ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P
%type <result> result
@@ -206,10 +206,10 @@ accessor_expr:
expr:
accessor_expr { $$ = makeItemList($1); }
| '(' expr ')' { $$ = $2; }
- | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); }
- | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); }
| expr '+' expr { $$ = makeItemBinary(jpiAdd, $1, $3); }
+ | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); }
| expr '-' expr { $$ = makeItemBinary(jpiSub, $1, $3); }
+ | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); }
| expr '*' expr { $$ = makeItemBinary(jpiMul, $1, $3); }
| expr '/' expr { $$ = makeItemBinary(jpiDiv, $1, $3); }
| expr '%' expr { $$ = makeItemBinary(jpiMod, $1, $3); }
@@ -278,28 +278,28 @@ key_name:
| EXISTS_P
| STRICT_P
| LAX_P
- | ABS_P
- | SIZE_P
+ | LAST_P
+ | FLAG_P
| TYPE_P
- | FLOOR_P
+ | SIZE_P
| DOUBLE_P
+ | ABS_P
| CEILING_P
+ | FLOOR_P
| DATETIME_P
| KEYVALUE_P
- | LAST_P
| STARTS_P
| WITH_P
| LIKE_REGEX_P
- | FLAG_P
;
method:
- ABS_P { $$ = jpiAbs; }
+ TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
- | TYPE_P { $$ = jpiType; }
- | FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
+ | ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
+ | FLOOR_P { $$ = jpiFloor; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
%%
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index f0181e0..59dc233 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -66,13 +66,6 @@ typedef enum JsonPathItemType
jpiGreater, /* expr > expr */
jpiLessOrEqual, /* expr <= expr */
jpiGreaterOrEqual, /* expr >= expr */
- jpiAdd, /* expr + expr */
- jpiSub, /* expr - expr */
- jpiMul, /* expr * expr */
- jpiDiv, /* expr / expr */
- jpiMod, /* expr % expr */
- jpiPlus, /* + expr */
- jpiMinus, /* - expr */
jpiAnyArray, /* [*] */
jpiAnyKey, /* .* */
jpiIndexArray, /* [subscript, ...] */
@@ -83,14 +76,28 @@ typedef enum JsonPathItemType
jpiVariable, /* $variable */
jpiFilter, /* ? (predicate) */
jpiExists, /* EXISTS (expr) predicate */
+
+ /*
+ * For better maintainability or readability, keep the order of the below
+ * jsonpath Operators and Methods at the other places, like in the
+ * documentation, switch() cases, keywords list, etc., too.
+ */
+ jpiAdd, /* expr + expr */
+ jpiPlus, /* + expr */
+ jpiSub, /* expr - expr */
+ jpiMinus, /* - expr */
+ jpiMul, /* expr * expr */
+ jpiDiv, /* expr / expr */
+ jpiMod, /* expr % expr */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiDouble, /* .double() item method */
jpiAbs, /* .abs() item method */
- jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
- jpiDouble, /* .double() item method */
+ jpiFloor, /* .floor() item method */
jpiDatetime, /* .datetime() item method */
jpiKeyValue, /* .keyvalue() item method */
+
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
jpiLast, /* LAST array subscript */
jpiStartsWith, /* STARTS WITH predicate */
--
1.8.3.1
v3-0004-Implement-jsonpath-.boolean-and-.string-methods.patchapplication/octet-stream; name=v3-0004-Implement-jsonpath-.boolean-and-.string-methods.patchDownload
From 4aa731ebe57d1edc04caf332821341fd25e6a24f Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 6 Nov 2023 18:09:10 +0530
Subject: [PATCH v3 4/4] Implement jsonpath .boolean() and .string() methods
This commit implements jsonpath .boolean() and .string() methods.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert string to a bool.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 32 ++++
src/backend/catalog/sql_features.txt | 4 +-
src/backend/utils/adt/jsonpath.c | 16 ++
src/backend/utils/adt/jsonpath_exec.c | 147 +++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 6 +-
src/backend/utils/adt/jsonpath_scan.l | 2 +
src/include/utils/jsonpath.h | 2 +
src/test/regress/expected/jsonb_jsonpath.out | 263 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 ++
src/test/regress/sql/jsonb_jsonpath.sql | 58 ++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 541 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index cbaed1b..c878349 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17672,6 +17672,38 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>boolean()</literal>
+ <returnvalue><replaceable>boolean</replaceable></returnvalue>
+ </para>
+ <para>
+ Boolean value converted from a JSON boolean, number, or string
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')</literal>
+ <returnvalue>[true, true, false]</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>string()</literal>
+ <returnvalue><replaceable>string</replaceable></returnvalue>
+ </para>
+ <para>
+ String value converted from a JSON boolean, number, string, or datetime
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')</literal>
+ <returnvalue>["1.23", "xyz", "false"]</returnvalue>
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.datetime().string()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>double()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index aa80634..4085a2d 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -575,13 +575,13 @@ T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
-T866 SQL/JSON item method: boolean() NO
+T866 SQL/JSON item method: boolean() YES
T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
-T872 SQL/JSON item method: string() NO
+T872 SQL/JSON item method: string() YES
T873 SQL/JSON item method: time() YES
T874 SQL/JSON item method: time_tz() YES
T875 SQL/JSON item method: time precision YES
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 2ebd11c..19cf79d 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -444,6 +444,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiDouble:
case jpiNumber:
case jpiBigint:
@@ -727,6 +729,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiBoolean:
+ appendStringInfoString(buf, ".boolean()");
+ break;
+ case jpiStringFunc:
+ appendStringInfoString(buf, ".string()");
+ break;
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
@@ -859,6 +867,10 @@ jspOperationName(JsonPathItemType type)
return "type";
case jpiSize:
return "size";
+ case jpiBoolean:
+ return "boolean";
+ case jpiStringFunc:
+ return "string";
case jpiDouble:
return "double";
case jpiNumber:
@@ -983,6 +995,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiDouble:
case jpiNumber:
case jpiBigint:
@@ -1111,6 +1125,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMod ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiBoolean ||
+ v->type == jpiStringFunc ||
v->type == jpiDouble ||
v->type == jpiNumber ||
v->type == jpiDecimal ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index deeee76..62571f7 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1021,6 +1021,153 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBoolean:
+ {
+ JsonbValue jbv;
+ bool bval;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvBool)
+ {
+ bval = jb->val.boolean;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvNumeric)
+ {
+ int ival;
+ Datum datum;
+ bool noerr;
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ jspOperationName(jsp->type)))));
+
+ ival = DatumGetInt32(datum);
+ if (ival == 0)
+ bval = false;
+ else
+ bval = true;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as boolean */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+
+ if (!parse_bool(tmp, &bval))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, or numeric value",
+ jspOperationName(jsp->type)))));
+
+ jb = &jbv;
+ jb->type = jbvBool;
+ jb->val.boolean = bval;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiStringFunc:
+ {
+ JsonbValue jbv;
+ char *tmp = NULL;
+
+ switch (JsonbType(jb))
+ {
+ case jbvString:
+ /*
+ * Value is not necessarily null-terminated, so we do
+ * pnstrdup() here.
+ */
+ tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ break;
+ case jbvNumeric:
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ break;
+ case jbvBool:
+ tmp = (jb->val.boolean) ? "true" : "false";
+ break;
+ case jbvDatetime:
+ {
+ switch (jb->val.datetime.typid)
+ {
+ case DATEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(date_out,
+ jb->val.datetime.value));
+ break;
+ case TIMEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(time_out,
+ jb->val.datetime.value));
+ break;
+ case TIMETZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timetz_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamp_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPTZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ jb->val.datetime.value));
+ break;
+ default:
+ elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+ jb->val.datetime.typid);
+ }
+ }
+ break;
+ case jbvNull:
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, numeric, or datetime value",
+ jspOperationName(jsp->type)))));
+ break;
+ }
+
+ res = jperOk;
+
+ jb = &jbv;
+ Assert(tmp != NULL); /* We must have set tmp above */
+ jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.len = strlen(jb->val.string.val);
+ jb->type = jbvString;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiDouble:
{
JsonbValue jbv;
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 7162d37..eee16cc 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> TYPE_P SIZE_P
+%token <str> TYPE_P SIZE_P BOOLEAN_P STRINGFUNC_P
%token <str> DOUBLE_P NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
%token <str> ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
@@ -339,6 +339,8 @@ key_name:
| FLAG_P
| TYPE_P
| SIZE_P
+ | BOOLEAN_P
+ | STRINGFUNC_P
| DOUBLE_P
| NUMBER_P
| DECIMAL_P
@@ -362,6 +364,8 @@ key_name:
method:
TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
+ | BOOLEAN_P { $$ = jpiBoolean; }
+ | STRINGFUNC_P { $$ = jpiStringFunc; }
| DOUBLE_P { $$ = jpiDouble; }
| NUMBER_P { $$ = jpiNumber; }
| BIGINT_P { $$ = jpiBigint; }
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 6a7d72a..d87da14 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -418,6 +418,8 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
+ { 6, false, STRINGFUNC_P, "string"},
+ { 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 5577086..92989c8 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -91,6 +91,8 @@ typedef enum JsonPathItemType
jpiMod, /* expr % expr */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiBoolean, /* .boolean() item method */
+ jpiStringFunc, /* .string() item method */
jpiDouble, /* .double() item method */
jpiNumber, /* .number() item method */
jpiDecimal, /* .decimal() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 0231265..f07eb90 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1348,6 +1348,269 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'l
1
(9 rows)
+select jsonb_path_query('null', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('null', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.boolean()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('{}', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"1.23"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('1e1000', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"nan"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"NaN"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"-inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"100"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('true', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('false', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('0', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('-1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('100', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"1"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"0"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"true"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"false"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"TRUE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"FALSE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"yes"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"NO"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"T"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"f"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"y"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"N"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('true', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('123', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+ jsonb_path_query_array
+------------------------
+ [true, true, false]
+(1 row)
+
+select jsonb_path_query('null', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('null', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('{}', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23aaa"
+(1 row)
+
+select jsonb_path_query('1234', '$.string()');
+ jsonb_path_query
+------------------
+ "1234"
+(1 row)
+
+select jsonb_path_query('true', '$.string()');
+ jsonb_path_query
+------------------
+ "true"
+(1 row)
+
+select jsonb_path_query('1234', '$.string().type()');
+ jsonb_path_query
+------------------
+ "string"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+ jsonb_path_query
+----------------------------
+ "Tue Aug 15 00:04:56 2023"
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+ jsonb_path_query_array
+--------------------------
+ ["1.23", "yes", "false"]
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+ jsonb_path_query_array
+--------------------------------
+ ["string", "string", "string"]
+(1 row)
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
jsonb_path_query
------------------
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index b47b2e9..6b16cda 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -381,6 +381,18 @@ select 'true.type()'::jsonpath;
true.type()
(1 row)
+select '$.boolean()'::jsonpath;
+ jsonpath
+-------------
+ $.boolean()
+(1 row)
+
+select '$.string()'::jsonpath;
+ jsonpath
+------------
+ $.string()
+(1 row)
+
select '$.double().floor().ceiling().abs()'::jsonpath;
jsonpath
------------------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 06a9548..c10fb2b 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -283,6 +283,64 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 's
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => true);
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');
+select jsonb_path_query('null', '$.boolean()');
+select jsonb_path_query('null', '$.boolean()', silent => true);
+select jsonb_path_query('[]', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()');
+select jsonb_path_query('{}', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+select jsonb_path_query('1.23', '$.boolean()');
+select jsonb_path_query('"1.23"', '$.boolean()');
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+select jsonb_path_query('1e1000', '$.boolean()');
+select jsonb_path_query('"nan"', '$.boolean()');
+select jsonb_path_query('"NaN"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()');
+select jsonb_path_query('"-inf"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"100"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean()');
+select jsonb_path_query('false', '$.boolean()');
+select jsonb_path_query('1', '$.boolean()');
+select jsonb_path_query('0', '$.boolean()');
+select jsonb_path_query('-1', '$.boolean()');
+select jsonb_path_query('100', '$.boolean()');
+select jsonb_path_query('"1"', '$.boolean()');
+select jsonb_path_query('"0"', '$.boolean()');
+select jsonb_path_query('"true"', '$.boolean()');
+select jsonb_path_query('"false"', '$.boolean()');
+select jsonb_path_query('"TRUE"', '$.boolean()');
+select jsonb_path_query('"FALSE"', '$.boolean()');
+select jsonb_path_query('"yes"', '$.boolean()');
+select jsonb_path_query('"NO"', '$.boolean()');
+select jsonb_path_query('"T"', '$.boolean()');
+select jsonb_path_query('"f"', '$.boolean()');
+select jsonb_path_query('"y"', '$.boolean()');
+select jsonb_path_query('"N"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean().type()');
+select jsonb_path_query('123', '$.boolean().type()');
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+
+select jsonb_path_query('null', '$.string()');
+select jsonb_path_query('null', '$.string()', silent => true);
+select jsonb_path_query('[]', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()');
+select jsonb_path_query('{}', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+select jsonb_path_query('{}', '$.string()', silent => true);
+select jsonb_path_query('1.23', '$.string()');
+select jsonb_path_query('"1.23"', '$.string()');
+select jsonb_path_query('"1.23aaa"', '$.string()');
+select jsonb_path_query('1234', '$.string()');
+select jsonb_path_query('true', '$.string()');
+select jsonb_path_query('1234', '$.string().type()');
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 44275c9..8f07b71 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -69,6 +69,8 @@ select '(1).type()'::jsonpath;
select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
+select '$.boolean()'::jsonpath;
+select '$.string()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
select '$.bigint().integer().number().decimal()'::jsonpath;
select '$.decimal(4,2)'::jsonpath;
--
1.8.3.1
v3-0003-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchapplication/octet-stream; name=v3-0003-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchDownload
From 96748541a095a74fc72a6e156164bb39b15846f7 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 6 Nov 2023 18:09:10 +0530
Subject: [PATCH v3 3/4] Implement jsonpath .date(), .time(), .time_tz(),
.timestamp(), and .timestamp_tz() methods
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.
The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. Unlike the
.datetime() method, all these methods don't accept format templates
and use ISO DateTime formats instead. However, except the .date()
method, these methods take an optional precision to adjust the
fractional seconds.
Jeevan Chalke
---
doc/src/sgml/func.sgml | 130 ++++
src/backend/catalog/sql_features.txt | 14 +-
src/backend/utils/adt/jsonpath.c | 70 +-
src/backend/utils/adt/jsonpath_exec.c | 301 +++++++-
src/backend/utils/adt/jsonpath_gram.y | 34 +-
src/backend/utils/adt/jsonpath_scan.l | 5 +
src/include/utils/jsonpath.h | 5 +
src/test/regress/expected/jsonb_jsonpath.out | 1053 +++++++++++++++++++++++++-
src/test/regress/expected/jsonpath.out | 54 ++
src/test/regress/sql/jsonb_jsonpath.sql | 295 ++++++++
src/test/regress/sql/jsonpath.sql | 9 +
11 files changed, 1931 insertions(+), 39 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index ec90c10..cbaed1b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17816,6 +17816,136 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>date()</literal>
+ <returnvalue><replaceable>date</replaceable></returnvalue>
+ </para>
+ <para>
+ Date value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.date()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time()</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56"', '$.time()')</literal>
+ <returnvalue>"12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789"', '$.time(2)')</literal>
+ <returnvalue>"12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz()</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')</literal>
+ <returnvalue>"12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)')</literal>
+ <returnvalue>"12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp()</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')</literal>
+ <returnvalue>"2023-08-15T12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string, with
+ fractional seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz()</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')</literal>
+ <returnvalue>"2023-08-15T12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>object</replaceable> <literal>.</literal> <literal>keyvalue()</literal>
<returnvalue><replaceable>array</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 6a76579..aa80634 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -576,18 +576,18 @@ T863 SQL/JSON simplified accessor: single-quoted string literal as member access
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
-T867 SQL/JSON item method: date() NO
+T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
-T873 SQL/JSON item method: time() NO
-T874 SQL/JSON item method: time_tz() NO
-T875 SQL/JSON item method: time precision NO
-T876 SQL/JSON item method: timestamp() NO
-T877 SQL/JSON item method: timestamp_tz() NO
-T878 SQL/JSON item method: timestamp precision NO
+T873 SQL/JSON item method: time() YES
+T874 SQL/JSON item method: time_tz() YES
+T875 SQL/JSON item method: time precision YES
+T876 SQL/JSON item method: timestamp() YES
+T877 SQL/JSON item method: timestamp_tz() YES
+T878 SQL/JSON item method: timestamp precision YES
T879 JSON in equality operations YES with jsonb
T880 JSON in grouping operations YES with jsonb
T881 JSON in ordering operations NO with jsonb, partially supported
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 41e596f..2ebd11c 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -356,6 +356,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMinus:
case jpiExists:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
{
int32 arg = reserveSpaceForItemPointer(buf);
@@ -447,6 +451,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiAbs:
case jpiCeiling:
case jpiFloor:
+ case jpiDate:
case jpiKeyValue:
break;
default:
@@ -767,6 +772,45 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
}
appendStringInfoChar(buf, ')');
break;
+ case jpiDate:
+ appendStringInfoString(buf, ".date()");
+ break;
+ case jpiTime:
+ appendStringInfoString(buf, ".time(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimeTz:
+ appendStringInfoString(buf, ".time_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestamp:
+ appendStringInfoString(buf, ".timestamp(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestampTz:
+ appendStringInfoString(buf, ".timestamp_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
case jpiKeyValue:
appendStringInfoString(buf, ".keyvalue()");
break;
@@ -833,6 +877,16 @@ jspOperationName(JsonPathItemType type)
return "floor";
case jpiDatetime:
return "datetime";
+ case jpiDate:
+ return "date";
+ case jpiTime:
+ return "time";
+ case jpiTimeTz:
+ return "time_tz";
+ case jpiTimestamp:
+ return "timestamp";
+ case jpiTimestampTz:
+ return "timestamp_tz";
case jpiKeyValue:
return "keyvalue";
case jpiStartsWith:
@@ -936,6 +990,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAbs:
case jpiCeiling:
case jpiFloor:
+ case jpiDate:
case jpiKeyValue:
case jpiLast:
break;
@@ -979,6 +1034,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiPlus:
case jpiMinus:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
read_int32(v->content.arg, base, pos);
break;
case jpiIndexArray:
@@ -1004,7 +1063,11 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiExists ||
v->type == jpiPlus ||
v->type == jpiMinus ||
- v->type == jpiDatetime);
+ v->type == jpiDatetime ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
jspInitByBuffer(a, v->base, v->content.arg);
}
@@ -1057,6 +1120,11 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiCeiling ||
v->type == jpiFloor ||
v->type == jpiDatetime ||
+ v->type == jpiDate ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
v->type == jpiLikeRegex);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 4f18eba..deeee76 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1369,6 +1369,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
found);
case jpiDatetime:
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -2041,11 +2046,15 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
/*
- * Implementation of the .datetime() method.
+ * Implementation of the .datetime() and related methods.
*
* 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.
+ *
+ * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
+ * have a format, so ISO format is used. However, except .date(), they all
+ * take an optional time precision.
*/
static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
@@ -2061,6 +2070,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
bool hasNext;
JsonPathExecResult res = jperNotFound;
JsonPathItem elem;
+ int32 time_precision = -1;
if (!(jb = getScalar(jb, jbvString)))
RETURN_ERROR(ereport(ERROR,
@@ -2078,7 +2088,11 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
collid = DEFAULT_COLLATION_OID;
- if (jsp->content.arg)
+ /*
+ * .datetime(template) has an argument, the rest of the methods don't have
+ * an argument. So we handle that separately.
+ */
+ if (jsp->type == jpiDatetime && jsp->content.arg)
{
text *template;
char *template_str;
@@ -2140,6 +2154,30 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
static text *fmt_txt[lengthof(fmt_str)] = {0};
int i;
+ /*
+ * Check for optional precision for methods other than .datetime() and
+ * .date()
+ */
+ if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
+ jsp->content.arg)
+ {
+ bool have_error;
+
+ jspGetArg(jsp, &elem);
+
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for %s argument",
+ jspOperationName(jsp->type));
+
+ time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
/* loop until datetime format fits */
for (i = 0; i < lengthof(fmt_str); i++)
{
@@ -2166,11 +2204,260 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
if (res == jperNotFound)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
- errhint("Use a datetime template argument to specify the input data format."))));
+ {
+ if (jsp->type == jpiDatetime)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("datetime format is not recognized: \"%s\"",
+ text_to_cstring(datetime)),
+ errhint("Use a datetime template argument to specify the input data format."))));
+ else
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("%s format is not recognized: \"%s\"",
+ jspOperationName(jsp->type), text_to_cstring(datetime)))));
+
+ }
+ }
+
+ /*
+ * parse_datetime() processes the entire input string per the template or
+ * ISO format and returns the Datum in best fitted datetime type. So, if
+ * this call is for a specific datatype, then we do the conversion here.
+ * Throw an error for incompatible types.
+ */
+ switch (jsp->type)
+ {
+ case jpiDatetime: /* Nothing to do for DATETIME */
+ break;
+ case jpiDate:
+ {
+ /* Convert result type to date */
+ switch (typid)
+ {
+ case DATEOID: /* Nothing to do for DATE */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("date format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_date,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_date,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = DATEOID;
+ }
+ break;
+ case jpiTime:
+ {
+ /* Convert result type to time without time zone */
+ switch (typid)
+ {
+ case TIMEOID: /* Nothing to do for TIME */
+ break;
+ case DATEOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMETZOID:
+ value = DirectFunctionCall1(timetz_time,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_time,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_time,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeADT result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(false,
+ time_precision);
+ result = DatumGetTimeADT(value);
+ AdjustTimeForTypmod(&result, time_precision);
+ value = TimeADTGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMEOID;
+ }
+ break;
+ case jpiTimeTz:
+ {
+ /* Convert result type to time with time zone */
+ switch (typid)
+ {
+ case TIMETZOID: /* Nothing to do for TIMETZ */
+ break;
+ case DATEOID:
+ case TIMESTAMPOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID:
+ value = DirectFunctionCall1(time_timetz,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timetz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeTzADT *result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(true,
+ time_precision);
+ result = DatumGetTimeTzADTP(value);
+ AdjustTimeForTypmod(&result->time, time_precision);
+ value = TimeTzADTPGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMETZOID;
+ }
+ break;
+ case jpiTimestamp:
+ {
+ /* Convert result type to timestamp without time zone */
+ switch (typid)
+ {
+ case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamp,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timestamp,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(false,
+ time_precision);
+ result = DatumGetTimestamp(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPOID;
+ }
+ break;
+ case jpiTimestampTz:
+ {
+ /* Convert result type to timestamp with time zone */
+ switch (typid)
+ {
+ case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamptz,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_timestamptz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(true,
+ time_precision);
+ result = DatumGetTimestampTz(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampTzGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPTZOID;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
pfree(datetime);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index a0484e9..7162d37 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -83,7 +83,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> TYPE_P SIZE_P
%token <str> DOUBLE_P NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
%token <str> ABS_P CEILING_P FLOOR_P KEYVALUE_P
-%token <str> DATETIME_P
+%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
%type <result> result
@@ -91,6 +91,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
datetime_template opt_datetime_template csv_elem
+ datetime_method datetime_precision opt_datetime_precision
%type <elems> accessor_expr csv_list opt_csv_list
@@ -264,11 +265,23 @@ accessor_op:
errmsg("invalid input syntax for type %s", "jsonpath"),
errdetail(".decimal() can only have an optional precision[,scale].")));
}
- | '.' DATETIME_P '(' opt_datetime_template ')'
- { $$ = makeItemUnary(jpiDatetime, $4); }
+ | '.' datetime_method { $$ = $2; }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+datetime_method:
+ DATETIME_P '(' opt_datetime_template ')'
+ { $$ = makeItemUnary(jpiDatetime, $3); }
+ | TIME_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTime, $3); }
+ | TIME_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimeTz, $3); }
+ | TIMESTAMP_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestamp, $3); }
+ | TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestampTz, $3); }
+ ;
+
csv_elem:
INT_P
{ $$ = makeItemNumeric(&$1); }
@@ -288,6 +301,15 @@ opt_csv_list:
| /* EMPTY */ { $$ = NULL; }
;
+datetime_precision:
+ INT_P { $$ = makeItemNumeric(&$1); }
+ ;
+
+opt_datetime_precision:
+ datetime_precision { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -326,6 +348,11 @@ key_name:
| CEILING_P
| FLOOR_P
| DATETIME_P
+ | DATE_P
+ | TIME_P
+ | TIME_TZ_P
+ | TIMESTAMP_P
+ | TIMESTAMP_TZ_P
| KEYVALUE_P
| STARTS_P
| WITH_P
@@ -342,6 +369,7 @@ method:
| ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
| FLOOR_P { $$ = jpiFloor; }
+ | DATE_P { $$ = jpiDate; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index b8922ee..6a7d72a 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -401,10 +401,12 @@ static const JsonPathKeyword keywords[] = {
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
+ { 4, false, DATE_P, "date"},
{ 4, false, FLAG_P, "flag"},
{ 4, false, LAST_P, "last"},
{ 4, true, NULL_P, "null"},
{ 4, false, SIZE_P, "size"},
+ { 4, false, TIME_P, "time"},
{ 4, true, TRUE_P, "true"},
{ 4, false, TYPE_P, "type"},
{ 4, false, WITH_P, "with"},
@@ -419,10 +421,13 @@ static const JsonPathKeyword keywords[] = {
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
+ { 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
+ { 9, false, TIMESTAMP_P, "timestamp"},
{ 10,false, LIKE_REGEX_P, "like_regex"},
+ { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
/* Check if current scanstring value is a keyword */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index ddc272a..5577086 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -100,6 +100,11 @@ typedef enum JsonPathItemType
jpiCeiling, /* .ceiling() item method */
jpiFloor, /* .floor() item method */
jpiDatetime, /* .datetime() item method */
+ jpiDate, /* .date() item method */
+ jpiTime, /* .time() item method */
+ jpiTimeTz, /* .time_tz() item method */
+ jpiTimestamp, /* .timestamp() item method */
+ jpiTimestampTz, /* .timestamp_tz() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index c51a9df..0231265 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2242,7 +2242,443 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
ERROR: unmatched format character "T"
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
ERROR: unmatched format character "T"
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('true', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('1', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('[]', '$.date()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('{}', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.date()');
+ERROR: date format is not recognized: "bogus"
+select jsonb '"2023-08-15"' @? '$.date()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+ jsonb_path_query
+------------------
+ "date"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+ERROR: date format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+ERROR: date format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ERROR: syntax error at or near "2" of jsonpath input
+LINE 1: select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ ^
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('true', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('1', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('[]', '$.time()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('{}', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time()');
+ERROR: time format is not recognized: "bogus"
+select jsonb '"12:34:56"' @? '$.time()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+ jsonb_path_query
+--------------------------
+ "time without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+ERROR: time format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+ERROR: numeric argument of jsonpath item method .time() is out of range for type integer
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+ jsonb_path_query
+------------------
+ "12:34:57"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+ jsonb_path_query
+------------------
+ "12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+WARNING: TIME(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+WARNING: TIME(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------
+ "12:34:56.789012"
+(1 row)
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('true', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('1', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.time_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "bogus"
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+ jsonb_path_query
+-----------------------
+ "time with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15 12:34:56"
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+ERROR: numeric argument of jsonpath item method .time_tz() is out of range for type integer
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+ jsonb_path_query
+------------------
+ "12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+ jsonb_path_query
+---------------------
+ "12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+WARNING: TIME(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+WARNING: TIME(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------------
+ "12:34:56.789012+05:30"
+(1 row)
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+ jsonb_path_query
+-------------------------------
+ "timestamp without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+ERROR: numeric argument of jsonpath item method .timestamp() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:57"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+ jsonb_path_query
+--------------------------
+ "2023-08-15T12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+WARNING: TIMESTAMP(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+WARNING: TIMESTAMP(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------
+ "2023-08-15T12:34:56.789012"
+(1 row)
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+ jsonb_path_query
+----------------------------
+ "timestamp with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T07:00:00+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+ERROR: numeric argument of jsonpath item method .timestamp_tz() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+ jsonb_path_query
+--------------------------------
+ "2023-08-15T12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+WARNING: TIMESTAMP(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+WARNING: TIMESTAMP(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------------
+ "2023-08-15T12:34:56.789012+05:30"
+(1 row)
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "07:04:56+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+00:00"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2308,6 +2744,36 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "17:04:56+10:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T02:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2373,6 +2839,30 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "00:04:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
jsonb_path_query
------------------
@@ -2529,28 +3019,123 @@ select jsonb_path_query_tz(
"2017-03-10T01:02:03+04:00"
(2 rows)
--- time comparison
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
- jsonb_path_query_tz
----------------------
- "12:35:00"
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-10T00:00:00"
+ "2017-03-10T03:00:00+03:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10T00:00:00"
+ "2017-03-10T12:34:56"
+ "2017-03-10T03:00:00+03:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-09"
+ "2017-03-10T01:02:03+04:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+-- time comparison
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
"12:35:00+00:00"
(2 rows)
@@ -2574,6 +3159,112 @@ select jsonb_path_query_tz(
"13:35:00+01:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+ jsonb_path_query
+------------------
+ "12:35:00.12"
+ "12:36:00.11"
+ "12:35:00.12"
+ "13:35:00.12"
+(4 rows)
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -2620,6 +3311,110 @@ select jsonb_path_query_tz(
"10:35:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00"
+ "12:35:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+ jsonb_path_query
+---------------------
+ "12:35:00.12+01:00"
+ "12:36:00.11+01:00"
+ "12:35:00.12-02:00"
+ "12:35:00.12+00:00"
+ "11:35:00.12+00:00"
+(5 rows)
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2667,6 +3462,111 @@ select jsonb_path_query_tz(
"2017-03-10"
(3 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00+01:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T13:35:00+01:00"
+ "2017-03-10T12:35:00-01:00"
+ "2017-03-11"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+ jsonb_path_query
+--------------------------
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T12:36:00.11"
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T13:35:00.1"
+ "2017-03-11T00:00:00"
+(5 rows)
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2716,6 +3616,117 @@ select jsonb_path_query_tz(
"2017-03-10"
(4 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T12:35:00"
+ "2017-03-11"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+ jsonb_path_query
+--------------------------------
+ "2017-03-10T12:35:00.12+01:00"
+ "2017-03-10T12:36:00.11+01:00"
+ "2017-03-10T12:35:00.12-02:00"
+ "2017-03-10T12:35:00.12+00:00"
+ "2017-03-11T00:00:00+00:00"
+(5 rows)
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
jsonb_path_query
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index 15fb717..b47b2e9 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -417,6 +417,60 @@ select '$.datetime("datetime template")'::jsonpath;
$.datetime("datetime template")
(1 row)
+select '$.date()'::jsonpath;
+ jsonpath
+----------
+ $.date()
+(1 row)
+
+select '$.time()'::jsonpath;
+ jsonpath
+----------
+ $.time()
+(1 row)
+
+select '$.time(6)'::jsonpath;
+ jsonpath
+-----------
+ $.time(6)
+(1 row)
+
+select '$.time_tz()'::jsonpath;
+ jsonpath
+-------------
+ $.time_tz()
+(1 row)
+
+select '$.time_tz(4)'::jsonpath;
+ jsonpath
+--------------
+ $.time_tz(4)
+(1 row)
+
+select '$.timestamp()'::jsonpath;
+ jsonpath
+---------------
+ $.timestamp()
+(1 row)
+
+select '$.timestamp(2)'::jsonpath;
+ jsonpath
+----------------
+ $.timestamp(2)
+(1 row)
+
+select '$.timestamp_tz()'::jsonpath;
+ jsonpath
+------------------
+ $.timestamp_tz()
+(1 row)
+
+select '$.timestamp_tz(0)'::jsonpath;
+ jsonpath
+-------------------
+ $.timestamp_tz(0)
+(1 row)
+
select '$ ? (@ starts with "abc")'::jsonpath;
jsonpath
-------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 2d4a1e7..06a9548 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -501,8 +501,138 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+select jsonb_path_query('true', '$.date()');
+select jsonb_path_query('1', '$.date()');
+select jsonb_path_query('[]', '$.date()');
+select jsonb_path_query('[]', 'strict $.date()');
+select jsonb_path_query('{}', '$.date()');
+select jsonb_path_query('"bogus"', '$.date()');
+
+select jsonb '"2023-08-15"' @? '$.date()';
+select jsonb_path_query('"2023-08-15"', '$.date()');
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+select jsonb_path_query('true', '$.time()');
+select jsonb_path_query('1', '$.time()');
+select jsonb_path_query('[]', '$.time()');
+select jsonb_path_query('[]', 'strict $.time()');
+select jsonb_path_query('{}', '$.time()');
+select jsonb_path_query('"bogus"', '$.time()');
+
+select jsonb '"12:34:56"' @? '$.time()';
+select jsonb_path_query('"12:34:56"', '$.time()');
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+select jsonb_path_query('true', '$.time_tz()');
+select jsonb_path_query('1', '$.time_tz()');
+select jsonb_path_query('[]', '$.time_tz()');
+select jsonb_path_query('[]', 'strict $.time_tz()');
+select jsonb_path_query('{}', '$.time_tz()');
+select jsonb_path_query('"bogus"', '$.time_tz()');
+
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+select jsonb_path_query('true', '$.timestamp()');
+select jsonb_path_query('1', '$.timestamp()');
+select jsonb_path_query('[]', '$.timestamp()');
+select jsonb_path_query('[]', 'strict $.timestamp()');
+select jsonb_path_query('{}', '$.timestamp()');
+select jsonb_path_query('"bogus"', '$.timestamp()');
+
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+select jsonb_path_query('true', '$.timestamp_tz()');
+select jsonb_path_query('1', '$.timestamp_tz()');
+select jsonb_path_query('[]', '$.timestamp_tz()');
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+select jsonb_path_query('{}', '$.timestamp_tz()');
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -518,6 +648,12 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -533,6 +669,11 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
select jsonb_path_query('"2017-03-10"', '$.datetime()');
select jsonb_path_query('"2017-03-10 12:34:56"', '$.datetime().type()');
@@ -575,6 +716,34 @@ select jsonb_path_query_tz(
'["2017-03-10", "2017-03-11", "2017-03-09", "12:34:56", "01:02:03+04", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
'$[*].datetime() ? (@ < "10.03.2017".datetime("dd.mm.yyyy"))');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -595,6 +764,38 @@ select jsonb_path_query_tz(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
'$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -615,6 +816,37 @@ select jsonb_path_query_tz(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ < "12:35 +1".datetime("HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -635,6 +867,37 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35".datetime("dd.mm.yyyy HH24:MI"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -655,6 +918,38 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35 +1".datetime("dd.mm.yyyy HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 1f25f89..44275c9 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -75,6 +75,15 @@ select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
+select '$.date()'::jsonpath;
+select '$.time()'::jsonpath;
+select '$.time(6)'::jsonpath;
+select '$.time_tz()'::jsonpath;
+select '$.time_tz(4)'::jsonpath;
+select '$.timestamp()'::jsonpath;
+select '$.timestamp(2)'::jsonpath;
+select '$.timestamp_tz()'::jsonpath;
+select '$.timestamp_tz(0)'::jsonpath;
select '$ ? (@ starts with "abc")'::jsonpath;
select '$ ? (@ starts with $var)'::jsonpath;
--
1.8.3.1
On 2023-11-06 Mo 08:23, Jeevan Chalke wrote:
On Wed, Nov 1, 2023 at 3:49 PM Andrew Dunstan <andrew@dunslane.net> wrote:
On 2023-11-01 We 03:00, Jeevan Chalke wrote:
Hello,
On Tue, Oct 24, 2023 at 6:41 PM Andrew Dunstan
<andrew@dunslane.net> wrote:On 2023-10-19 Th 02:06, Jeevan Chalke wrote:
Thanks, Peter for the comments.
On Fri, Oct 6, 2023 at 5:13 PM Peter Eisentraut
<peter@eisentraut.org> wrote:On 29.08.23 09:05, Jeevan Chalke wrote:
v1-0001-Implement-jsonpath-.bigint-.integer-and-.number-m.patch
This commit implements jsonpath .bigint(), .integer(),
and .number()
methods. The JSON string or a numeric value is
converted to the
bigint, int4, and numeric type representation.
A comment that applies to all of these: These add
various keywords,
switch cases, documentation entries in some order. Are
we happy with
that? Should we try to reorder all of that for better
maintainability
or readability?Yeah, that's the better suggestion. While implementing these
methods, I was confused about where to put them exactly and
tried keeping them in some logical place.
I think once these methods get in, we can have a follow-up
patch reorganizing all of these.I think it would be better to organize things how we want
them before adding in more stuff.I have tried reordering all the jsonpath Operators and Methods
consistently. With this patch, they all appear in the same order
when together in the group.In some switch cases, they are still divided, like in
flattenJsonPathParseItem(), where 2-arg, 1-arg, and no-arg cases
are clubbed together. But I have tried to keep them in order in
those subgroups.I will rebase my patches for this task on this patch, but before
doing so, I would like to get your views on this reordering.This appears to be reasonable. Maybe we need to add a note in one
or two places about maintaining the consistency?+1
Added a note in jsonpath.h where enums are defined.I have rebased all three patches over this reordering patch making 4
patches in the set.Let me know your views on the same.
Thanks
Hi Jeevan,
I think these are in reasonably good shape, but there are a few things
that concern me:
andrew@~=# select jsonb_path_query_array('[1.2]', '$[*].bigint()');
ERROR: numeric argument of jsonpath item method .bigint() is out of
range for type bigint
I'm ok with this being an error, but I think the error message is wrong.
It should be the "invalid input" message.
andrew@~=# select jsonb_path_query_array('[1.0]', '$[*].bigint()');
ERROR: numeric argument of jsonpath item method .bigint() is out of
range for type bigint
Should we trim trailing dot+zeros from numeric values before trying to
convert to bigint/int? If not, this too should be an "invalid input" case.
andrew@~=# select jsonb_path_query_array('[1.0]', '$[*].boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out of
range for type boolean
It seems odd that any non-zero integer is true but not any non-zero
numeric. Is that in the spec? If not I'd avoid trying to convert it to
an integer first, and just check for infinity/nan before looking to see
if it's zero.
The code for integer() and bigint() seems a bit duplicative, but I'm not
sure there's a clean way of avoiding that.
The items for datetime types and string look OK.
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
On Sun, Dec 3, 2023 at 9:44 PM Andrew Dunstan <andrew@dunslane.net> wrote:
Hi Jeevan,
I think these are in reasonably good shape, but there are a few things
that concern me:andrew@~=# select jsonb_path_query_array('[1.2]', '$[*].bigint()');
ERROR: numeric argument of jsonpath item method .bigint() is out of range
for type bigintI'm ok with this being an error, but I think the error message is wrong.
It should be the "invalid input" message.andrew@~=# select jsonb_path_query_array('[1.0]', '$[*].bigint()');
ERROR: numeric argument of jsonpath item method .bigint() is out of range
for type bigintShould we trim trailing dot+zeros from numeric values before trying to
convert to bigint/int? If not, this too should be an "invalid input" case.
We have the same issue with integer conversion and need a fix.
Unfortunately, I was using int8in() for the conversion of numeric values.
We should be using numeric_int8() instead. However, there is no opt_error
version of the same.
So, I have introduced a numeric_int8_opt_error() version just like we have
one for int4, i.e. numeric_int4_opt_error(), to suppress the error. These
changes are in the 0001 patch. (All other patch numbers are now increased
by 1)
I have used this new function to fix this reported issue and used
numeric_int4_opt_error() for integer conversion.
andrew@~=# select jsonb_path_query_array('[1.0]', '$[*].boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out of
range for type booleanIt seems odd that any non-zero integer is true but not any non-zero
numeric. Is that in the spec? If not I'd avoid trying to convert it to an
integer first, and just check for infinity/nan before looking to see if
it's zero.
PostgreSQL doesn’t cast a numeric to boolean. So maybe we should keep this
behavior as is.
# select 1.0::boolean;
ERROR: cannot cast type numeric to boolean
LINE 1: select 1.0::boolean;
The code for integer() and bigint() seems a bit duplicative, but I'm not
sure there's a clean way of avoiding that.The items for datetime types and string look OK.
Thanks.
Suggestions?
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
--
Jeevan Chalke
*Senior Staff SDE, Database Architect, and ManagerProduct Development*
edbpostgres.com
Attachments:
v4-0001-Add-numeric_int8_opt_error-to-optionally-suppress.patchapplication/octet-stream; name=v4-0001-Add-numeric_int8_opt_error-to-optionally-suppress.patchDownload
From d9cde96423f1f7dcb5c15eb51b352c47cca3cd7d Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Thu, 7 Dec 2023 18:21:12 +0530
Subject: [PATCH v4 1/5] Add numeric_int8_opt_error() to optionally suppress
errors.
Jsonpath methods report errors in their way and thus want the
internal functions to not throw any error. So, add this version for
numeric_int8() conversion. We already did such error suppression in
commit 16d489b0fe058e527619f5e9d92fd7ca3c6c2994.
Jeevan Chalke
---
src/backend/utils/adt/numeric.c | 59 ++++++++++++++++++++++++++++++-----------
src/include/utils/numeric.h | 1 +
2 files changed, 44 insertions(+), 16 deletions(-)
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index bf61fd7..2a29690 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -4430,35 +4430,62 @@ int8_numeric(PG_FUNCTION_ARGS)
PG_RETURN_NUMERIC(int64_to_numeric(val));
}
-
-Datum
-numeric_int8(PG_FUNCTION_ARGS)
+int64
+numeric_int8_opt_error(Numeric num, bool *have_error)
{
- Numeric num = PG_GETARG_NUMERIC(0);
NumericVar x;
int64 result;
+ if (have_error)
+ *have_error = false;
+
if (NUMERIC_IS_SPECIAL(num))
{
- if (NUMERIC_IS_NAN(num))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert NaN to %s", "bigint")));
+ if (have_error)
+ {
+ *have_error = true;
+ return 0;
+ }
else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot convert infinity to %s", "bigint")));
+ {
+ if (NUMERIC_IS_NAN(num))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert NaN to %s", "bigint")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot convert infinity to %s", "bigint")));
+ }
}
- /* Convert to variable format and thence to int8 */
+ /* Convert to variable format, then convert to int8 */
init_var_from_num(num, &x);
if (!numericvar_to_int64(&x, &result))
- ereport(ERROR,
- (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
- errmsg("bigint out of range")));
+ {
+ if (have_error)
+ {
+ *have_error = true;
+ return 0;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
+ errmsg("bigint out of range")));
+ }
+ }
+
+ return result;
+}
+
+Datum
+numeric_int8(PG_FUNCTION_ARGS)
+{
+ Numeric num = PG_GETARG_NUMERIC(0);
- PG_RETURN_INT64(result);
+ PG_RETURN_INT64(numeric_int8_opt_error(num, NULL));
}
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 08e4f8c..0780b52 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -101,5 +101,6 @@ extern Numeric numeric_div_opt_error(Numeric num1, Numeric num2,
extern Numeric numeric_mod_opt_error(Numeric num1, Numeric num2,
bool *have_error);
extern int32 numeric_int4_opt_error(Numeric num, bool *have_error);
+extern int64 numeric_int8_opt_error(Numeric num, bool *have_error);
#endif /* _PG_NUMERIC_H_ */
--
1.8.3.1
v4-0002-Reorganise-jsonpath-Operators-and-Methods.patchapplication/octet-stream; name=v4-0002-Reorganise-jsonpath-Operators-and-Methods.patchDownload
From 91885a41c586f2f5aeaf9f740d7005c4096577a9 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Thu, 7 Dec 2023 18:21:12 +0530
Subject: [PATCH v4 2/5] Reorganise jsonpath Operators and Methods
Various jsonpath operators and methods add various keywords,
switch cases, and documentation entries in some order. However,
they are not consistent; reorder them for better maintainability or
readability.
Jeevan Chalke, per suggestion from Peter Eisentraut.
---
doc/src/sgml/func.sgml | 24 +++++-----
src/backend/utils/adt/jsonpath.c | 82 +++++++++++++++++------------------
src/backend/utils/adt/jsonpath_exec.c | 78 ++++++++++++++++-----------------
src/backend/utils/adt/jsonpath_gram.y | 22 +++++-----
src/include/utils/jsonpath.h | 25 +++++++----
5 files changed, 119 insertions(+), 112 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 20da3ed..96fff2c 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17685,43 +17685,43 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Nearest integer greater than or equal to the given number
+ Absolute value of the given number
</para>
<para>
- <literal>jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')</literal>
- <returnvalue>2</returnvalue>
+ <literal>jsonb_path_query('{"z": -0.3}', '$.z.abs()')</literal>
+ <returnvalue>0.3</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>floor()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>ceiling()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Nearest integer less than or equal to the given number
+ Nearest integer greater than or equal to the given number
</para>
<para>
- <literal>jsonb_path_query('{"h": 1.7}', '$.h.floor()')</literal>
- <returnvalue>1</returnvalue>
+ <literal>jsonb_path_query('{"h": 1.3}', '$.h.ceiling()')</literal>
+ <returnvalue>2</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
- <replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
+ <replaceable>number</replaceable> <literal>.</literal> <literal>floor()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
<para>
- Absolute value of the given number
+ Nearest integer less than or equal to the given number
</para>
<para>
- <literal>jsonb_path_query('{"z": -0.3}', '$.z.abs()')</literal>
- <returnvalue>0.3</returnvalue>
+ <literal>jsonb_path_query('{"h": 1.7}', '$.h.floor()')</literal>
+ <returnvalue>1</returnvalue>
</para></entry>
</row>
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index c5ba3b7..8ff9b56 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -439,10 +439,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiDouble:
case jpiAbs:
- case jpiFloor:
case jpiCeiling:
- case jpiDouble:
+ case jpiFloor:
case jpiKeyValue:
break;
default:
@@ -610,18 +610,6 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
if (printBracketes)
appendStringInfoChar(buf, ')');
break;
- case jpiPlus:
- case jpiMinus:
- if (printBracketes)
- appendStringInfoChar(buf, '(');
- appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
- jspGetArg(v, &elem);
- printJsonPathItem(buf, &elem, false,
- operationPriority(elem.type) <=
- operationPriority(v->type));
- if (printBracketes)
- appendStringInfoChar(buf, ')');
- break;
case jpiFilter:
appendStringInfoString(buf, "?(");
jspGetArg(v, &elem);
@@ -712,23 +700,35 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
v->content.anybounds.first,
v->content.anybounds.last);
break;
+ case jpiPlus:
+ case jpiMinus:
+ if (printBracketes)
+ appendStringInfoChar(buf, '(');
+ appendStringInfoChar(buf, v->type == jpiPlus ? '+' : '-');
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false,
+ operationPriority(elem.type) <=
+ operationPriority(v->type));
+ if (printBracketes)
+ appendStringInfoChar(buf, ')');
+ break;
case jpiType:
appendStringInfoString(buf, ".type()");
break;
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiDouble:
+ appendStringInfoString(buf, ".double()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
- case jpiFloor:
- appendStringInfoString(buf, ".floor()");
- break;
case jpiCeiling:
appendStringInfoString(buf, ".ceiling()");
break;
- case jpiDouble:
- appendStringInfoString(buf, ".double()");
+ case jpiFloor:
+ appendStringInfoString(buf, ".floor()");
break;
case jpiDatetime:
appendStringInfoString(buf, ".datetime(");
@@ -771,11 +771,11 @@ jspOperationName(JsonPathItemType type)
return "<=";
case jpiGreaterOrEqual:
return ">=";
- case jpiPlus:
case jpiAdd:
+ case jpiPlus:
return "+";
- case jpiMinus:
case jpiSub:
+ case jpiMinus:
return "-";
case jpiMul:
return "*";
@@ -783,26 +783,26 @@ jspOperationName(JsonPathItemType type)
return "/";
case jpiMod:
return "%";
- case jpiStartsWith:
- return "starts with";
- case jpiLikeRegex:
- return "like_regex";
case jpiType:
return "type";
case jpiSize:
return "size";
- case jpiKeyValue:
- return "keyvalue";
case jpiDouble:
return "double";
case jpiAbs:
return "abs";
- case jpiFloor:
- return "floor";
case jpiCeiling:
return "ceiling";
+ case jpiFloor:
+ return "floor";
case jpiDatetime:
return "datetime";
+ case jpiKeyValue:
+ return "keyvalue";
+ case jpiStartsWith:
+ return "starts with";
+ case jpiLikeRegex:
+ return "like_regex";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -893,10 +893,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiDouble:
case jpiAbs:
- case jpiFloor:
case jpiCeiling:
- case jpiDouble:
+ case jpiFloor:
case jpiKeyValue:
case jpiLast:
break;
@@ -935,9 +935,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiNot:
case jpiExists:
case jpiIsUnknown:
+ case jpiFilter:
case jpiPlus:
case jpiMinus:
- case jpiFilter:
case jpiDatetime:
read_int32(v->content.arg, base, pos);
break;
@@ -989,13 +989,6 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiRoot ||
v->type == jpiVariable ||
v->type == jpiLast ||
- v->type == jpiAdd ||
- v->type == jpiSub ||
- v->type == jpiMul ||
- v->type == jpiDiv ||
- v->type == jpiMod ||
- v->type == jpiPlus ||
- v->type == jpiMinus ||
v->type == jpiEqual ||
v->type == jpiNotEqual ||
v->type == jpiGreater ||
@@ -1006,12 +999,19 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiOr ||
v->type == jpiNot ||
v->type == jpiIsUnknown ||
+ v->type == jpiAdd ||
+ v->type == jpiPlus ||
+ v->type == jpiSub ||
+ v->type == jpiMinus ||
+ v->type == jpiMul ||
+ v->type == jpiDiv ||
+ v->type == jpiMod ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiDouble ||
v->type == jpiAbs ||
- v->type == jpiFloor ||
v->type == jpiCeiling ||
- v->type == jpiDouble ||
+ v->type == jpiFloor ||
v->type == jpiDatetime ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 2d0599b..766335e 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -874,33 +874,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
- case jpiAdd:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_add_opt_error, found);
-
- case jpiSub:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_sub_opt_error, found);
-
- case jpiMul:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_mul_opt_error, found);
-
- case jpiDiv:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_div_opt_error, found);
-
- case jpiMod:
- return executeBinaryArithmExpr(cxt, jsp, jb,
- numeric_mod_opt_error, found);
-
- case jpiPlus:
- return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
-
- case jpiMinus:
- return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
- found);
-
case jpiFilter:
{
JsonPathBool st;
@@ -980,6 +953,33 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiAdd:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_add_opt_error, found);
+
+ case jpiPlus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, NULL, found);
+
+ case jpiSub:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_sub_opt_error, found);
+
+ case jpiMinus:
+ return executeUnaryArithmExpr(cxt, jsp, jb, numeric_uminus,
+ found);
+
+ case jpiMul:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mul_opt_error, found);
+
+ case jpiDiv:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_div_opt_error, found);
+
+ case jpiMod:
+ return executeBinaryArithmExpr(cxt, jsp, jb,
+ numeric_mod_opt_error, found);
+
case jpiType:
{
JsonbValue *jbv = palloc(sizeof(*jbv));
@@ -1021,18 +1021,6 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
- case jpiAbs:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
- found);
-
- case jpiFloor:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
- found);
-
- case jpiCeiling:
- return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
- found);
-
case jpiDouble:
{
JsonbValue jbv;
@@ -1098,6 +1086,18 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiAbs:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
+ found);
+
+ case jpiCeiling:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_ceil,
+ found);
+
+ case jpiFloor:
+ return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_floor,
+ found);
+
case jpiDatetime:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index adc259d..4233eed 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
+%token <str> TYPE_P SIZE_P DOUBLE_P ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P
%type <result> result
@@ -206,10 +206,10 @@ accessor_expr:
expr:
accessor_expr { $$ = makeItemList($1); }
| '(' expr ')' { $$ = $2; }
- | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); }
- | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); }
| expr '+' expr { $$ = makeItemBinary(jpiAdd, $1, $3); }
+ | '+' expr %prec UMINUS { $$ = makeItemUnary(jpiPlus, $2); }
| expr '-' expr { $$ = makeItemBinary(jpiSub, $1, $3); }
+ | '-' expr %prec UMINUS { $$ = makeItemUnary(jpiMinus, $2); }
| expr '*' expr { $$ = makeItemBinary(jpiMul, $1, $3); }
| expr '/' expr { $$ = makeItemBinary(jpiDiv, $1, $3); }
| expr '%' expr { $$ = makeItemBinary(jpiMod, $1, $3); }
@@ -278,28 +278,28 @@ key_name:
| EXISTS_P
| STRICT_P
| LAX_P
- | ABS_P
- | SIZE_P
+ | LAST_P
+ | FLAG_P
| TYPE_P
- | FLOOR_P
+ | SIZE_P
| DOUBLE_P
+ | ABS_P
| CEILING_P
+ | FLOOR_P
| DATETIME_P
| KEYVALUE_P
- | LAST_P
| STARTS_P
| WITH_P
| LIKE_REGEX_P
- | FLAG_P
;
method:
- ABS_P { $$ = jpiAbs; }
+ TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
- | TYPE_P { $$ = jpiType; }
- | FLOOR_P { $$ = jpiFloor; }
| DOUBLE_P { $$ = jpiDouble; }
+ | ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
+ | FLOOR_P { $$ = jpiFloor; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
%%
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index f0181e0..59dc233 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -66,13 +66,6 @@ typedef enum JsonPathItemType
jpiGreater, /* expr > expr */
jpiLessOrEqual, /* expr <= expr */
jpiGreaterOrEqual, /* expr >= expr */
- jpiAdd, /* expr + expr */
- jpiSub, /* expr - expr */
- jpiMul, /* expr * expr */
- jpiDiv, /* expr / expr */
- jpiMod, /* expr % expr */
- jpiPlus, /* + expr */
- jpiMinus, /* - expr */
jpiAnyArray, /* [*] */
jpiAnyKey, /* .* */
jpiIndexArray, /* [subscript, ...] */
@@ -83,14 +76,28 @@ typedef enum JsonPathItemType
jpiVariable, /* $variable */
jpiFilter, /* ? (predicate) */
jpiExists, /* EXISTS (expr) predicate */
+
+ /*
+ * For better maintainability or readability, keep the order of the below
+ * jsonpath Operators and Methods at the other places, like in the
+ * documentation, switch() cases, keywords list, etc., too.
+ */
+ jpiAdd, /* expr + expr */
+ jpiPlus, /* + expr */
+ jpiSub, /* expr - expr */
+ jpiMinus, /* - expr */
+ jpiMul, /* expr * expr */
+ jpiDiv, /* expr / expr */
+ jpiMod, /* expr % expr */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiDouble, /* .double() item method */
jpiAbs, /* .abs() item method */
- jpiFloor, /* .floor() item method */
jpiCeiling, /* .ceiling() item method */
- jpiDouble, /* .double() item method */
+ jpiFloor, /* .floor() item method */
jpiDatetime, /* .datetime() item method */
jpiKeyValue, /* .keyvalue() item method */
+
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
jpiLast, /* LAST array subscript */
jpiStartsWith, /* STARTS WITH predicate */
--
1.8.3.1
v4-0005-Implement-jsonpath-.boolean-and-.string-methods.patchapplication/octet-stream; name=v4-0005-Implement-jsonpath-.boolean-and-.string-methods.patchDownload
From 765f4689cfffba91ec84fe695ea1db2e24ac69e1 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Thu, 7 Dec 2023 18:21:12 +0530
Subject: [PATCH v4 5/5] Implement jsonpath .boolean() and .string() methods
This commit implements jsonpath .boolean() and .string() methods.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert string to a bool.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 32 ++++
src/backend/catalog/sql_features.txt | 4 +-
src/backend/utils/adt/jsonpath.c | 16 ++
src/backend/utils/adt/jsonpath_exec.c | 147 +++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 6 +-
src/backend/utils/adt/jsonpath_scan.l | 2 +
src/include/utils/jsonpath.h | 2 +
src/test/regress/expected/jsonb_jsonpath.out | 263 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 ++
src/test/regress/sql/jsonb_jsonpath.sql | 58 ++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 541 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 8991b45..618d59b 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17670,6 +17670,38 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>boolean()</literal>
+ <returnvalue><replaceable>boolean</replaceable></returnvalue>
+ </para>
+ <para>
+ Boolean value converted from a JSON boolean, number, or string
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')</literal>
+ <returnvalue>[true, true, false]</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>string()</literal>
+ <returnvalue><replaceable>string</replaceable></returnvalue>
+ </para>
+ <para>
+ String value converted from a JSON boolean, number, string, or datetime
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')</literal>
+ <returnvalue>["1.23", "xyz", "false"]</returnvalue>
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.datetime().string()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>double()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index aa80634..4085a2d 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -575,13 +575,13 @@ T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
-T866 SQL/JSON item method: boolean() NO
+T866 SQL/JSON item method: boolean() YES
T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
-T872 SQL/JSON item method: string() NO
+T872 SQL/JSON item method: string() YES
T873 SQL/JSON item method: time() YES
T874 SQL/JSON item method: time_tz() YES
T875 SQL/JSON item method: time precision YES
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 2ebd11c..19cf79d 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -444,6 +444,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
break;
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiDouble:
case jpiNumber:
case jpiBigint:
@@ -727,6 +729,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiSize:
appendStringInfoString(buf, ".size()");
break;
+ case jpiBoolean:
+ appendStringInfoString(buf, ".boolean()");
+ break;
+ case jpiStringFunc:
+ appendStringInfoString(buf, ".string()");
+ break;
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
@@ -859,6 +867,10 @@ jspOperationName(JsonPathItemType type)
return "type";
case jpiSize:
return "size";
+ case jpiBoolean:
+ return "boolean";
+ case jpiStringFunc:
+ return "string";
case jpiDouble:
return "double";
case jpiNumber:
@@ -983,6 +995,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAnyKey:
case jpiType:
case jpiSize:
+ case jpiBoolean:
+ case jpiStringFunc:
case jpiDouble:
case jpiNumber:
case jpiBigint:
@@ -1111,6 +1125,8 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMod ||
v->type == jpiType ||
v->type == jpiSize ||
+ v->type == jpiBoolean ||
+ v->type == jpiStringFunc ||
v->type == jpiDouble ||
v->type == jpiNumber ||
v->type == jpiDecimal ||
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 1e090e1..42f344d 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1021,6 +1021,153 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBoolean:
+ {
+ JsonbValue jbv;
+ bool bval;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvBool)
+ {
+ bval = jb->val.boolean;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvNumeric)
+ {
+ int ival;
+ Datum datum;
+ bool noerr;
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ jspOperationName(jsp->type)))));
+
+ ival = DatumGetInt32(datum);
+ if (ival == 0)
+ bval = false;
+ else
+ bval = true;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as boolean */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+
+ if (!parse_bool(tmp, &bval))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, or numeric value",
+ jspOperationName(jsp->type)))));
+
+ jb = &jbv;
+ jb->type = jbvBool;
+ jb->val.boolean = bval;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiStringFunc:
+ {
+ JsonbValue jbv;
+ char *tmp = NULL;
+
+ switch (JsonbType(jb))
+ {
+ case jbvString:
+ /*
+ * Value is not necessarily null-terminated, so we do
+ * pnstrdup() here.
+ */
+ tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ break;
+ case jbvNumeric:
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ break;
+ case jbvBool:
+ tmp = (jb->val.boolean) ? "true" : "false";
+ break;
+ case jbvDatetime:
+ {
+ switch (jb->val.datetime.typid)
+ {
+ case DATEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(date_out,
+ jb->val.datetime.value));
+ break;
+ case TIMEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(time_out,
+ jb->val.datetime.value));
+ break;
+ case TIMETZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timetz_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamp_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPTZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ jb->val.datetime.value));
+ break;
+ default:
+ elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+ jb->val.datetime.typid);
+ }
+ }
+ break;
+ case jbvNull:
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, numeric, or datetime value",
+ jspOperationName(jsp->type)))));
+ break;
+ }
+
+ res = jperOk;
+
+ jb = &jbv;
+ Assert(tmp != NULL); /* We must have set tmp above */
+ jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.len = strlen(jb->val.string.val);
+ jb->type = jbvString;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiDouble:
{
JsonbValue jbv;
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 7162d37..eee16cc 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> TYPE_P SIZE_P
+%token <str> TYPE_P SIZE_P BOOLEAN_P STRINGFUNC_P
%token <str> DOUBLE_P NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
%token <str> ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
@@ -339,6 +339,8 @@ key_name:
| FLAG_P
| TYPE_P
| SIZE_P
+ | BOOLEAN_P
+ | STRINGFUNC_P
| DOUBLE_P
| NUMBER_P
| DECIMAL_P
@@ -362,6 +364,8 @@ key_name:
method:
TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
+ | BOOLEAN_P { $$ = jpiBoolean; }
+ | STRINGFUNC_P { $$ = jpiStringFunc; }
| DOUBLE_P { $$ = jpiDouble; }
| NUMBER_P { $$ = jpiNumber; }
| BIGINT_P { $$ = jpiBigint; }
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 6a7d72a..d87da14 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -418,6 +418,8 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
+ { 6, false, STRINGFUNC_P, "string"},
+ { 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 5577086..92989c8 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -91,6 +91,8 @@ typedef enum JsonPathItemType
jpiMod, /* expr % expr */
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
+ jpiBoolean, /* .boolean() item method */
+ jpiStringFunc, /* .string() item method */
jpiDouble, /* .double() item method */
jpiNumber, /* .number() item method */
jpiDecimal, /* .decimal() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index b77289c..67f47ca 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1348,6 +1348,269 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'l
1
(9 rows)
+select jsonb_path_query('null', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('null', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.boolean()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('{}', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"1.23"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('1e1000', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"nan"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"NaN"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"-inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"100"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('true', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('false', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('0', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('-1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('100', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"1"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"0"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"true"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"false"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"TRUE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"FALSE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"yes"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"NO"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"T"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"f"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"y"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"N"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('true', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('123', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+ jsonb_path_query_array
+------------------------
+ [true, true, false]
+(1 row)
+
+select jsonb_path_query('null', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('null', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('{}', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23aaa"
+(1 row)
+
+select jsonb_path_query('1234', '$.string()');
+ jsonb_path_query
+------------------
+ "1234"
+(1 row)
+
+select jsonb_path_query('true', '$.string()');
+ jsonb_path_query
+------------------
+ "true"
+(1 row)
+
+select jsonb_path_query('1234', '$.string().type()');
+ jsonb_path_query
+------------------
+ "string"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+ jsonb_path_query
+----------------------------
+ "Tue Aug 15 00:04:56 2023"
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+ jsonb_path_query_array
+--------------------------
+ ["1.23", "yes", "false"]
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+ jsonb_path_query_array
+--------------------------------
+ ["string", "string", "string"]
+(1 row)
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
jsonb_path_query
------------------
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index b47b2e9..6b16cda 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -381,6 +381,18 @@ select 'true.type()'::jsonpath;
true.type()
(1 row)
+select '$.boolean()'::jsonpath;
+ jsonpath
+-------------
+ $.boolean()
+(1 row)
+
+select '$.string()'::jsonpath;
+ jsonpath
+------------
+ $.string()
+(1 row)
+
select '$.double().floor().ceiling().abs()'::jsonpath;
jsonpath
------------------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index eb96c3b..f4b9915 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -283,6 +283,64 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 's
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => true);
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');
+select jsonb_path_query('null', '$.boolean()');
+select jsonb_path_query('null', '$.boolean()', silent => true);
+select jsonb_path_query('[]', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()');
+select jsonb_path_query('{}', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+select jsonb_path_query('1.23', '$.boolean()');
+select jsonb_path_query('"1.23"', '$.boolean()');
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+select jsonb_path_query('1e1000', '$.boolean()');
+select jsonb_path_query('"nan"', '$.boolean()');
+select jsonb_path_query('"NaN"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()');
+select jsonb_path_query('"-inf"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"100"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean()');
+select jsonb_path_query('false', '$.boolean()');
+select jsonb_path_query('1', '$.boolean()');
+select jsonb_path_query('0', '$.boolean()');
+select jsonb_path_query('-1', '$.boolean()');
+select jsonb_path_query('100', '$.boolean()');
+select jsonb_path_query('"1"', '$.boolean()');
+select jsonb_path_query('"0"', '$.boolean()');
+select jsonb_path_query('"true"', '$.boolean()');
+select jsonb_path_query('"false"', '$.boolean()');
+select jsonb_path_query('"TRUE"', '$.boolean()');
+select jsonb_path_query('"FALSE"', '$.boolean()');
+select jsonb_path_query('"yes"', '$.boolean()');
+select jsonb_path_query('"NO"', '$.boolean()');
+select jsonb_path_query('"T"', '$.boolean()');
+select jsonb_path_query('"f"', '$.boolean()');
+select jsonb_path_query('"y"', '$.boolean()');
+select jsonb_path_query('"N"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean().type()');
+select jsonb_path_query('123', '$.boolean().type()');
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+
+select jsonb_path_query('null', '$.string()');
+select jsonb_path_query('null', '$.string()', silent => true);
+select jsonb_path_query('[]', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()');
+select jsonb_path_query('{}', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+select jsonb_path_query('{}', '$.string()', silent => true);
+select jsonb_path_query('1.23', '$.string()');
+select jsonb_path_query('"1.23"', '$.string()');
+select jsonb_path_query('"1.23aaa"', '$.string()');
+select jsonb_path_query('1234', '$.string()');
+select jsonb_path_query('true', '$.string()');
+select jsonb_path_query('1234', '$.string().type()');
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 44275c9..8f07b71 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -69,6 +69,8 @@ select '(1).type()'::jsonpath;
select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
+select '$.boolean()'::jsonpath;
+select '$.string()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
select '$.bigint().integer().number().decimal()'::jsonpath;
select '$.decimal(4,2)'::jsonpath;
--
1.8.3.1
v4-0003-Implement-jsonpath-.number-.decimal-precision-sca.patchapplication/octet-stream; name=v4-0003-Implement-jsonpath-.number-.decimal-precision-sca.patchDownload
From 881aae4331af04e023056ea068bc4b6b099f2bd4 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Thu, 7 Dec 2023 18:21:12 +0530
Subject: [PATCH v4 3/5] Implement jsonpath .number(), .decimal([precision [,
scale]]), .bigint(), and .integer() methods
This commit implements jsonpath .number(), .decimal() with optional
precision and scale, .bigint(), and .integer() methods. The JSON
string or a numeric value is converted to the numeric, numeric,
bigint, and int4 type representation respectively. If precision and
scale are provided for .decimal(), then it is converted to the
equivalent numerictypmod and applied to the numeric number.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 56 +++
src/backend/catalog/sql_features.txt | 10 +-
src/backend/utils/adt/jsonpath.c | 46 +++
src/backend/utils/adt/jsonpath_exec.c | 266 +++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 48 ++-
src/backend/utils/adt/jsonpath_scan.l | 4 +
src/include/utils/jsonpath.h | 4 +
src/test/regress/expected/jsonb_jsonpath.out | 534 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 +
src/test/regress/sql/jsonb_jsonpath.sql | 133 +++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 1107 insertions(+), 8 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 96fff2c..611b539 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17685,6 +17685,62 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>number()</literal>
+ <returnvalue><replaceable>numeric</replaceable></returnvalue>
+ </para>
+ <para>
+ Numeric value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "123.45"}', '$.len.number()')</literal>
+ <returnvalue>123.45</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>decimal( [ <replaceable>precision</replaceable> [ , <replaceable>scale</replaceable> ] ] )</literal>
+ <returnvalue><replaceable>decimal</replaceable></returnvalue>
+ </para>
+ <para>
+ Rounded decimal value converted from a JSON number or string. <literal>precision</literal> and <literal>scale</literal> must be integer values.
+ </para>
+ <para>
+ <literal>jsonb_path_query('1234.5678', '$.decimal(6, 2)')</literal>
+ <returnvalue>1234.57</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
+ <returnvalue><replaceable>bigint</replaceable></returnvalue>
+ </para>
+ <para>
+ Big integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')</literal>
+ <returnvalue>9876543219</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>integer()</literal>
+ <returnvalue><replaceable>integer</replaceable></returnvalue>
+ </para>
+ <para>
+ Integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "12345"}', '$.len.integer()')</literal>
+ <returnvalue>12345</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>number</replaceable> <literal>.</literal> <literal>abs()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 80c40ea..6a76579 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -574,13 +574,13 @@ T861 SQL/JSON simplified accessor: case-sensitive JSON member accessor NO
T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
-T865 SQL/JSON item method: bigint() NO
+T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
T867 SQL/JSON item method: date() NO
-T868 SQL/JSON item method: decimal() NO
-T869 SQL/JSON item method: decimal() with precision and scale NO
-T870 SQL/JSON item method: integer() NO
-T871 SQL/JSON item method: number() NO
+T868 SQL/JSON item method: decimal() YES
+T869 SQL/JSON item method: decimal() with precision and scale YES
+T870 SQL/JSON item method: integer() YES
+T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
T873 SQL/JSON item method: time() NO
T874 SQL/JSON item method: time_tz() NO
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 8ff9b56..41e596f 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -294,6 +294,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMul:
case jpiDiv:
case jpiMod:
+ case jpiDecimal:
case jpiStartsWith:
{
/*
@@ -440,6 +441,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiType:
case jpiSize:
case jpiDouble:
+ case jpiNumber:
+ case jpiBigint:
+ case jpiInteger:
case jpiAbs:
case jpiCeiling:
case jpiFloor:
@@ -721,6 +725,30 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiDouble:
appendStringInfoString(buf, ".double()");
break;
+ case jpiNumber:
+ appendStringInfoString(buf, ".number()");
+ break;
+ case jpiDecimal:
+ appendStringInfoString(buf, ".decimal(");
+ if (v->content.args.left)
+ {
+ jspGetLeftArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ if (v->content.args.right)
+ {
+ appendStringInfoChar(buf, ',');
+ jspGetRightArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiBigint:
+ appendStringInfoString(buf, ".bigint()");
+ break;
+ case jpiInteger:
+ appendStringInfoString(buf, ".integer()");
+ break;
case jpiAbs:
appendStringInfoString(buf, ".abs()");
break;
@@ -789,6 +817,14 @@ jspOperationName(JsonPathItemType type)
return "size";
case jpiDouble:
return "double";
+ case jpiNumber:
+ return "number";
+ case jpiDecimal:
+ return "decimal";
+ case jpiBigint:
+ return "bigint";
+ case jpiInteger:
+ return "integer";
case jpiAbs:
return "abs";
case jpiCeiling:
@@ -894,6 +930,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiType:
case jpiSize:
case jpiDouble:
+ case jpiNumber:
+ case jpiBigint:
+ case jpiInteger:
case jpiAbs:
case jpiCeiling:
case jpiFloor:
@@ -922,6 +961,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiGreater:
case jpiLessOrEqual:
case jpiGreaterOrEqual:
+ case jpiDecimal:
case jpiStartsWith:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
@@ -1009,6 +1049,10 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiType ||
v->type == jpiSize ||
v->type == jpiDouble ||
+ v->type == jpiNumber ||
+ v->type == jpiDecimal ||
+ v->type == jpiBigint ||
+ v->type == jpiInteger ||
v->type == jpiAbs ||
v->type == jpiCeiling ||
v->type == jpiFloor ||
@@ -1041,6 +1085,7 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.left);
@@ -1062,6 +1107,7 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
+ v->type == jpiDecimal ||
v->type == jpiStartsWith);
jspInitByBuffer(a, v->base, v->content.args.right);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 766335e..c813369 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1086,6 +1086,272 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiNumber:
+ case jpiDecimal:
+ {
+ JsonbValue jbv;
+ Numeric num;
+ char *numstr = NULL;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ num = jb->val.numeric;
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->type == jpiDecimal)
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as number */
+ Datum datum;
+ bool noerr;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
+
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(datum);
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ /*
+ * If we have arguments, then they must be the precision and
+ * optional scale used in .decimal(). Convert them to the
+ * typmod equivalent and then truncate the numeric value per
+ * this typmod details.
+ */
+ if (jsp->type == jpiDecimal && jsp->content.args.left)
+ {
+ Datum numdatum;
+ Datum dtypmod;
+ int32 precision;
+ int32 scale = 0;
+ bool have_error;
+ bool noerr;
+ ArrayType *arrtypmod;
+ Datum datums[2];
+ char pstr[12]; /* sign, 10 digits and '\0' */
+ char sstr[12]; /* sign, 10 digits and '\0' */
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ jspGetLeftArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() precision");
+
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("precision of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->content.args.right)
+ {
+ jspGetRightArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() scale");
+
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("scale of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
+ /*
+ * numerictypmodin() takes the precision and scale in the
+ * form of CString arrays.
+ */
+ pg_ltoa(precision, pstr);
+ datums[0] = CStringGetDatum(pstr);
+ pg_ltoa(scale, sstr);
+ datums[1] = CStringGetDatum(sstr);
+ arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
+
+ dtypmod = DirectFunctionCall1(numerictypmodin,
+ PointerGetDatum(arrtypmod));
+
+ /* Convert numstr to Numeric with typmod */
+ Assert(numstr != NULL);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, dtypmod,
+ (Node *) &escontext,
+ &numdatum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(numdatum);
+ pfree(arrtypmod);
+ }
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = num;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiBigint:
+ {
+ JsonbValue jbv;
+ Datum datum;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ bool have_error;
+ int64 val;
+
+ val = numeric_int8_opt_error(jb->val.numeric, &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ jspOperationName(jsp->type)))));
+
+ datum = Int64GetDatum(val);
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as bigint */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+ bool noerr;
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiInteger:
+ {
+ JsonbValue jbv;
+ Datum datum;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ bool have_error;
+ int32 val;
+
+ val = numeric_int4_opt_error(jb->val.numeric, &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ datum = Int32GetDatum(val);
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as integer */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+ bool noerr;
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
case jpiAbs:
return executeNumericItemMethod(cxt, jsp, jb, unwrap, numeric_abs,
found);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 4233eed..a0484e9 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -80,7 +80,9 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> OR_P AND_P NOT_P
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
-%token <str> TYPE_P SIZE_P DOUBLE_P ABS_P CEILING_P FLOOR_P KEYVALUE_P
+%token <str> TYPE_P SIZE_P
+%token <str> DOUBLE_P NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
+%token <str> ABS_P CEILING_P FLOOR_P KEYVALUE_P
%token <str> DATETIME_P
%type <result> result
@@ -88,9 +90,9 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template
+ datetime_template opt_datetime_template csv_elem
-%type <elems> accessor_expr
+%type <elems> accessor_expr csv_list opt_csv_list
%type <indexs> index_list
@@ -248,11 +250,44 @@ accessor_op:
| array_accessor { $$ = $1; }
| '.' any_path { $$ = $2; }
| '.' method '(' ')' { $$ = makeItemType($2); }
+ | '.' DECIMAL_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiDecimal, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
+ else if (list_length($4) == 2)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".decimal() can only have an optional precision[,scale].")));
+ }
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+csv_elem:
+ INT_P
+ { $$ = makeItemNumeric(&$1); }
+ | '+' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
+ | '-' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
+ ;
+
+csv_list:
+ csv_elem { $$ = list_make1($1); }
+ | csv_list ',' csv_elem { $$ = lappend($1, $3); }
+ ;
+
+opt_csv_list:
+ csv_list { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -283,6 +318,10 @@ key_name:
| TYPE_P
| SIZE_P
| DOUBLE_P
+ | NUMBER_P
+ | DECIMAL_P
+ | BIGINT_P
+ | INTEGER_P
| ABS_P
| CEILING_P
| FLOOR_P
@@ -297,6 +336,9 @@ method:
TYPE_P { $$ = jpiType; }
| SIZE_P { $$ = jpiSize; }
| DOUBLE_P { $$ = jpiDouble; }
+ | NUMBER_P { $$ = jpiNumber; }
+ | BIGINT_P { $$ = jpiBigint; }
+ | INTEGER_P { $$ = jpiInteger; }
| ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
| FLOOR_P { $$ = jpiFloor; }
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 29c26af..b8922ee 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -410,11 +410,15 @@ static const JsonPathKeyword keywords[] = {
{ 4, false, WITH_P, "with"},
{ 5, true, FALSE_P, "false"},
{ 5, false, FLOOR_P, "floor"},
+ { 6, false, BIGINT_P, "bigint"},
{ 6, false, DOUBLE_P, "double"},
{ 6, false, EXISTS_P, "exists"},
+ { 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, DECIMAL_P, "decimal"},
+ { 7, false, INTEGER_P, "integer"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 59dc233..ddc272a 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -92,6 +92,10 @@ typedef enum JsonPathItemType
jpiType, /* .type() item method */
jpiSize, /* .size() item method */
jpiDouble, /* .double() item method */
+ jpiNumber, /* .number() item method */
+ jpiDecimal, /* .decimal() item method */
+ jpiBigint, /* .bigint() item method */
+ jpiInteger, /* .integer() item method */
jpiAbs, /* .abs() item method */
jpiCeiling, /* .ceiling() item method */
jpiFloor, /* .floor() item method */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 6659bc9..60af215 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1517,6 +1517,540 @@ select jsonb_path_query('"-inf"', '$.double()', silent => true);
------------------
(0 rows)
+select jsonb_path_query('null', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.bigint()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"1.23"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('1e1000', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"nan"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"NaN"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"-inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1.23', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('1.83', '$.bigint()');
+ jsonb_path_query
+------------------
+ 2
+(1 row)
+
+select jsonb_path_query('1234567890123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"+123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.bigint() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.integer()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"1.23"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('1e1000', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"nan"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"NaN"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"-inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1.23', '$.integer()');
+ jsonb_path_query
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('1.83', '$.integer()');
+ jsonb_path_query
+------------------
+ 2
+(1 row)
+
+select jsonb_path_query('12345678901', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"12345678901"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"+123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.integer() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.number()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.number()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.number() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('null', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.decimal()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.decimal()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+ jsonb_path_query
+------------------
+ 12345.7
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+ERROR: NUMERIC precision 0 must be between 1 and 1000
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+ERROR: NUMERIC precision 1001 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ 1200
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+ERROR: NUMERIC precision -6 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+ERROR: NUMERIC scale -1001 must be between -1000 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+ERROR: NUMERIC scale 1001 must be between -1000 and 1000
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ -1200
+(1 row)
+
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+ jsonb_path_query
+------------------
+ 0.01
+(1 row)
+
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+ jsonb_path_query
+------------------
+ 0.0012
+(1 row)
+
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+ jsonb_path_query
+------------------
+ 0
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
+ERROR: precision of jsonpath item method .decimal() is out of range for type integer
+select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+ERROR: scale of jsonpath item method .decimal() is out of range for type integer
select jsonb_path_query('{}', '$.abs()');
ERROR: jsonpath item method .abs() can only be applied to a numeric value
select jsonb_path_query('true', '$.floor()');
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index eeffb38..15fb717 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -387,6 +387,18 @@ select '$.double().floor().ceiling().abs()'::jsonpath;
$.double().floor().ceiling().abs()
(1 row)
+select '$.bigint().integer().number().decimal()'::jsonpath;
+ jsonpath
+-----------------------------------------
+ $.bigint().integer().number().decimal()
+(1 row)
+
+select '$.decimal(4,2)'::jsonpath;
+ jsonpath
+----------------
+ $.decimal(4,2)
+(1 row)
+
select '$.keyvalue().key'::jsonpath;
jsonpath
--------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e0ce509..4006a75 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -320,6 +320,139 @@ select jsonb_path_query('"-inf"', '$.double()');
select jsonb_path_query('"inf"', '$.double()', silent => true);
select jsonb_path_query('"-inf"', '$.double()', silent => true);
+select jsonb_path_query('null', '$.bigint()');
+select jsonb_path_query('true', '$.bigint()');
+select jsonb_path_query('null', '$.bigint()', silent => true);
+select jsonb_path_query('true', '$.bigint()', silent => true);
+select jsonb_path_query('[]', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()');
+select jsonb_path_query('{}', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+select jsonb_path_query('"1.23"', '$.bigint()');
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+select jsonb_path_query('1e1000', '$.bigint()');
+select jsonb_path_query('"nan"', '$.bigint()');
+select jsonb_path_query('"NaN"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()');
+select jsonb_path_query('"-inf"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+select jsonb_path_query('123', '$.bigint()');
+select jsonb_path_query('"123"', '$.bigint()');
+select jsonb_path_query('1.23', '$.bigint()');
+select jsonb_path_query('1.83', '$.bigint()');
+select jsonb_path_query('1234567890123', '$.bigint()');
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+select jsonb_path_query('"+123"', '$.bigint()');
+select jsonb_path_query('-123', '$.bigint()');
+select jsonb_path_query('"-123"', '$.bigint()');
+select jsonb_path_query('123', '$.bigint() * 2');
+
+select jsonb_path_query('null', '$.integer()');
+select jsonb_path_query('true', '$.integer()');
+select jsonb_path_query('null', '$.integer()', silent => true);
+select jsonb_path_query('true', '$.integer()', silent => true);
+select jsonb_path_query('[]', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()');
+select jsonb_path_query('{}', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+select jsonb_path_query('{}', '$.integer()', silent => true);
+select jsonb_path_query('"1.23"', '$.integer()');
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+select jsonb_path_query('1e1000', '$.integer()');
+select jsonb_path_query('"nan"', '$.integer()');
+select jsonb_path_query('"NaN"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()');
+select jsonb_path_query('"-inf"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+select jsonb_path_query('123', '$.integer()');
+select jsonb_path_query('"123"', '$.integer()');
+select jsonb_path_query('1.23', '$.integer()');
+select jsonb_path_query('1.83', '$.integer()');
+select jsonb_path_query('12345678901', '$.integer()');
+select jsonb_path_query('"12345678901"', '$.integer()');
+select jsonb_path_query('"+123"', '$.integer()');
+select jsonb_path_query('-123', '$.integer()');
+select jsonb_path_query('"-123"', '$.integer()');
+select jsonb_path_query('123', '$.integer() * 2');
+
+select jsonb_path_query('null', '$.number()');
+select jsonb_path_query('true', '$.number()');
+select jsonb_path_query('null', '$.number()', silent => true);
+select jsonb_path_query('true', '$.number()', silent => true);
+select jsonb_path_query('[]', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()');
+select jsonb_path_query('{}', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+select jsonb_path_query('{}', '$.number()', silent => true);
+select jsonb_path_query('1.23', '$.number()');
+select jsonb_path_query('"1.23"', '$.number()');
+select jsonb_path_query('"1.23aaa"', '$.number()');
+select jsonb_path_query('1e1000', '$.number()');
+select jsonb_path_query('"nan"', '$.number()');
+select jsonb_path_query('"NaN"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()');
+select jsonb_path_query('"-inf"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+select jsonb_path_query('123', '$.number()');
+select jsonb_path_query('"123"', '$.number()');
+select jsonb_path_query('12345678901234567890', '$.number()');
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+select jsonb_path_query('"+12.3"', '$.number()');
+select jsonb_path_query('-12.3', '$.number()');
+select jsonb_path_query('"-12.3"', '$.number()');
+select jsonb_path_query('12.3', '$.number() * 2');
+
+select jsonb_path_query('null', '$.decimal()');
+select jsonb_path_query('true', '$.decimal()');
+select jsonb_path_query('null', '$.decimal()', silent => true);
+select jsonb_path_query('true', '$.decimal()', silent => true);
+select jsonb_path_query('[]', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()');
+select jsonb_path_query('{}', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+select jsonb_path_query('1.23', '$.decimal()');
+select jsonb_path_query('"1.23"', '$.decimal()');
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+select jsonb_path_query('1e1000', '$.decimal()');
+select jsonb_path_query('"nan"', '$.decimal()');
+select jsonb_path_query('"NaN"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()');
+select jsonb_path_query('"-inf"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+select jsonb_path_query('123', '$.decimal()');
+select jsonb_path_query('"123"', '$.decimal()');
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+select jsonb_path_query('"+12.3"', '$.decimal()');
+select jsonb_path_query('-12.3', '$.decimal()');
+select jsonb_path_query('"-12.3"', '$.decimal()');
+select jsonb_path_query('12.3', '$.decimal() * 2');
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
+select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+
select jsonb_path_query('{}', '$.abs()');
select jsonb_path_query('true', '$.floor()');
select jsonb_path_query('"1.2"', '$.ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 56e0bef..1f25f89 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -70,6 +70,8 @@ select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
+select '$.bigint().integer().number().decimal()'::jsonpath;
+select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
--
1.8.3.1
v4-0004-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchapplication/octet-stream; name=v4-0004-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchDownload
From 13c20ddc14e0ecb1ceb046fc2c80c94c86456bab Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Thu, 7 Dec 2023 18:21:12 +0530
Subject: [PATCH v4 4/5] Implement jsonpath .date(), .time(), .time_tz(),
.timestamp(), and .timestamp_tz() methods
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.
The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. Unlike the
.datetime() method, all these methods don't accept format templates
and use ISO DateTime formats instead. However, except the .date()
method, these methods take an optional precision to adjust the
fractional seconds.
Jeevan Chalke
---
doc/src/sgml/func.sgml | 130 ++++
src/backend/catalog/sql_features.txt | 14 +-
src/backend/utils/adt/jsonpath.c | 70 +-
src/backend/utils/adt/jsonpath_exec.c | 301 +++++++-
src/backend/utils/adt/jsonpath_gram.y | 34 +-
src/backend/utils/adt/jsonpath_scan.l | 5 +
src/include/utils/jsonpath.h | 5 +
src/test/regress/expected/jsonb_jsonpath.out | 1053 +++++++++++++++++++++++++-
src/test/regress/expected/jsonpath.out | 54 ++
src/test/regress/sql/jsonb_jsonpath.sql | 295 ++++++++
src/test/regress/sql/jsonpath.sql | 9 +
11 files changed, 1931 insertions(+), 39 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 611b539..8991b45 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17814,6 +17814,136 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>date()</literal>
+ <returnvalue><replaceable>date</replaceable></returnvalue>
+ </para>
+ <para>
+ Date value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.date()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time()</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56"', '$.time()')</literal>
+ <returnvalue>"12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789"', '$.time(2)')</literal>
+ <returnvalue>"12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz()</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')</literal>
+ <returnvalue>"12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)')</literal>
+ <returnvalue>"12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp()</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')</literal>
+ <returnvalue>"2023-08-15T12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string, with
+ fractional seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz()</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')</literal>
+ <returnvalue>"2023-08-15T12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>object</replaceable> <literal>.</literal> <literal>keyvalue()</literal>
<returnvalue><replaceable>array</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 6a76579..aa80634 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -576,18 +576,18 @@ T863 SQL/JSON simplified accessor: single-quoted string literal as member access
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
-T867 SQL/JSON item method: date() NO
+T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
-T873 SQL/JSON item method: time() NO
-T874 SQL/JSON item method: time_tz() NO
-T875 SQL/JSON item method: time precision NO
-T876 SQL/JSON item method: timestamp() NO
-T877 SQL/JSON item method: timestamp_tz() NO
-T878 SQL/JSON item method: timestamp precision NO
+T873 SQL/JSON item method: time() YES
+T874 SQL/JSON item method: time_tz() YES
+T875 SQL/JSON item method: time precision YES
+T876 SQL/JSON item method: timestamp() YES
+T877 SQL/JSON item method: timestamp_tz() YES
+T878 SQL/JSON item method: timestamp precision YES
T879 JSON in equality operations YES with jsonb
T880 JSON in grouping operations YES with jsonb
T881 JSON in ordering operations NO with jsonb, partially supported
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 41e596f..2ebd11c 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -356,6 +356,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMinus:
case jpiExists:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
{
int32 arg = reserveSpaceForItemPointer(buf);
@@ -447,6 +451,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiAbs:
case jpiCeiling:
case jpiFloor:
+ case jpiDate:
case jpiKeyValue:
break;
default:
@@ -767,6 +772,45 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
}
appendStringInfoChar(buf, ')');
break;
+ case jpiDate:
+ appendStringInfoString(buf, ".date()");
+ break;
+ case jpiTime:
+ appendStringInfoString(buf, ".time(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimeTz:
+ appendStringInfoString(buf, ".time_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestamp:
+ appendStringInfoString(buf, ".timestamp(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestampTz:
+ appendStringInfoString(buf, ".timestamp_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
case jpiKeyValue:
appendStringInfoString(buf, ".keyvalue()");
break;
@@ -833,6 +877,16 @@ jspOperationName(JsonPathItemType type)
return "floor";
case jpiDatetime:
return "datetime";
+ case jpiDate:
+ return "date";
+ case jpiTime:
+ return "time";
+ case jpiTimeTz:
+ return "time_tz";
+ case jpiTimestamp:
+ return "timestamp";
+ case jpiTimestampTz:
+ return "timestamp_tz";
case jpiKeyValue:
return "keyvalue";
case jpiStartsWith:
@@ -936,6 +990,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiAbs:
case jpiCeiling:
case jpiFloor:
+ case jpiDate:
case jpiKeyValue:
case jpiLast:
break;
@@ -979,6 +1034,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiPlus:
case jpiMinus:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
read_int32(v->content.arg, base, pos);
break;
case jpiIndexArray:
@@ -1004,7 +1063,11 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiExists ||
v->type == jpiPlus ||
v->type == jpiMinus ||
- v->type == jpiDatetime);
+ v->type == jpiDatetime ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
jspInitByBuffer(a, v->base, v->content.arg);
}
@@ -1057,6 +1120,11 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiCeiling ||
v->type == jpiFloor ||
v->type == jpiDatetime ||
+ v->type == jpiDate ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz ||
v->type == jpiKeyValue ||
v->type == jpiStartsWith ||
v->type == jpiLikeRegex);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index c813369..1e090e1 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1365,6 +1365,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
found);
case jpiDatetime:
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -2037,11 +2042,15 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
/*
- * Implementation of the .datetime() method.
+ * Implementation of the .datetime() and related methods.
*
* 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.
+ *
+ * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
+ * have a format, so ISO format is used. However, except .date(), they all
+ * take an optional time precision.
*/
static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
@@ -2057,6 +2066,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
bool hasNext;
JsonPathExecResult res = jperNotFound;
JsonPathItem elem;
+ int32 time_precision = -1;
if (!(jb = getScalar(jb, jbvString)))
RETURN_ERROR(ereport(ERROR,
@@ -2074,7 +2084,11 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
collid = DEFAULT_COLLATION_OID;
- if (jsp->content.arg)
+ /*
+ * .datetime(template) has an argument, the rest of the methods don't have
+ * an argument. So we handle that separately.
+ */
+ if (jsp->type == jpiDatetime && jsp->content.arg)
{
text *template;
char *template_str;
@@ -2136,6 +2150,30 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
static text *fmt_txt[lengthof(fmt_str)] = {0};
int i;
+ /*
+ * Check for optional precision for methods other than .datetime() and
+ * .date()
+ */
+ if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
+ jsp->content.arg)
+ {
+ bool have_error;
+
+ jspGetArg(jsp, &elem);
+
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for %s argument",
+ jspOperationName(jsp->type));
+
+ time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
/* loop until datetime format fits */
for (i = 0; i < lengthof(fmt_str); i++)
{
@@ -2162,11 +2200,260 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
if (res == jperNotFound)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
- errhint("Use a datetime template argument to specify the input data format."))));
+ {
+ if (jsp->type == jpiDatetime)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("datetime format is not recognized: \"%s\"",
+ text_to_cstring(datetime)),
+ errhint("Use a datetime template argument to specify the input data format."))));
+ else
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("%s format is not recognized: \"%s\"",
+ jspOperationName(jsp->type), text_to_cstring(datetime)))));
+
+ }
+ }
+
+ /*
+ * parse_datetime() processes the entire input string per the template or
+ * ISO format and returns the Datum in best fitted datetime type. So, if
+ * this call is for a specific datatype, then we do the conversion here.
+ * Throw an error for incompatible types.
+ */
+ switch (jsp->type)
+ {
+ case jpiDatetime: /* Nothing to do for DATETIME */
+ break;
+ case jpiDate:
+ {
+ /* Convert result type to date */
+ switch (typid)
+ {
+ case DATEOID: /* Nothing to do for DATE */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("date format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_date,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_date,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = DATEOID;
+ }
+ break;
+ case jpiTime:
+ {
+ /* Convert result type to time without time zone */
+ switch (typid)
+ {
+ case TIMEOID: /* Nothing to do for TIME */
+ break;
+ case DATEOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMETZOID:
+ value = DirectFunctionCall1(timetz_time,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_time,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_time,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeADT result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(false,
+ time_precision);
+ result = DatumGetTimeADT(value);
+ AdjustTimeForTypmod(&result, time_precision);
+ value = TimeADTGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMEOID;
+ }
+ break;
+ case jpiTimeTz:
+ {
+ /* Convert result type to time with time zone */
+ switch (typid)
+ {
+ case TIMETZOID: /* Nothing to do for TIMETZ */
+ break;
+ case DATEOID:
+ case TIMESTAMPOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID:
+ value = DirectFunctionCall1(time_timetz,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timetz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeTzADT *result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(true,
+ time_precision);
+ result = DatumGetTimeTzADTP(value);
+ AdjustTimeForTypmod(&result->time, time_precision);
+ value = TimeTzADTPGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMETZOID;
+ }
+ break;
+ case jpiTimestamp:
+ {
+ /* Convert result type to timestamp without time zone */
+ switch (typid)
+ {
+ case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamp,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timestamp,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(false,
+ time_precision);
+ result = DatumGetTimestamp(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPOID;
+ }
+ break;
+ case jpiTimestampTz:
+ {
+ /* Convert result type to timestamp with time zone */
+ switch (typid)
+ {
+ case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamptz,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_timestamptz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(true,
+ time_precision);
+ result = DatumGetTimestampTz(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampTzGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPTZOID;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
pfree(datetime);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index a0484e9..7162d37 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -83,7 +83,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> TYPE_P SIZE_P
%token <str> DOUBLE_P NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
%token <str> ABS_P CEILING_P FLOOR_P KEYVALUE_P
-%token <str> DATETIME_P
+%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
%type <result> result
@@ -91,6 +91,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
datetime_template opt_datetime_template csv_elem
+ datetime_method datetime_precision opt_datetime_precision
%type <elems> accessor_expr csv_list opt_csv_list
@@ -264,11 +265,23 @@ accessor_op:
errmsg("invalid input syntax for type %s", "jsonpath"),
errdetail(".decimal() can only have an optional precision[,scale].")));
}
- | '.' DATETIME_P '(' opt_datetime_template ')'
- { $$ = makeItemUnary(jpiDatetime, $4); }
+ | '.' datetime_method { $$ = $2; }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
;
+datetime_method:
+ DATETIME_P '(' opt_datetime_template ')'
+ { $$ = makeItemUnary(jpiDatetime, $3); }
+ | TIME_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTime, $3); }
+ | TIME_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimeTz, $3); }
+ | TIMESTAMP_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestamp, $3); }
+ | TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestampTz, $3); }
+ ;
+
csv_elem:
INT_P
{ $$ = makeItemNumeric(&$1); }
@@ -288,6 +301,15 @@ opt_csv_list:
| /* EMPTY */ { $$ = NULL; }
;
+datetime_precision:
+ INT_P { $$ = makeItemNumeric(&$1); }
+ ;
+
+opt_datetime_precision:
+ datetime_precision { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -326,6 +348,11 @@ key_name:
| CEILING_P
| FLOOR_P
| DATETIME_P
+ | DATE_P
+ | TIME_P
+ | TIME_TZ_P
+ | TIMESTAMP_P
+ | TIMESTAMP_TZ_P
| KEYVALUE_P
| STARTS_P
| WITH_P
@@ -342,6 +369,7 @@ method:
| ABS_P { $$ = jpiAbs; }
| CEILING_P { $$ = jpiCeiling; }
| FLOOR_P { $$ = jpiFloor; }
+ | DATE_P { $$ = jpiDate; }
| KEYVALUE_P { $$ = jpiKeyValue; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index b8922ee..6a7d72a 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -401,10 +401,12 @@ static const JsonPathKeyword keywords[] = {
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
+ { 4, false, DATE_P, "date"},
{ 4, false, FLAG_P, "flag"},
{ 4, false, LAST_P, "last"},
{ 4, true, NULL_P, "null"},
{ 4, false, SIZE_P, "size"},
+ { 4, false, TIME_P, "time"},
{ 4, true, TRUE_P, "true"},
{ 4, false, TYPE_P, "type"},
{ 4, false, WITH_P, "with"},
@@ -419,10 +421,13 @@ static const JsonPathKeyword keywords[] = {
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
+ { 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
+ { 9, false, TIMESTAMP_P, "timestamp"},
{ 10,false, LIKE_REGEX_P, "like_regex"},
+ { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
/* Check if current scanstring value is a keyword */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index ddc272a..5577086 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -100,6 +100,11 @@ typedef enum JsonPathItemType
jpiCeiling, /* .ceiling() item method */
jpiFloor, /* .floor() item method */
jpiDatetime, /* .datetime() item method */
+ jpiDate, /* .date() item method */
+ jpiTime, /* .time() item method */
+ jpiTimeTz, /* .time_tz() item method */
+ jpiTimestamp, /* .timestamp() item method */
+ jpiTimestampTz, /* .timestamp_tz() item method */
jpiKeyValue, /* .keyvalue() item method */
jpiSubscript, /* array subscript: 'expr' or 'expr TO expr' */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 60af215..b77289c 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2266,7 +2266,443 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
ERROR: unmatched format character "T"
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
ERROR: unmatched format character "T"
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('true', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('1', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('[]', '$.date()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('{}', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.date()');
+ERROR: date format is not recognized: "bogus"
+select jsonb '"2023-08-15"' @? '$.date()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+ jsonb_path_query
+------------------
+ "date"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+ERROR: date format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+ERROR: date format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ERROR: syntax error at or near "2" of jsonpath input
+LINE 1: select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ ^
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('true', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('1', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('[]', '$.time()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('{}', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time()');
+ERROR: time format is not recognized: "bogus"
+select jsonb '"12:34:56"' @? '$.time()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+ jsonb_path_query
+--------------------------
+ "time without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+ERROR: time format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+ERROR: time precision of jsonpath item method .time() is out of range for type integer
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+ jsonb_path_query
+------------------
+ "12:34:57"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+ jsonb_path_query
+------------------
+ "12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+WARNING: TIME(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+WARNING: TIME(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------
+ "12:34:56.789012"
+(1 row)
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('true', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('1', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.time_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "bogus"
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+ jsonb_path_query
+-----------------------
+ "time with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15 12:34:56"
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+ERROR: time precision of jsonpath item method .time_tz() is out of range for type integer
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+ jsonb_path_query
+------------------
+ "12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+ jsonb_path_query
+---------------------
+ "12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+WARNING: TIME(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+WARNING: TIME(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------------
+ "12:34:56.789012+05:30"
+(1 row)
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+ jsonb_path_query
+-------------------------------
+ "timestamp without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+ERROR: time precision of jsonpath item method .timestamp() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:57"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+ jsonb_path_query
+--------------------------
+ "2023-08-15T12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+WARNING: TIMESTAMP(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+WARNING: TIMESTAMP(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------
+ "2023-08-15T12:34:56.789012"
+(1 row)
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+ jsonb_path_query
+----------------------------
+ "timestamp with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T07:00:00+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+ERROR: time precision of jsonpath item method .timestamp_tz() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+ jsonb_path_query
+--------------------------------
+ "2023-08-15T12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+WARNING: TIMESTAMP(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+WARNING: TIMESTAMP(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------------
+ "2023-08-15T12:34:56.789012+05:30"
+(1 row)
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "07:04:56+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+00:00"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2332,6 +2768,36 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "17:04:56+10:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T02:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2397,6 +2863,30 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "00:04:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
jsonb_path_query
------------------
@@ -2553,28 +3043,123 @@ select jsonb_path_query_tz(
"2017-03-10T01:02:03+04:00"
(2 rows)
--- time comparison
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
- jsonb_path_query_tz
----------------------
- "12:35:00"
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-10T00:00:00"
+ "2017-03-10T03:00:00+03:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10T00:00:00"
+ "2017-03-10T12:34:56"
+ "2017-03-10T03:00:00+03:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-09"
+ "2017-03-10T01:02:03+04:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+-- time comparison
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
"12:35:00+00:00"
(2 rows)
@@ -2598,6 +3183,112 @@ select jsonb_path_query_tz(
"13:35:00+01:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+ jsonb_path_query
+------------------
+ "12:35:00.12"
+ "12:36:00.11"
+ "12:35:00.12"
+ "13:35:00.12"
+(4 rows)
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -2644,6 +3335,110 @@ select jsonb_path_query_tz(
"10:35:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00"
+ "12:35:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+ jsonb_path_query
+---------------------
+ "12:35:00.12+01:00"
+ "12:36:00.11+01:00"
+ "12:35:00.12-02:00"
+ "12:35:00.12+00:00"
+ "11:35:00.12+00:00"
+(5 rows)
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2691,6 +3486,111 @@ select jsonb_path_query_tz(
"2017-03-10"
(3 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00+01:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T13:35:00+01:00"
+ "2017-03-10T12:35:00-01:00"
+ "2017-03-11"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+ jsonb_path_query
+--------------------------
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T12:36:00.11"
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T13:35:00.1"
+ "2017-03-11T00:00:00"
+(5 rows)
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2740,6 +3640,117 @@ select jsonb_path_query_tz(
"2017-03-10"
(4 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T12:35:00"
+ "2017-03-11"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+ jsonb_path_query
+--------------------------------
+ "2017-03-10T12:35:00.12+01:00"
+ "2017-03-10T12:36:00.11+01:00"
+ "2017-03-10T12:35:00.12-02:00"
+ "2017-03-10T12:35:00.12+00:00"
+ "2017-03-11T00:00:00+00:00"
+(5 rows)
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
jsonb_path_query
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index 15fb717..b47b2e9 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -417,6 +417,60 @@ select '$.datetime("datetime template")'::jsonpath;
$.datetime("datetime template")
(1 row)
+select '$.date()'::jsonpath;
+ jsonpath
+----------
+ $.date()
+(1 row)
+
+select '$.time()'::jsonpath;
+ jsonpath
+----------
+ $.time()
+(1 row)
+
+select '$.time(6)'::jsonpath;
+ jsonpath
+-----------
+ $.time(6)
+(1 row)
+
+select '$.time_tz()'::jsonpath;
+ jsonpath
+-------------
+ $.time_tz()
+(1 row)
+
+select '$.time_tz(4)'::jsonpath;
+ jsonpath
+--------------
+ $.time_tz(4)
+(1 row)
+
+select '$.timestamp()'::jsonpath;
+ jsonpath
+---------------
+ $.timestamp()
+(1 row)
+
+select '$.timestamp(2)'::jsonpath;
+ jsonpath
+----------------
+ $.timestamp(2)
+(1 row)
+
+select '$.timestamp_tz()'::jsonpath;
+ jsonpath
+------------------
+ $.timestamp_tz()
+(1 row)
+
+select '$.timestamp_tz(0)'::jsonpath;
+ jsonpath
+-------------------
+ $.timestamp_tz(0)
+(1 row)
+
select '$ ? (@ starts with "abc")'::jsonpath;
jsonpath
-------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 4006a75..eb96c3b 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -505,8 +505,138 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+select jsonb_path_query('true', '$.date()');
+select jsonb_path_query('1', '$.date()');
+select jsonb_path_query('[]', '$.date()');
+select jsonb_path_query('[]', 'strict $.date()');
+select jsonb_path_query('{}', '$.date()');
+select jsonb_path_query('"bogus"', '$.date()');
+
+select jsonb '"2023-08-15"' @? '$.date()';
+select jsonb_path_query('"2023-08-15"', '$.date()');
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+select jsonb_path_query('true', '$.time()');
+select jsonb_path_query('1', '$.time()');
+select jsonb_path_query('[]', '$.time()');
+select jsonb_path_query('[]', 'strict $.time()');
+select jsonb_path_query('{}', '$.time()');
+select jsonb_path_query('"bogus"', '$.time()');
+
+select jsonb '"12:34:56"' @? '$.time()';
+select jsonb_path_query('"12:34:56"', '$.time()');
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+select jsonb_path_query('true', '$.time_tz()');
+select jsonb_path_query('1', '$.time_tz()');
+select jsonb_path_query('[]', '$.time_tz()');
+select jsonb_path_query('[]', 'strict $.time_tz()');
+select jsonb_path_query('{}', '$.time_tz()');
+select jsonb_path_query('"bogus"', '$.time_tz()');
+
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+select jsonb_path_query('true', '$.timestamp()');
+select jsonb_path_query('1', '$.timestamp()');
+select jsonb_path_query('[]', '$.timestamp()');
+select jsonb_path_query('[]', 'strict $.timestamp()');
+select jsonb_path_query('{}', '$.timestamp()');
+select jsonb_path_query('"bogus"', '$.timestamp()');
+
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+select jsonb_path_query('true', '$.timestamp_tz()');
+select jsonb_path_query('1', '$.timestamp_tz()');
+select jsonb_path_query('[]', '$.timestamp_tz()');
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+select jsonb_path_query('{}', '$.timestamp_tz()');
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -522,6 +652,12 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -537,6 +673,11 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
select jsonb_path_query('"2017-03-10"', '$.datetime()');
select jsonb_path_query('"2017-03-10 12:34:56"', '$.datetime().type()');
@@ -579,6 +720,34 @@ select jsonb_path_query_tz(
'["2017-03-10", "2017-03-11", "2017-03-09", "12:34:56", "01:02:03+04", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
'$[*].datetime() ? (@ < "10.03.2017".datetime("dd.mm.yyyy"))');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -599,6 +768,38 @@ select jsonb_path_query_tz(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
'$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -619,6 +820,37 @@ select jsonb_path_query_tz(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ < "12:35 +1".datetime("HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -639,6 +871,37 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35".datetime("dd.mm.yyyy HH24:MI"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -659,6 +922,38 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35 +1".datetime("dd.mm.yyyy HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 1f25f89..44275c9 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -75,6 +75,15 @@ select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
+select '$.date()'::jsonpath;
+select '$.time()'::jsonpath;
+select '$.time(6)'::jsonpath;
+select '$.time_tz()'::jsonpath;
+select '$.time_tz(4)'::jsonpath;
+select '$.timestamp()'::jsonpath;
+select '$.timestamp(2)'::jsonpath;
+select '$.timestamp_tz()'::jsonpath;
+select '$.timestamp_tz(0)'::jsonpath;
select '$ ? (@ starts with "abc")'::jsonpath;
select '$ ? (@ starts with $var)'::jsonpath;
--
1.8.3.1
On 07.12.23 14:24, Jeevan Chalke wrote:
We have the same issue with integer conversion and need a fix.
Unfortunately, I was using int8in() for the conversion of numeric
values. We should be using numeric_int8() instead. However, there is no
opt_error version of the same.So, I have introduced a numeric_int8_opt_error() version just like we
have one for int4, i.e. numeric_int4_opt_error(), to suppress the error.
These changes are in the 0001 patch. (All other patch numbers are now
increased by 1)I have used this new function to fix this reported issue and used
numeric_int4_opt_error() for integer conversion.
I have committed the 0001 and 0002 patches for now.
The remaining patches look reasonable to me, but I haven't reviewed them
in detail.
On 03.01.24 13:01, Peter Eisentraut wrote:
On 07.12.23 14:24, Jeevan Chalke wrote:
We have the same issue with integer conversion and need a fix.
Unfortunately, I was using int8in() for the conversion of numeric
values. We should be using numeric_int8() instead. However, there is
no opt_error version of the same.So, I have introduced a numeric_int8_opt_error() version just like we
have one for int4, i.e. numeric_int4_opt_error(), to suppress the
error. These changes are in the 0001 patch. (All other patch numbers
are now increased by 1)I have used this new function to fix this reported issue and used
numeric_int4_opt_error() for integer conversion.I have committed the 0001 and 0002 patches for now.
The remaining patches look reasonable to me, but I haven't reviewed them
in detail.
The 0002 patch had to be reverted, because we can't change the order of
the enum values in JsonPathItemType. I have instead committed a
different patch that adjusts the various switch cases to observe the
current order of the enum. That also means that the remaining patches
that add new item methods need to add the new enum values at the end and
adjust the rest of their code accordingly.
On Thu, Jan 4, 2024 at 2:34 AM Peter Eisentraut <peter@eisentraut.org>
wrote:
On 03.01.24 13:01, Peter Eisentraut wrote:
On 07.12.23 14:24, Jeevan Chalke wrote:
We have the same issue with integer conversion and need a fix.
Unfortunately, I was using int8in() for the conversion of numeric
values. We should be using numeric_int8() instead. However, there is
no opt_error version of the same.So, I have introduced a numeric_int8_opt_error() version just like we
have one for int4, i.e. numeric_int4_opt_error(), to suppress the
error. These changes are in the 0001 patch. (All other patch numbers
are now increased by 1)I have used this new function to fix this reported issue and used
numeric_int4_opt_error() for integer conversion.I have committed the 0001 and 0002 patches for now.
The remaining patches look reasonable to me, but I haven't reviewed them
in detail.The 0002 patch had to be reverted, because we can't change the order of
the enum values in JsonPathItemType. I have instead committed a
different patch that adjusts the various switch cases to observe the
current order of the enum. That also means that the remaining patches
that add new item methods need to add the new enum values at the end and
adjust the rest of their code accordingly.
Thanks, Peter.
I will work on rebasing and reorganizing the remaining patches.
Thanks
--
Jeevan Chalke
*PrincipalProduct Development*
edbpostgres.com
On Mon, Jan 8, 2024 at 12:30 PM Jeevan Chalke <
jeevan.chalke@enterprisedb.com> wrote:
On Thu, Jan 4, 2024 at 2:34 AM Peter Eisentraut <peter@eisentraut.org>
wrote:On 03.01.24 13:01, Peter Eisentraut wrote:
On 07.12.23 14:24, Jeevan Chalke wrote:
We have the same issue with integer conversion and need a fix.
Unfortunately, I was using int8in() for the conversion of numeric
values. We should be using numeric_int8() instead. However, there is
no opt_error version of the same.So, I have introduced a numeric_int8_opt_error() version just like we
have one for int4, i.e. numeric_int4_opt_error(), to suppress the
error. These changes are in the 0001 patch. (All other patch numbers
are now increased by 1)I have used this new function to fix this reported issue and used
numeric_int4_opt_error() for integer conversion.I have committed the 0001 and 0002 patches for now.
The remaining patches look reasonable to me, but I haven't reviewed
them
in detail.
The 0002 patch had to be reverted, because we can't change the order of
the enum values in JsonPathItemType. I have instead committed a
different patch that adjusts the various switch cases to observe the
current order of the enum. That also means that the remaining patches
that add new item methods need to add the new enum values at the end and
adjust the rest of their code accordingly.Thanks, Peter.
I will work on rebasing and reorganizing the remaining patches.
Attached are rebased patches.
Thanks
Thanks
--
Jeevan Chalke*PrincipalProduct Development*
edbpostgres.com
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
Attachments:
v5-0003-Implement-jsonpath-.boolean-and-.string-methods.patchapplication/octet-stream; name=v5-0003-Implement-jsonpath-.boolean-and-.string-methods.patchDownload
From 6037788694e33b6a1de3413b98f8c6f5a5f19012 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Wed, 10 Jan 2024 13:14:49 +0530
Subject: [PATCH v5 3/3] Implement jsonpath .boolean() and .string() methods
This commit implements jsonpath .boolean() and .string() methods.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert string to a bool.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 32 ++++
src/backend/catalog/sql_features.txt | 4 +-
src/backend/utils/adt/jsonpath.c | 18 +-
src/backend/utils/adt/jsonpath_exec.c | 147 +++++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 5 +
src/backend/utils/adt/jsonpath_scan.l | 2 +
src/include/utils/jsonpath.h | 2 +
src/test/regress/expected/jsonb_jsonpath.out | 263 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 ++
src/test/regress/sql/jsonb_jsonpath.sql | 58 ++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 542 insertions(+), 3 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 0080ee2..4ae97d5 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17676,6 +17676,38 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>boolean()</literal>
+ <returnvalue><replaceable>boolean</replaceable></returnvalue>
+ </para>
+ <para>
+ Boolean value converted from a JSON boolean, number, or string
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')</literal>
+ <returnvalue>[true, true, false]</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>string()</literal>
+ <returnvalue><replaceable>string</replaceable></returnvalue>
+ </para>
+ <para>
+ String value converted from a JSON boolean, number, string, or datetime
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')</literal>
+ <returnvalue>["1.23", "xyz", "false"]</returnvalue>
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.datetime().string()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>double()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index aa80634..4085a2d 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -575,13 +575,13 @@ T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
-T866 SQL/JSON item method: boolean() NO
+T866 SQL/JSON item method: boolean() YES
T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
-T872 SQL/JSON item method: string() NO
+T872 SQL/JSON item method: string() YES
T873 SQL/JSON item method: time() YES
T874 SQL/JSON item method: time_tz() YES
T875 SQL/JSON item method: time precision YES
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index ba9537e..457d4fc 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -453,6 +453,8 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiBigint:
case jpiInteger:
case jpiDate:
+ case jpiBoolean:
+ case jpiStringFunc:
break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
@@ -814,6 +816,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
}
appendStringInfoChar(buf, ')');
break;
+ case jpiBoolean:
+ appendStringInfoString(buf, ".boolean()");
+ break;
+ case jpiStringFunc:
+ appendStringInfoString(buf, ".string()");
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
@@ -893,6 +901,10 @@ jspOperationName(JsonPathItemType type)
return "timestamp";
case jpiTimestampTz:
return "timestamp_tz";
+ case jpiBoolean:
+ return "boolean";
+ case jpiStringFunc:
+ return "string";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -993,6 +1005,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiBigint:
case jpiInteger:
case jpiDate:
+ case jpiBoolean:
+ case jpiStringFunc:
break;
case jpiString:
case jpiKey:
@@ -1127,7 +1141,9 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiTime ||
v->type == jpiTimeTz ||
v->type == jpiTimestamp ||
- v->type == jpiTimestampTz);
+ v->type == jpiTimestampTz ||
+ v->type == jpiBoolean ||
+ v->type == jpiStringFunc);
if (a)
jspInitByBuffer(a, v->base, v->nextPos);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index aa154f3..c024ff7 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1381,6 +1381,153 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBoolean:
+ {
+ JsonbValue jbv;
+ bool bval;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvBool)
+ {
+ bval = jb->val.boolean;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvNumeric)
+ {
+ int ival;
+ Datum datum;
+ bool noerr;
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ jspOperationName(jsp->type)))));
+
+ ival = DatumGetInt32(datum);
+ if (ival == 0)
+ bval = false;
+ else
+ bval = true;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as boolean */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+
+ if (!parse_bool(tmp, &bval))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, or numeric value",
+ jspOperationName(jsp->type)))));
+
+ jb = &jbv;
+ jb->type = jbvBool;
+ jb->val.boolean = bval;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiStringFunc:
+ {
+ JsonbValue jbv;
+ char *tmp = NULL;
+
+ switch (JsonbType(jb))
+ {
+ case jbvString:
+ /*
+ * Value is not necessarily null-terminated, so we do
+ * pnstrdup() here.
+ */
+ tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ break;
+ case jbvNumeric:
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ break;
+ case jbvBool:
+ tmp = (jb->val.boolean) ? "true" : "false";
+ break;
+ case jbvDatetime:
+ {
+ switch (jb->val.datetime.typid)
+ {
+ case DATEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(date_out,
+ jb->val.datetime.value));
+ break;
+ case TIMEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(time_out,
+ jb->val.datetime.value));
+ break;
+ case TIMETZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timetz_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamp_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPTZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ jb->val.datetime.value));
+ break;
+ default:
+ elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+ jb->val.datetime.typid);
+ }
+ }
+ break;
+ case jbvNull:
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, numeric, or datetime value",
+ jspOperationName(jsp->type)))));
+ break;
+ }
+
+ res = jperOk;
+
+ jb = &jbv;
+ Assert(tmp != NULL); /* We must have set tmp above */
+ jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.len = strlen(jb->val.string.val);
+ jb->type = jbvString;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
default:
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 79c6371..3054762 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -83,6 +83,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
+%token <str> BOOLEAN_P STRINGFUNC_P
%type <result> result
@@ -356,6 +357,8 @@ key_name:
| TIME_TZ_P
| TIMESTAMP_P
| TIMESTAMP_TZ_P
+ | BOOLEAN_P
+ | STRINGFUNC_P
;
method:
@@ -370,6 +373,8 @@ method:
| BIGINT_P { $$ = jpiBigint; }
| INTEGER_P { $$ = jpiInteger; }
| DATE_P { $$ = jpiDate; }
+ | BOOLEAN_P { $$ = jpiBoolean; }
+ | STRINGFUNC_P { $$ = jpiStringFunc; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index d3e9401..7acda77 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -418,6 +418,8 @@ static const JsonPathKeyword keywords[] = {
{ 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
+ { 6, false, STRINGFUNC_P, "string"},
+ { 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index d0c88d5..d85d4e4 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -111,6 +111,8 @@ typedef enum JsonPathItemType
jpiTimeTz, /* .time_tz() item method */
jpiTimestamp, /* .timestamp() item method */
jpiTimestampTz, /* .timestamp_tz() item method */
+ jpiBoolean, /* .boolean() item method */
+ jpiStringFunc, /* .string() item method */
} JsonPathItemType;
/* XQuery regex mode flags for LIKE_REGEX predicate */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index b77289c..67f47ca 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1348,6 +1348,269 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'l
1
(9 rows)
+select jsonb_path_query('null', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('null', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.boolean()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('{}', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"1.23"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('1e1000', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"nan"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"NaN"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"-inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"100"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('true', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('false', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('0', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('-1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('100', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"1"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"0"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"true"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"false"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"TRUE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"FALSE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"yes"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"NO"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"T"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"f"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"y"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"N"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('true', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('123', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+ jsonb_path_query_array
+------------------------
+ [true, true, false]
+(1 row)
+
+select jsonb_path_query('null', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('null', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('{}', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23aaa"
+(1 row)
+
+select jsonb_path_query('1234', '$.string()');
+ jsonb_path_query
+------------------
+ "1234"
+(1 row)
+
+select jsonb_path_query('true', '$.string()');
+ jsonb_path_query
+------------------
+ "true"
+(1 row)
+
+select jsonb_path_query('1234', '$.string().type()');
+ jsonb_path_query
+------------------
+ "string"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+ jsonb_path_query
+----------------------------
+ "Tue Aug 15 00:04:56 2023"
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+ jsonb_path_query_array
+--------------------------
+ ["1.23", "yes", "false"]
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+ jsonb_path_query_array
+--------------------------------
+ ["string", "string", "string"]
+(1 row)
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
jsonb_path_query
------------------
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index b47b2e9..6b16cda 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -381,6 +381,18 @@ select 'true.type()'::jsonpath;
true.type()
(1 row)
+select '$.boolean()'::jsonpath;
+ jsonpath
+-------------
+ $.boolean()
+(1 row)
+
+select '$.string()'::jsonpath;
+ jsonpath
+------------
+ $.string()
+(1 row)
+
select '$.double().floor().ceiling().abs()'::jsonpath;
jsonpath
------------------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index eb96c3b..f4b9915 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -283,6 +283,64 @@ select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 's
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'strict $[*].size()', silent => true);
select jsonb_path_query('[1,null,true,"11",[],[1],[1,2,3],{},{"a":1,"b":2}]', 'lax $[*].size()');
+select jsonb_path_query('null', '$.boolean()');
+select jsonb_path_query('null', '$.boolean()', silent => true);
+select jsonb_path_query('[]', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()');
+select jsonb_path_query('{}', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+select jsonb_path_query('1.23', '$.boolean()');
+select jsonb_path_query('"1.23"', '$.boolean()');
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+select jsonb_path_query('1e1000', '$.boolean()');
+select jsonb_path_query('"nan"', '$.boolean()');
+select jsonb_path_query('"NaN"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()');
+select jsonb_path_query('"-inf"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"100"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean()');
+select jsonb_path_query('false', '$.boolean()');
+select jsonb_path_query('1', '$.boolean()');
+select jsonb_path_query('0', '$.boolean()');
+select jsonb_path_query('-1', '$.boolean()');
+select jsonb_path_query('100', '$.boolean()');
+select jsonb_path_query('"1"', '$.boolean()');
+select jsonb_path_query('"0"', '$.boolean()');
+select jsonb_path_query('"true"', '$.boolean()');
+select jsonb_path_query('"false"', '$.boolean()');
+select jsonb_path_query('"TRUE"', '$.boolean()');
+select jsonb_path_query('"FALSE"', '$.boolean()');
+select jsonb_path_query('"yes"', '$.boolean()');
+select jsonb_path_query('"NO"', '$.boolean()');
+select jsonb_path_query('"T"', '$.boolean()');
+select jsonb_path_query('"f"', '$.boolean()');
+select jsonb_path_query('"y"', '$.boolean()');
+select jsonb_path_query('"N"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean().type()');
+select jsonb_path_query('123', '$.boolean().type()');
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+
+select jsonb_path_query('null', '$.string()');
+select jsonb_path_query('null', '$.string()', silent => true);
+select jsonb_path_query('[]', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()');
+select jsonb_path_query('{}', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+select jsonb_path_query('{}', '$.string()', silent => true);
+select jsonb_path_query('1.23', '$.string()');
+select jsonb_path_query('"1.23"', '$.string()');
+select jsonb_path_query('"1.23aaa"', '$.string()');
+select jsonb_path_query('1234', '$.string()');
+select jsonb_path_query('true', '$.string()');
+select jsonb_path_query('1234', '$.string().type()');
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].abs()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].floor()');
select jsonb_path_query('[0, 1, -2, -3.4, 5.6]', '$[*].ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 44275c9..8f07b71 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -69,6 +69,8 @@ select '(1).type()'::jsonpath;
select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
+select '$.boolean()'::jsonpath;
+select '$.string()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
select '$.bigint().integer().number().decimal()'::jsonpath;
select '$.decimal(4,2)'::jsonpath;
--
1.8.3.1
v5-0002-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchapplication/octet-stream; name=v5-0002-Implement-jsonpath-.date-.time-.time_tz-.timestam.patchDownload
From efacc1e1ed77bc7494c4dd106897c759291532ae Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Wed, 10 Jan 2024 13:14:49 +0530
Subject: [PATCH v5 2/3] Implement jsonpath .date(), .time(), .time_tz(),
.timestamp(), and .timestamp_tz() methods
This commit implements jsonpath .date(), .time(), .time_tz(),
.timestamp(), .timestamp_tz() methods. The JSON string representing
a valid date/time is converted to the specific date or time type
representation.
The changes use the infrastructure of the .datetime() method and
perform the datatype conversion as appropriate. Unlike the
.datetime() method, all these methods don't accept format templates
and use ISO DateTime formats instead. However, except the .date()
method, these methods take an optional precision to adjust the
fractional seconds.
Jeevan Chalke
---
doc/src/sgml/func.sgml | 130 ++++
src/backend/catalog/sql_features.txt | 14 +-
src/backend/utils/adt/jsonpath.c | 72 +-
src/backend/utils/adt/jsonpath_exec.c | 301 +++++++-
src/backend/utils/adt/jsonpath_gram.y | 34 +-
src/backend/utils/adt/jsonpath_scan.l | 5 +
src/include/utils/jsonpath.h | 5 +
src/test/regress/expected/jsonb_jsonpath.out | 1053 +++++++++++++++++++++++++-
src/test/regress/expected/jsonpath.out | 54 ++
src/test/regress/sql/jsonb_jsonpath.sql | 295 ++++++++
src/test/regress/sql/jsonpath.sql | 9 +
11 files changed, 1932 insertions(+), 40 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index b273af6..0080ee2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17820,6 +17820,136 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>date()</literal>
+ <returnvalue><replaceable>date</replaceable></returnvalue>
+ </para>
+ <para>
+ Date value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.date()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time()</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56"', '$.time()')</literal>
+ <returnvalue>"12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789"', '$.time(2)')</literal>
+ <returnvalue>"12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz()</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')</literal>
+ <returnvalue>"12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)')</literal>
+ <returnvalue>"12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp()</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')</literal>
+ <returnvalue>"2023-08-15T12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string, with
+ fractional seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz()</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')</literal>
+ <returnvalue>"2023-08-15T12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>object</replaceable> <literal>.</literal> <literal>keyvalue()</literal>
<returnvalue><replaceable>array</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 6a76579..aa80634 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -576,18 +576,18 @@ T863 SQL/JSON simplified accessor: single-quoted string literal as member access
T864 SQL/JSON simplified accessor NO
T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
-T867 SQL/JSON item method: date() NO
+T867 SQL/JSON item method: date() YES
T868 SQL/JSON item method: decimal() YES
T869 SQL/JSON item method: decimal() with precision and scale YES
T870 SQL/JSON item method: integer() YES
T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
-T873 SQL/JSON item method: time() NO
-T874 SQL/JSON item method: time_tz() NO
-T875 SQL/JSON item method: time precision NO
-T876 SQL/JSON item method: timestamp() NO
-T877 SQL/JSON item method: timestamp_tz() NO
-T878 SQL/JSON item method: timestamp precision NO
+T873 SQL/JSON item method: time() YES
+T874 SQL/JSON item method: time_tz() YES
+T875 SQL/JSON item method: time precision YES
+T876 SQL/JSON item method: timestamp() YES
+T877 SQL/JSON item method: timestamp_tz() YES
+T878 SQL/JSON item method: timestamp precision YES
T879 JSON in equality operations YES with jsonb
T880 JSON in grouping operations YES with jsonb
T881 JSON in ordering operations NO with jsonb, partially supported
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index 85b0b07..ba9537e 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -356,6 +356,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMinus:
case jpiExists:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
{
int32 arg = reserveSpaceForItemPointer(buf);
@@ -448,6 +452,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiNumber:
case jpiBigint:
case jpiInteger:
+ case jpiDate:
break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
@@ -770,6 +775,45 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
case jpiInteger:
appendStringInfoString(buf, ".integer()");
break;
+ case jpiDate:
+ appendStringInfoString(buf, ".date()");
+ break;
+ case jpiTime:
+ appendStringInfoString(buf, ".time(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimeTz:
+ appendStringInfoString(buf, ".time_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestamp:
+ appendStringInfoString(buf, ".timestamp(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestampTz:
+ appendStringInfoString(buf, ".timestamp_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
@@ -839,6 +883,16 @@ jspOperationName(JsonPathItemType type)
return "bigint";
case jpiInteger:
return "integer";
+ case jpiDate:
+ return "date";
+ case jpiTime:
+ return "time";
+ case jpiTimeTz:
+ return "time_tz";
+ case jpiTimestamp:
+ return "timestamp";
+ case jpiTimestampTz:
+ return "timestamp_tz";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -938,6 +992,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiNumber:
case jpiBigint:
case jpiInteger:
+ case jpiDate:
break;
case jpiString:
case jpiKey:
@@ -973,6 +1028,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiMinus:
case jpiFilter:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
read_int32(v->content.arg, base, pos);
break;
case jpiIndexArray:
@@ -1004,7 +1063,11 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMinus ||
v->type == jpiFilter ||
v->type == jpiExists ||
- v->type == jpiDatetime);
+ v->type == jpiDatetime ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
jspInitByBuffer(a, v->base, v->content.arg);
}
@@ -1059,7 +1122,12 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiNumber ||
v->type == jpiDecimal ||
v->type == jpiBigint ||
- v->type == jpiInteger);
+ v->type == jpiInteger ||
+ v->type == jpiDate ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
if (a)
jspInitByBuffer(a, v->base, v->nextPos);
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index a361c45..aa154f3 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1071,6 +1071,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
case jpiDatetime:
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -2037,11 +2042,15 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
/*
- * Implementation of the .datetime() method.
+ * Implementation of the .datetime() and related methods.
*
* 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.
+ *
+ * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
+ * have a format, so ISO format is used. However, except .date(), they all
+ * take an optional time precision.
*/
static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
@@ -2057,6 +2066,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
bool hasNext;
JsonPathExecResult res = jperNotFound;
JsonPathItem elem;
+ int32 time_precision = -1;
if (!(jb = getScalar(jb, jbvString)))
RETURN_ERROR(ereport(ERROR,
@@ -2074,7 +2084,11 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
collid = DEFAULT_COLLATION_OID;
- if (jsp->content.arg)
+ /*
+ * .datetime(template) has an argument, the rest of the methods don't have
+ * an argument. So we handle that separately.
+ */
+ if (jsp->type == jpiDatetime && jsp->content.arg)
{
text *template;
char *template_str;
@@ -2136,6 +2150,30 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
static text *fmt_txt[lengthof(fmt_str)] = {0};
int i;
+ /*
+ * Check for optional precision for methods other than .datetime() and
+ * .date()
+ */
+ if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
+ jsp->content.arg)
+ {
+ bool have_error;
+
+ jspGetArg(jsp, &elem);
+
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for %s argument",
+ jspOperationName(jsp->type));
+
+ time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
/* loop until datetime format fits */
for (i = 0; i < lengthof(fmt_str); i++)
{
@@ -2162,11 +2200,260 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
if (res == jperNotFound)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
- errhint("Use a datetime template argument to specify the input data format."))));
+ {
+ if (jsp->type == jpiDatetime)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("datetime format is not recognized: \"%s\"",
+ text_to_cstring(datetime)),
+ errhint("Use a datetime template argument to specify the input data format."))));
+ else
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("%s format is not recognized: \"%s\"",
+ jspOperationName(jsp->type), text_to_cstring(datetime)))));
+
+ }
+ }
+
+ /*
+ * parse_datetime() processes the entire input string per the template or
+ * ISO format and returns the Datum in best fitted datetime type. So, if
+ * this call is for a specific datatype, then we do the conversion here.
+ * Throw an error for incompatible types.
+ */
+ switch (jsp->type)
+ {
+ case jpiDatetime: /* Nothing to do for DATETIME */
+ break;
+ case jpiDate:
+ {
+ /* Convert result type to date */
+ switch (typid)
+ {
+ case DATEOID: /* Nothing to do for DATE */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("date format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_date,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_date,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = DATEOID;
+ }
+ break;
+ case jpiTime:
+ {
+ /* Convert result type to time without time zone */
+ switch (typid)
+ {
+ case TIMEOID: /* Nothing to do for TIME */
+ break;
+ case DATEOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMETZOID:
+ value = DirectFunctionCall1(timetz_time,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_time,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_time,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeADT result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(false,
+ time_precision);
+ result = DatumGetTimeADT(value);
+ AdjustTimeForTypmod(&result, time_precision);
+ value = TimeADTGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMEOID;
+ }
+ break;
+ case jpiTimeTz:
+ {
+ /* Convert result type to time with time zone */
+ switch (typid)
+ {
+ case TIMETZOID: /* Nothing to do for TIMETZ */
+ break;
+ case DATEOID:
+ case TIMESTAMPOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID:
+ value = DirectFunctionCall1(time_timetz,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timetz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeTzADT *result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(true,
+ time_precision);
+ result = DatumGetTimeTzADTP(value);
+ AdjustTimeForTypmod(&result->time, time_precision);
+ value = TimeTzADTPGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMETZOID;
+ }
+ break;
+ case jpiTimestamp:
+ {
+ /* Convert result type to timestamp without time zone */
+ switch (typid)
+ {
+ case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamp,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timestamp,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(false,
+ time_precision);
+ result = DatumGetTimestamp(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPOID;
+ }
+ break;
+ case jpiTimestampTz:
+ {
+ /* Convert result type to timestamp with time zone */
+ switch (typid)
+ {
+ case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamptz,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_timestamptz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(true,
+ time_precision);
+ result = DatumGetTimestampTz(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampTzGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPTZOID;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
pfree(datetime);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 24c3104..79c6371 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -82,7 +82,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
-%token <str> DATETIME_P
+%token <str> DATETIME_P DATE_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
%type <result> result
@@ -90,6 +90,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
datetime_template opt_datetime_template csv_elem
+ datetime_method datetime_precision opt_datetime_precision
%type <elems> accessor_expr csv_list opt_csv_list
@@ -249,8 +250,7 @@ accessor_op:
| array_accessor { $$ = $1; }
| '.' any_path { $$ = $2; }
| '.' method '(' ')' { $$ = makeItemType($2); }
- | '.' DATETIME_P '(' opt_datetime_template ')'
- { $$ = makeItemUnary(jpiDatetime, $4); }
+ | '.' datetime_method { $$ = $2; }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
| '.' DECIMAL_P '(' opt_csv_list ')'
{
@@ -268,6 +268,19 @@ accessor_op:
}
;
+datetime_method:
+ DATETIME_P '(' opt_datetime_template ')'
+ { $$ = makeItemUnary(jpiDatetime, $3); }
+ | TIME_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTime, $3); }
+ | TIME_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimeTz, $3); }
+ | TIMESTAMP_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestamp, $3); }
+ | TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestampTz, $3); }
+ ;
+
csv_elem:
INT_P
{ $$ = makeItemNumeric(&$1); }
@@ -287,6 +300,15 @@ opt_csv_list:
| /* EMPTY */ { $$ = NULL; }
;
+datetime_precision:
+ INT_P { $$ = makeItemNumeric(&$1); }
+ ;
+
+opt_datetime_precision:
+ datetime_precision { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
datetime_template:
STRING_P { $$ = makeItemString(&$1); }
;
@@ -329,6 +351,11 @@ key_name:
| DECIMAL_P
| BIGINT_P
| INTEGER_P
+ | DATE_P
+ | TIME_P
+ | TIME_TZ_P
+ | TIMESTAMP_P
+ | TIMESTAMP_TZ_P
;
method:
@@ -342,6 +369,7 @@ method:
| NUMBER_P { $$ = jpiNumber; }
| BIGINT_P { $$ = jpiBigint; }
| INTEGER_P { $$ = jpiInteger; }
+ | DATE_P { $$ = jpiDate; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 07d229d..d3e9401 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -401,10 +401,12 @@ static const JsonPathKeyword keywords[] = {
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
+ { 4, false, DATE_P, "date"},
{ 4, false, FLAG_P, "flag"},
{ 4, false, LAST_P, "last"},
{ 4, true, NULL_P, "null"},
{ 4, false, SIZE_P, "size"},
+ { 4, false, TIME_P, "time"},
{ 4, true, TRUE_P, "true"},
{ 4, false, TYPE_P, "type"},
{ 4, false, WITH_P, "with"},
@@ -419,10 +421,13 @@ static const JsonPathKeyword keywords[] = {
{ 7, false, CEILING_P, "ceiling"},
{ 7, false, DECIMAL_P, "decimal"},
{ 7, false, INTEGER_P, "integer"},
+ { 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
+ { 9, false, TIMESTAMP_P, "timestamp"},
{ 10,false, LIKE_REGEX_P, "like_regex"},
+ { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
/* Check if current scanstring value is a keyword */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index ef81d1d..d0c88d5 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -106,6 +106,11 @@ typedef enum JsonPathItemType
jpiDecimal, /* .decimal() item method */
jpiBigint, /* .bigint() item method */
jpiInteger, /* .integer() item method */
+ jpiDate, /* .date() item method */
+ jpiTime, /* .time() item method */
+ jpiTimeTz, /* .time_tz() item method */
+ jpiTimestamp, /* .timestamp() item method */
+ jpiTimestampTz, /* .timestamp_tz() item method */
} JsonPathItemType;
/* XQuery regex mode flags for LIKE_REGEX predicate */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 60af215..b77289c 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2266,7 +2266,443 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
ERROR: unmatched format character "T"
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
ERROR: unmatched format character "T"
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('true', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('1', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('[]', '$.date()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('{}', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.date()');
+ERROR: date format is not recognized: "bogus"
+select jsonb '"2023-08-15"' @? '$.date()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+ jsonb_path_query
+------------------
+ "date"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+ERROR: date format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+ERROR: date format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ERROR: syntax error at or near "2" of jsonpath input
+LINE 1: select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ ^
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('true', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('1', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('[]', '$.time()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('{}', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time()');
+ERROR: time format is not recognized: "bogus"
+select jsonb '"12:34:56"' @? '$.time()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+ jsonb_path_query
+--------------------------
+ "time without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+ERROR: time format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+ERROR: time precision of jsonpath item method .time() is out of range for type integer
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+ jsonb_path_query
+------------------
+ "12:34:57"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+ jsonb_path_query
+------------------
+ "12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+WARNING: TIME(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+WARNING: TIME(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------
+ "12:34:56.789012"
+(1 row)
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('true', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('1', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.time_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "bogus"
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+ jsonb_path_query
+-----------------------
+ "time with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15 12:34:56"
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+ERROR: time precision of jsonpath item method .time_tz() is out of range for type integer
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+ jsonb_path_query
+------------------
+ "12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+ jsonb_path_query
+---------------------
+ "12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+WARNING: TIME(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+WARNING: TIME(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------------
+ "12:34:56.789012+05:30"
+(1 row)
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+ jsonb_path_query
+-------------------------------
+ "timestamp without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+ERROR: time precision of jsonpath item method .timestamp() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:57"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+ jsonb_path_query
+--------------------------
+ "2023-08-15T12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+WARNING: TIMESTAMP(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+WARNING: TIMESTAMP(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------
+ "2023-08-15T12:34:56.789012"
+(1 row)
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+ jsonb_path_query
+----------------------------
+ "timestamp with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T07:00:00+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+ERROR: time precision of jsonpath item method .timestamp_tz() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+ jsonb_path_query
+--------------------------------
+ "2023-08-15T12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+WARNING: TIMESTAMP(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+WARNING: TIMESTAMP(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------------
+ "2023-08-15T12:34:56.789012+05:30"
+(1 row)
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "07:04:56+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+00:00"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2332,6 +2768,36 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "17:04:56+10:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T02:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -2397,6 +2863,30 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "00:04:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
jsonb_path_query
------------------
@@ -2553,28 +3043,123 @@ select jsonb_path_query_tz(
"2017-03-10T01:02:03+04:00"
(2 rows)
--- time comparison
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
-select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
- jsonb_path_query_tz
----------------------
- "12:35:00"
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-10T00:00:00"
+ "2017-03-10T03:00:00+03:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10T00:00:00"
+ "2017-03-10T12:34:56"
+ "2017-03-10T03:00:00+03:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-09"
+ "2017-03-10T01:02:03+04:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+-- time comparison
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
"12:35:00+00:00"
(2 rows)
@@ -2598,6 +3183,112 @@ select jsonb_path_query_tz(
"13:35:00+01:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+ jsonb_path_query
+------------------
+ "12:35:00.12"
+ "12:36:00.11"
+ "12:35:00.12"
+ "13:35:00.12"
+(4 rows)
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -2644,6 +3335,110 @@ select jsonb_path_query_tz(
"10:35:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00"
+ "12:35:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+ jsonb_path_query
+---------------------
+ "12:35:00.12+01:00"
+ "12:36:00.11+01:00"
+ "12:35:00.12-02:00"
+ "12:35:00.12+00:00"
+ "11:35:00.12+00:00"
+(5 rows)
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2691,6 +3486,111 @@ select jsonb_path_query_tz(
"2017-03-10"
(3 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00+01:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T13:35:00+01:00"
+ "2017-03-10T12:35:00-01:00"
+ "2017-03-11"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+ jsonb_path_query
+--------------------------
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T12:36:00.11"
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T13:35:00.1"
+ "2017-03-11T00:00:00"
+(5 rows)
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2740,6 +3640,117 @@ select jsonb_path_query_tz(
"2017-03-10"
(4 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T12:35:00"
+ "2017-03-11"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+ jsonb_path_query
+--------------------------------
+ "2017-03-10T12:35:00.12+01:00"
+ "2017-03-10T12:36:00.11+01:00"
+ "2017-03-10T12:35:00.12-02:00"
+ "2017-03-10T12:35:00.12+00:00"
+ "2017-03-11T00:00:00+00:00"
+(5 rows)
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
jsonb_path_query
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index 15fb717..b47b2e9 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -417,6 +417,60 @@ select '$.datetime("datetime template")'::jsonpath;
$.datetime("datetime template")
(1 row)
+select '$.date()'::jsonpath;
+ jsonpath
+----------
+ $.date()
+(1 row)
+
+select '$.time()'::jsonpath;
+ jsonpath
+----------
+ $.time()
+(1 row)
+
+select '$.time(6)'::jsonpath;
+ jsonpath
+-----------
+ $.time(6)
+(1 row)
+
+select '$.time_tz()'::jsonpath;
+ jsonpath
+-------------
+ $.time_tz()
+(1 row)
+
+select '$.time_tz(4)'::jsonpath;
+ jsonpath
+--------------
+ $.time_tz(4)
+(1 row)
+
+select '$.timestamp()'::jsonpath;
+ jsonpath
+---------------
+ $.timestamp()
+(1 row)
+
+select '$.timestamp(2)'::jsonpath;
+ jsonpath
+----------------
+ $.timestamp(2)
+(1 row)
+
+select '$.timestamp_tz()'::jsonpath;
+ jsonpath
+------------------
+ $.timestamp_tz()
+(1 row)
+
+select '$.timestamp_tz(0)'::jsonpath;
+ jsonpath
+-------------------
+ $.timestamp_tz(0)
+(1 row)
+
select '$ ? (@ starts with "abc")'::jsonpath;
jsonpath
-------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index 4006a75..eb96c3b 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -505,8 +505,138 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+select jsonb_path_query('true', '$.date()');
+select jsonb_path_query('1', '$.date()');
+select jsonb_path_query('[]', '$.date()');
+select jsonb_path_query('[]', 'strict $.date()');
+select jsonb_path_query('{}', '$.date()');
+select jsonb_path_query('"bogus"', '$.date()');
+
+select jsonb '"2023-08-15"' @? '$.date()';
+select jsonb_path_query('"2023-08-15"', '$.date()');
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+select jsonb_path_query('true', '$.time()');
+select jsonb_path_query('1', '$.time()');
+select jsonb_path_query('[]', '$.time()');
+select jsonb_path_query('[]', 'strict $.time()');
+select jsonb_path_query('{}', '$.time()');
+select jsonb_path_query('"bogus"', '$.time()');
+
+select jsonb '"12:34:56"' @? '$.time()';
+select jsonb_path_query('"12:34:56"', '$.time()');
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+select jsonb_path_query('true', '$.time_tz()');
+select jsonb_path_query('1', '$.time_tz()');
+select jsonb_path_query('[]', '$.time_tz()');
+select jsonb_path_query('[]', 'strict $.time_tz()');
+select jsonb_path_query('{}', '$.time_tz()');
+select jsonb_path_query('"bogus"', '$.time_tz()');
+
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+select jsonb_path_query('true', '$.timestamp()');
+select jsonb_path_query('1', '$.timestamp()');
+select jsonb_path_query('[]', '$.timestamp()');
+select jsonb_path_query('[]', 'strict $.timestamp()');
+select jsonb_path_query('{}', '$.timestamp()');
+select jsonb_path_query('"bogus"', '$.timestamp()');
+
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+select jsonb_path_query('true', '$.timestamp_tz()');
+select jsonb_path_query('1', '$.timestamp_tz()');
+select jsonb_path_query('[]', '$.timestamp_tz()');
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+select jsonb_path_query('{}', '$.timestamp_tz()');
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -522,6 +652,12 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -537,6 +673,11 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
select jsonb_path_query('"2017-03-10"', '$.datetime()');
select jsonb_path_query('"2017-03-10 12:34:56"', '$.datetime().type()');
@@ -579,6 +720,34 @@ select jsonb_path_query_tz(
'["2017-03-10", "2017-03-11", "2017-03-09", "12:34:56", "01:02:03+04", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
'$[*].datetime() ? (@ < "10.03.2017".datetime("dd.mm.yyyy"))');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -599,6 +768,38 @@ select jsonb_path_query_tz(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
'$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -619,6 +820,37 @@ select jsonb_path_query_tz(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ < "12:35 +1".datetime("HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -639,6 +871,37 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35".datetime("dd.mm.yyyy HH24:MI"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -659,6 +922,38 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35 +1".datetime("dd.mm.yyyy HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 1f25f89..44275c9 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -75,6 +75,15 @@ select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
+select '$.date()'::jsonpath;
+select '$.time()'::jsonpath;
+select '$.time(6)'::jsonpath;
+select '$.time_tz()'::jsonpath;
+select '$.time_tz(4)'::jsonpath;
+select '$.timestamp()'::jsonpath;
+select '$.timestamp(2)'::jsonpath;
+select '$.timestamp_tz()'::jsonpath;
+select '$.timestamp_tz(0)'::jsonpath;
select '$ ? (@ starts with "abc")'::jsonpath;
select '$ ? (@ starts with $var)'::jsonpath;
--
1.8.3.1
v5-0001-Implement-jsonpath-.number-.decimal-precision-sca.patchapplication/octet-stream; name=v5-0001-Implement-jsonpath-.number-.decimal-precision-sca.patchDownload
From 403ab92764d17cf77ddbc94f096655c7242a8a1a Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Wed, 10 Jan 2024 13:14:48 +0530
Subject: [PATCH v5 1/3] Implement jsonpath .number(), .decimal([precision [,
scale]]), .bigint(), and .integer() methods
This commit implements jsonpath .number(), .decimal() with optional
precision and scale, .bigint(), and .integer() methods. The JSON
string or a numeric value is converted to the numeric, numeric,
bigint, and int4 type representation respectively. If precision and
scale are provided for .decimal(), then it is converted to the
equivalent numerictypmod and applied to the numeric number.
Jeevan Chalke.
---
doc/src/sgml/func.sgml | 56 +++
src/backend/catalog/sql_features.txt | 10 +-
src/backend/utils/adt/jsonpath.c | 52 ++-
src/backend/utils/adt/jsonpath_exec.c | 266 +++++++++++++
src/backend/utils/adt/jsonpath_gram.y | 45 ++-
src/backend/utils/adt/jsonpath_scan.l | 4 +
src/include/utils/jsonpath.h | 4 +
src/test/regress/expected/jsonb_jsonpath.out | 534 +++++++++++++++++++++++++++
src/test/regress/expected/jsonpath.out | 12 +
src/test/regress/sql/jsonb_jsonpath.sql | 133 +++++++
src/test/regress/sql/jsonpath.sql | 2 +
11 files changed, 1108 insertions(+), 10 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index de78d58..b273af6 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17733,6 +17733,62 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>number()</literal>
+ <returnvalue><replaceable>numeric</replaceable></returnvalue>
+ </para>
+ <para>
+ Numeric value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "123.45"}', '$.len.number()')</literal>
+ <returnvalue>123.45</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>decimal( [ <replaceable>precision</replaceable> [ , <replaceable>scale</replaceable> ] ] )</literal>
+ <returnvalue><replaceable>decimal</replaceable></returnvalue>
+ </para>
+ <para>
+ Rounded decimal value converted from a JSON number or string. <literal>precision</literal> and <literal>scale</literal> must be integer values.
+ </para>
+ <para>
+ <literal>jsonb_path_query('1234.5678', '$.decimal(6, 2)')</literal>
+ <returnvalue>1234.57</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
+ <returnvalue><replaceable>bigint</replaceable></returnvalue>
+ </para>
+ <para>
+ Big integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')</literal>
+ <returnvalue>9876543219</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>integer()</literal>
+ <returnvalue><replaceable>integer</replaceable></returnvalue>
+ </para>
+ <para>
+ Integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "12345"}', '$.len.integer()')</literal>
+ <returnvalue>12345</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>string</replaceable> <literal>.</literal> <literal>datetime()</literal>
<returnvalue><replaceable>datetime_type</replaceable></returnvalue>
(see note)
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 80c40ea..6a76579 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -574,13 +574,13 @@ T861 SQL/JSON simplified accessor: case-sensitive JSON member accessor NO
T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
-T865 SQL/JSON item method: bigint() NO
+T865 SQL/JSON item method: bigint() YES
T866 SQL/JSON item method: boolean() NO
T867 SQL/JSON item method: date() NO
-T868 SQL/JSON item method: decimal() NO
-T869 SQL/JSON item method: decimal() with precision and scale NO
-T870 SQL/JSON item method: integer() NO
-T871 SQL/JSON item method: number() NO
+T868 SQL/JSON item method: decimal() YES
+T869 SQL/JSON item method: decimal() with precision and scale YES
+T870 SQL/JSON item method: integer() YES
+T871 SQL/JSON item method: number() YES
T872 SQL/JSON item method: string() NO
T873 SQL/JSON item method: time() NO
T874 SQL/JSON item method: time_tz() NO
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index d02c03e..85b0b07 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -295,6 +295,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiDiv:
case jpiMod:
case jpiStartsWith:
+ case jpiDecimal:
{
/*
* First, reserve place for left/right arg's positions, then
@@ -444,6 +445,9 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiCeiling:
case jpiDouble:
case jpiKeyValue:
+ case jpiNumber:
+ case jpiBigint:
+ case jpiInteger:
break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
@@ -742,6 +746,30 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
if (printBracketes)
appendStringInfoChar(buf, ')');
break;
+ case jpiNumber:
+ appendStringInfoString(buf, ".number()");
+ break;
+ case jpiDecimal:
+ appendStringInfoString(buf, ".decimal(");
+ if (v->content.args.left)
+ {
+ jspGetLeftArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ if (v->content.args.right)
+ {
+ appendStringInfoChar(buf, ',');
+ jspGetRightArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiBigint:
+ appendStringInfoString(buf, ".bigint()");
+ break;
+ case jpiInteger:
+ appendStringInfoString(buf, ".integer()");
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
@@ -803,6 +831,14 @@ jspOperationName(JsonPathItemType type)
return "starts with";
case jpiLikeRegex:
return "like_regex";
+ case jpiNumber:
+ return "number";
+ case jpiDecimal:
+ return "decimal";
+ case jpiBigint:
+ return "bigint";
+ case jpiInteger:
+ return "integer";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -899,6 +935,9 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiDouble:
case jpiKeyValue:
case jpiLast:
+ case jpiNumber:
+ case jpiBigint:
+ case jpiInteger:
break;
case jpiString:
case jpiKey:
@@ -923,6 +962,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiDiv:
case jpiMod:
case jpiStartsWith:
+ case jpiDecimal:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
break;
@@ -1015,7 +1055,11 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiKeyValue ||
v->type == jpiLast ||
v->type == jpiStartsWith ||
- v->type == jpiLikeRegex);
+ v->type == jpiLikeRegex ||
+ v->type == jpiNumber ||
+ v->type == jpiDecimal ||
+ v->type == jpiBigint ||
+ v->type == jpiInteger);
if (a)
jspInitByBuffer(a, v->base, v->nextPos);
@@ -1041,7 +1085,8 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
- v->type == jpiStartsWith);
+ v->type == jpiStartsWith ||
+ v->type == jpiDecimal);
jspInitByBuffer(a, v->base, v->content.args.left);
}
@@ -1062,7 +1107,8 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
- v->type == jpiStartsWith);
+ v->type == jpiStartsWith ||
+ v->type == jpiDecimal);
jspInitByBuffer(a, v->base, v->content.args.right);
}
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index ac16f5c..a361c45 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1110,6 +1110,272 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiNumber:
+ case jpiDecimal:
+ {
+ JsonbValue jbv;
+ Numeric num;
+ char *numstr = NULL;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ num = jb->val.numeric;
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->type == jpiDecimal)
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as number */
+ Datum datum;
+ bool noerr;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
+
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(datum);
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ /*
+ * If we have arguments, then they must be the precision and
+ * optional scale used in .decimal(). Convert them to the
+ * typmod equivalent and then truncate the numeric value per
+ * this typmod details.
+ */
+ if (jsp->type == jpiDecimal && jsp->content.args.left)
+ {
+ Datum numdatum;
+ Datum dtypmod;
+ int32 precision;
+ int32 scale = 0;
+ bool have_error;
+ bool noerr;
+ ArrayType *arrtypmod;
+ Datum datums[2];
+ char pstr[12]; /* sign, 10 digits and '\0' */
+ char sstr[12]; /* sign, 10 digits and '\0' */
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ jspGetLeftArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() precision");
+
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("precision of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->content.args.right)
+ {
+ jspGetRightArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() scale");
+
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("scale of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
+ /*
+ * numerictypmodin() takes the precision and scale in the
+ * form of CString arrays.
+ */
+ pg_ltoa(precision, pstr);
+ datums[0] = CStringGetDatum(pstr);
+ pg_ltoa(scale, sstr);
+ datums[1] = CStringGetDatum(sstr);
+ arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
+
+ dtypmod = DirectFunctionCall1(numerictypmodin,
+ PointerGetDatum(arrtypmod));
+
+ /* Convert numstr to Numeric with typmod */
+ Assert(numstr != NULL);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, dtypmod,
+ (Node *) &escontext,
+ &numdatum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(numdatum);
+ pfree(arrtypmod);
+ }
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = num;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiBigint:
+ {
+ JsonbValue jbv;
+ Datum datum;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ bool have_error;
+ int64 val;
+
+ val = numeric_int8_opt_error(jb->val.numeric, &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ jspOperationName(jsp->type)))));
+
+ datum = Int64GetDatum(val);
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as bigint */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+ bool noerr;
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiInteger:
+ {
+ JsonbValue jbv;
+ Datum datum;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ bool have_error;
+ int32 val;
+
+ val = numeric_int4_opt_error(jb->val.numeric, &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ datum = Int32GetDatum(val);
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as integer */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+ bool noerr;
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
default:
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 5e4eb52..24c3104 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -81,6 +81,7 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> LESS_P LESSEQUAL_P EQUAL_P NOTEQUAL_P GREATEREQUAL_P GREATER_P
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
+%token <str> NUMBER_P DECIMAL_P BIGINT_P INTEGER_P
%token <str> DATETIME_P
%type <result> result
@@ -88,9 +89,9 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template
+ datetime_template opt_datetime_template csv_elem
-%type <elems> accessor_expr
+%type <elems> accessor_expr csv_list opt_csv_list
%type <indexs> index_list
@@ -251,6 +252,39 @@ accessor_op:
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
+ | '.' DECIMAL_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiDecimal, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
+ else if (list_length($4) == 2)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".decimal() can only have an optional precision[,scale].")));
+ }
+ ;
+
+csv_elem:
+ INT_P
+ { $$ = makeItemNumeric(&$1); }
+ | '+' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
+ | '-' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
+ ;
+
+csv_list:
+ csv_elem { $$ = list_make1($1); }
+ | csv_list ',' csv_elem { $$ = lappend($1, $3); }
+ ;
+
+opt_csv_list:
+ csv_list { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
;
datetime_template:
@@ -291,6 +325,10 @@ key_name:
| WITH_P
| LIKE_REGEX_P
| FLAG_P
+ | NUMBER_P
+ | DECIMAL_P
+ | BIGINT_P
+ | INTEGER_P
;
method:
@@ -301,6 +339,9 @@ method:
| DOUBLE_P { $$ = jpiDouble; }
| CEILING_P { $$ = jpiCeiling; }
| KEYVALUE_P { $$ = jpiKeyValue; }
+ | NUMBER_P { $$ = jpiNumber; }
+ | BIGINT_P { $$ = jpiBigint; }
+ | INTEGER_P { $$ = jpiInteger; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 757cd95..07d229d 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -410,11 +410,15 @@ static const JsonPathKeyword keywords[] = {
{ 4, false, WITH_P, "with"},
{ 5, true, FALSE_P, "false"},
{ 5, false, FLOOR_P, "floor"},
+ { 6, false, BIGINT_P, "bigint"},
{ 6, false, DOUBLE_P, "double"},
{ 6, false, EXISTS_P, "exists"},
+ { 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, DECIMAL_P, "decimal"},
+ { 7, false, INTEGER_P, "integer"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 9d55c25..ef81d1d 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -102,6 +102,10 @@ typedef enum JsonPathItemType
jpiLast, /* LAST array subscript */
jpiStartsWith, /* STARTS WITH predicate */
jpiLikeRegex, /* LIKE_REGEX predicate */
+ jpiNumber, /* .number() item method */
+ jpiDecimal, /* .decimal() item method */
+ jpiBigint, /* .bigint() item method */
+ jpiInteger, /* .integer() item method */
} JsonPathItemType;
/* XQuery regex mode flags for LIKE_REGEX predicate */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 6659bc9..60af215 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1517,6 +1517,540 @@ select jsonb_path_query('"-inf"', '$.double()', silent => true);
------------------
(0 rows)
+select jsonb_path_query('null', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.bigint()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"1.23"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('1e1000', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"nan"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"NaN"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"-inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1.23', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('1.83', '$.bigint()');
+ jsonb_path_query
+------------------
+ 2
+(1 row)
+
+select jsonb_path_query('1234567890123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"+123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.bigint() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.integer()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"1.23"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('1e1000', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"nan"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"NaN"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"-inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1.23', '$.integer()');
+ jsonb_path_query
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('1.83', '$.integer()');
+ jsonb_path_query
+------------------
+ 2
+(1 row)
+
+select jsonb_path_query('12345678901', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"12345678901"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"+123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.integer() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+select jsonb_path_query('null', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.number()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.number()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.number() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('null', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.decimal()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.decimal()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+ jsonb_path_query
+------------------
+ 12345.7
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+ERROR: NUMERIC precision 0 must be between 1 and 1000
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+ERROR: NUMERIC precision 1001 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ 1200
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+ERROR: NUMERIC precision -6 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+ERROR: NUMERIC scale -1001 must be between -1000 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+ERROR: NUMERIC scale 1001 must be between -1000 and 1000
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ -1200
+(1 row)
+
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+ jsonb_path_query
+------------------
+ 0.01
+(1 row)
+
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+ jsonb_path_query
+------------------
+ 0.0012
+(1 row)
+
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+ jsonb_path_query
+------------------
+ 0
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
+ERROR: precision of jsonpath item method .decimal() is out of range for type integer
+select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+ERROR: scale of jsonpath item method .decimal() is out of range for type integer
select jsonb_path_query('{}', '$.abs()');
ERROR: jsonpath item method .abs() can only be applied to a numeric value
select jsonb_path_query('true', '$.floor()');
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index eeffb38..15fb717 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -387,6 +387,18 @@ select '$.double().floor().ceiling().abs()'::jsonpath;
$.double().floor().ceiling().abs()
(1 row)
+select '$.bigint().integer().number().decimal()'::jsonpath;
+ jsonpath
+-----------------------------------------
+ $.bigint().integer().number().decimal()
+(1 row)
+
+select '$.decimal(4,2)'::jsonpath;
+ jsonpath
+----------------
+ $.decimal(4,2)
+(1 row)
+
select '$.keyvalue().key'::jsonpath;
jsonpath
--------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e0ce509..4006a75 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -320,6 +320,139 @@ select jsonb_path_query('"-inf"', '$.double()');
select jsonb_path_query('"inf"', '$.double()', silent => true);
select jsonb_path_query('"-inf"', '$.double()', silent => true);
+select jsonb_path_query('null', '$.bigint()');
+select jsonb_path_query('true', '$.bigint()');
+select jsonb_path_query('null', '$.bigint()', silent => true);
+select jsonb_path_query('true', '$.bigint()', silent => true);
+select jsonb_path_query('[]', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()');
+select jsonb_path_query('{}', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+select jsonb_path_query('"1.23"', '$.bigint()');
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+select jsonb_path_query('1e1000', '$.bigint()');
+select jsonb_path_query('"nan"', '$.bigint()');
+select jsonb_path_query('"NaN"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()');
+select jsonb_path_query('"-inf"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+select jsonb_path_query('123', '$.bigint()');
+select jsonb_path_query('"123"', '$.bigint()');
+select jsonb_path_query('1.23', '$.bigint()');
+select jsonb_path_query('1.83', '$.bigint()');
+select jsonb_path_query('1234567890123', '$.bigint()');
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+select jsonb_path_query('"+123"', '$.bigint()');
+select jsonb_path_query('-123', '$.bigint()');
+select jsonb_path_query('"-123"', '$.bigint()');
+select jsonb_path_query('123', '$.bigint() * 2');
+
+select jsonb_path_query('null', '$.integer()');
+select jsonb_path_query('true', '$.integer()');
+select jsonb_path_query('null', '$.integer()', silent => true);
+select jsonb_path_query('true', '$.integer()', silent => true);
+select jsonb_path_query('[]', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()');
+select jsonb_path_query('{}', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+select jsonb_path_query('{}', '$.integer()', silent => true);
+select jsonb_path_query('"1.23"', '$.integer()');
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+select jsonb_path_query('1e1000', '$.integer()');
+select jsonb_path_query('"nan"', '$.integer()');
+select jsonb_path_query('"NaN"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()');
+select jsonb_path_query('"-inf"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+select jsonb_path_query('123', '$.integer()');
+select jsonb_path_query('"123"', '$.integer()');
+select jsonb_path_query('1.23', '$.integer()');
+select jsonb_path_query('1.83', '$.integer()');
+select jsonb_path_query('12345678901', '$.integer()');
+select jsonb_path_query('"12345678901"', '$.integer()');
+select jsonb_path_query('"+123"', '$.integer()');
+select jsonb_path_query('-123', '$.integer()');
+select jsonb_path_query('"-123"', '$.integer()');
+select jsonb_path_query('123', '$.integer() * 2');
+
+select jsonb_path_query('null', '$.number()');
+select jsonb_path_query('true', '$.number()');
+select jsonb_path_query('null', '$.number()', silent => true);
+select jsonb_path_query('true', '$.number()', silent => true);
+select jsonb_path_query('[]', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()');
+select jsonb_path_query('{}', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+select jsonb_path_query('{}', '$.number()', silent => true);
+select jsonb_path_query('1.23', '$.number()');
+select jsonb_path_query('"1.23"', '$.number()');
+select jsonb_path_query('"1.23aaa"', '$.number()');
+select jsonb_path_query('1e1000', '$.number()');
+select jsonb_path_query('"nan"', '$.number()');
+select jsonb_path_query('"NaN"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()');
+select jsonb_path_query('"-inf"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+select jsonb_path_query('123', '$.number()');
+select jsonb_path_query('"123"', '$.number()');
+select jsonb_path_query('12345678901234567890', '$.number()');
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+select jsonb_path_query('"+12.3"', '$.number()');
+select jsonb_path_query('-12.3', '$.number()');
+select jsonb_path_query('"-12.3"', '$.number()');
+select jsonb_path_query('12.3', '$.number() * 2');
+
+select jsonb_path_query('null', '$.decimal()');
+select jsonb_path_query('true', '$.decimal()');
+select jsonb_path_query('null', '$.decimal()', silent => true);
+select jsonb_path_query('true', '$.decimal()', silent => true);
+select jsonb_path_query('[]', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()');
+select jsonb_path_query('{}', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+select jsonb_path_query('1.23', '$.decimal()');
+select jsonb_path_query('"1.23"', '$.decimal()');
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+select jsonb_path_query('1e1000', '$.decimal()');
+select jsonb_path_query('"nan"', '$.decimal()');
+select jsonb_path_query('"NaN"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()');
+select jsonb_path_query('"-inf"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+select jsonb_path_query('123', '$.decimal()');
+select jsonb_path_query('"123"', '$.decimal()');
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+select jsonb_path_query('"+12.3"', '$.decimal()');
+select jsonb_path_query('-12.3', '$.decimal()');
+select jsonb_path_query('"-12.3"', '$.decimal()');
+select jsonb_path_query('12.3', '$.decimal() * 2');
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
+select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+
select jsonb_path_query('{}', '$.abs()');
select jsonb_path_query('true', '$.floor()');
select jsonb_path_query('"1.2"', '$.ceiling()');
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 56e0bef..1f25f89 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -70,6 +70,8 @@ select '1.2.type()'::jsonpath;
select '"aaa".type()'::jsonpath;
select 'true.type()'::jsonpath;
select '$.double().floor().ceiling().abs()'::jsonpath;
+select '$.bigint().integer().number().decimal()'::jsonpath;
+select '$.decimal(4,2)'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
--
1.8.3.1
Attached are two small fixup patches for your patch set.
In the first one, I simplified the grammar for the .decimal() method.
It seemed a bit overkill to build a whole list structure when all we
need are 0, 1, or 2 arguments.
Per SQL standard, the precision and scale arguments are unsigned
integers, so unary plus and minus signs are not supported. So my patch
removes that support, but I didn't adjust the regression tests for that.
Also note that in your 0002 patch, the datetime precision is similarly
unsigned, so that's consistent.
By the way, in your 0002 patch, don't see the need for the separate
datetime_method grammar rule. You can fold that into accessor_op.
Overall, I think it would be better if you combined all three of these
patches into one. Right now, you have arranged these as incremental
features, and as a result of that, the additions to the JsonPathItemType
enum and the grammar keywords etc. are ordered in the way you worked on
these features, I guess. It would be good to maintain a bit of sanity
to put all of this together and order all the enums and everything else
for example in the order they are in the sql_features.txt file (which is
alphabetical, I suppose). At this point I suspect we'll end up
committing this whole feature set together anyway, so we might as well
organize it that way.
Attachments:
0001-fixup-Implement-jsonpath-.number-.decimal-pr.patch.nocfbottext/plain; charset=UTF-8; name=0001-fixup-Implement-jsonpath-.number-.decimal-pr.patch.nocfbotDownload
From 81e330a243d85dff7f64adf17815258e2764ea01 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 15 Jan 2024 14:11:36 +0100
Subject: [PATCH 1/2] fixup! Implement jsonpath .number(), .decimal([precision
[, scale]]), .bigint(), and .integer() methods
---
src/backend/utils/adt/jsonpath_gram.y | 43 +++++----------------------
1 file changed, 8 insertions(+), 35 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 24c31047ffd..9c06c3f6cb9 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -89,9 +89,9 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template csv_elem
+ datetime_template opt_datetime_template
-%type <elems> accessor_expr csv_list opt_csv_list
+%type <elems> accessor_expr
%type <indexs> index_list
@@ -252,39 +252,12 @@ accessor_op:
| '.' DATETIME_P '(' opt_datetime_template ')'
{ $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
- | '.' DECIMAL_P '(' opt_csv_list ')'
- {
- if (list_length($4) == 0)
- $$ = makeItemBinary(jpiDecimal, NULL, NULL);
- else if (list_length($4) == 1)
- $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
- else if (list_length($4) == 2)
- $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
- else
- ereturn(escontext, false,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("invalid input syntax for type %s", "jsonpath"),
- errdetail(".decimal() can only have an optional precision[,scale].")));
- }
- ;
-
-csv_elem:
- INT_P
- { $$ = makeItemNumeric(&$1); }
- | '+' INT_P %prec UMINUS
- { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
- | '-' INT_P %prec UMINUS
- { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
- ;
-
-csv_list:
- csv_elem { $$ = list_make1($1); }
- | csv_list ',' csv_elem { $$ = lappend($1, $3); }
- ;
-
-opt_csv_list:
- csv_list { $$ = $1; }
- | /* EMPTY */ { $$ = NULL; }
+ | '.' DECIMAL_P '(' ')'
+ { $$ = makeItemBinary(jpiDecimal, NULL, NULL); }
+ | '.' DECIMAL_P '(' INT_P ')'
+ { $$ = makeItemBinary(jpiDecimal, makeItemNumeric(&$4), NULL); }
+ | '.' DECIMAL_P '(' INT_P ',' INT_P ')'
+ { $$ = makeItemBinary(jpiDecimal, makeItemNumeric(&$4), makeItemNumeric(&$6)); }
;
datetime_template:
--
2.43.0
0002-fixup-Implement-jsonpath-.number-.decimal-pr.patch.nocfbottext/plain; charset=UTF-8; name=0002-fixup-Implement-jsonpath-.number-.decimal-pr.patch.nocfbotDownload
From f2f9176548d36d3b02bd3baf1d5afdf0a84a84b1 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 15 Jan 2024 14:19:22 +0100
Subject: [PATCH 2/2] fixup! Implement jsonpath .number(), .decimal([precision
[, scale]]), .bigint(), and .integer() methods
---
src/backend/utils/adt/jsonpath_exec.c | 48 +++++++++------------------
1 file changed, 16 insertions(+), 32 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index a361c456c54..092bcda45ee 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1118,8 +1118,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
char *numstr = NULL;
if (unwrap && JsonbType(jb) == jbvArray)
- return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
- false);
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
if (jb->type == jbvNumeric)
{
@@ -1131,8 +1130,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
jspOperationName(jsp->type)))));
if (jsp->type == jpiDecimal)
- numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
- NumericGetDatum(num)));
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out, NumericGetDatum(num)));
res = jperOk;
}
else if (jb->type == jbvString)
@@ -1144,10 +1142,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
- noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
- InvalidOid, -1,
- (Node *) &escontext,
- &datum);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr, InvalidOid, -1,
+ (Node *) &escontext, &datum);
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
@@ -1195,8 +1191,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for .decimal() precision");
- precision = numeric_int4_opt_error(jspGetNumeric(&elem),
- &have_error);
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem), &have_error);
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
@@ -1209,8 +1204,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (elem.type != jpiNumeric)
elog(ERROR, "invalid jsonpath item type for .decimal() scale");
- scale = numeric_int4_opt_error(jspGetNumeric(&elem),
- &have_error);
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem), &have_error);
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
@@ -1228,8 +1222,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
datums[1] = CStringGetDatum(sstr);
arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
- dtypmod = DirectFunctionCall1(numerictypmodin,
- PointerGetDatum(arrtypmod));
+ dtypmod = DirectFunctionCall1(numerictypmodin, PointerGetDatum(arrtypmod));
/* Convert numstr to Numeric with typmod */
Assert(numstr != NULL);
@@ -1262,8 +1255,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
Datum datum;
if (unwrap && JsonbType(jb) == jbvArray)
- return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
- false);
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
if (jb->type == jbvNumeric)
{
@@ -1288,10 +1280,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
ErrorSaveContext escontext = {T_ErrorSaveContext};
bool noerr;
- noerr = DirectInputFunctionCallSafe(int8in, tmp,
- InvalidOid, -1,
- (Node *) &escontext,
- &datum);
+ noerr = DirectInputFunctionCallSafe(int8in, tmp, InvalidOid, -1,
+ (Node *) &escontext, &datum);
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
@@ -1309,8 +1299,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
jb = &jbv;
jb->type = jbvNumeric;
- jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
- datum));
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric, datum));
res = executeNextItem(cxt, jsp, NULL, jb, found, true);
}
@@ -1322,8 +1311,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
Datum datum;
if (unwrap && JsonbType(jb) == jbvArray)
- return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
- false);
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
if (jb->type == jbvNumeric)
{
@@ -1343,15 +1331,12 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
else if (jb->type == jbvString)
{
/* cast string as integer */
- char *tmp = pnstrdup(jb->val.string.val,
- jb->val.string.len);
+ char *tmp = pnstrdup(jb->val.string.val, jb->val.string.len);
ErrorSaveContext escontext = {T_ErrorSaveContext};
bool noerr;
- noerr = DirectInputFunctionCallSafe(int4in, tmp,
- InvalidOid, -1,
- (Node *) &escontext,
- &datum);
+ noerr = DirectInputFunctionCallSafe(int4in, tmp, InvalidOid, -1,
+ (Node *) &escontext, &datum);
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
@@ -1369,8 +1354,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
jb = &jbv;
jb->type = jbvNumeric;
- jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
- datum));
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric, datum));
res = executeNextItem(cxt, jsp, NULL, jb, found, true);
}
--
2.43.0
On Mon, Jan 15, 2024 at 7:41 PM Peter Eisentraut <peter@eisentraut.org>
wrote:
Attached are two small fixup patches for your patch set.
Thanks, Peter.
In the first one, I simplified the grammar for the .decimal() method.
It seemed a bit overkill to build a whole list structure when all we
need are 0, 1, or 2 arguments.
Agree.
I added unary '+' and '-' support as well and thus thought of having
separate rules altogether rather than folding those in.
Per SQL standard, the precision and scale arguments are unsigned
integers, so unary plus and minus signs are not supported. So my patch
removes that support, but I didn't adjust the regression tests for that.
However, PostgreSQL numeric casting does support a negative scale. Here is
an example:
# select '12345'::numeric(4,-2);
numeric
---------
12300
(1 row)
And thus thought of supporting those.
Do we want this JSON item method to behave differently here?
Also note that in your 0002 patch, the datetime precision is similarly
unsigned, so that's consistent.By the way, in your 0002 patch, don't see the need for the separate
datetime_method grammar rule. You can fold that into accessor_op.
Sure.
Overall, I think it would be better if you combined all three of these
patches into one. Right now, you have arranged these as incremental
features, and as a result of that, the additions to the JsonPathItemType
enum and the grammar keywords etc. are ordered in the way you worked on
these features, I guess. It would be good to maintain a bit of sanity
to put all of this together and order all the enums and everything else
for example in the order they are in the sql_features.txt file (which is
alphabetical, I suppose). At this point I suspect we'll end up
committing this whole feature set together anyway, so we might as well
organize it that way.
OK.
I will merge them all into one and will try to keep them in the order
specified in sql_features.txt.
However, for documentation, it makes more sense to keep them in logical
order than the alphabetical one. What are your views on this?
Thanks
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
On 2024-01-17 We 04:03, Jeevan Chalke wrote:
On Mon, Jan 15, 2024 at 7:41 PM Peter Eisentraut
<peter@eisentraut.org> wrote:Overall, I think it would be better if you combined all three of
these
patches into one. Right now, you have arranged these as incremental
features, and as a result of that, the additions to the
JsonPathItemType
enum and the grammar keywords etc. are ordered in the way you
worked on
these features, I guess. It would be good to maintain a bit of
sanity
to put all of this together and order all the enums and everything
else
for example in the order they are in the sql_features.txt file
(which is
alphabetical, I suppose). At this point I suspect we'll end up
committing this whole feature set together anyway, so we might as
well
organize it that way.OK.
I will merge them all into one and will try to keep them in the order
specified in sql_features.txt.
However, for documentation, it makes more sense to keep them in
logical order than the alphabetical one. What are your views on this?
I agree that we should order the documentation logically. Users don't
care how we organize the code etc, but they do care about docs have
sensible structure.
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
On 17.01.24 10:03, Jeevan Chalke wrote:
I added unary '+' and '-' support as well and thus thought of having
separate rules altogether rather than folding those in.Per SQL standard, the precision and scale arguments are unsigned
integers, so unary plus and minus signs are not supported. So my patch
removes that support, but I didn't adjust the regression tests for that.However, PostgreSQL numeric casting does support a negative scale. Here
is an example:# select '12345'::numeric(4,-2);
numeric
---------
12300
(1 row)And thus thought of supporting those.
Do we want this JSON item method to behave differently here?
Ok, it would make sense to support this in SQL/JSON as well.
I will merge them all into one and will try to keep them in the order
specified in sql_features.txt.
However, for documentation, it makes more sense to keep them in logical
order than the alphabetical one. What are your views on this?
The documentation can be in a different order.
On Thu, Jan 18, 2024 at 1:03 AM Peter Eisentraut <peter@eisentraut.org>
wrote:
On 17.01.24 10:03, Jeevan Chalke wrote:
I added unary '+' and '-' support as well and thus thought of having
separate rules altogether rather than folding those in.Per SQL standard, the precision and scale arguments are unsigned
integers, so unary plus and minus signs are not supported. So mypatch
removes that support, but I didn't adjust the regression tests for
that.
However, PostgreSQL numeric casting does support a negative scale. Here
is an example:# select '12345'::numeric(4,-2);
numeric
---------
12300
(1 row)And thus thought of supporting those.
Do we want this JSON item method to behave differently here?Ok, it would make sense to support this in SQL/JSON as well.
OK. So with this, we don't need changes done in your 0001 patches.
I will merge them all into one and will try to keep them in the order
specified in sql_features.txt.
However, for documentation, it makes more sense to keep them in logical
order than the alphabetical one. What are your views on this?The documentation can be in a different order.
Thanks, Andrew and Peter for the confirmation.
Attached merged single patch along these lines.
Peter, I didn't understand why the changes you did in your 0002 patch were
required here. I did run the pgindent, and it didn't complain to me. So,
just curious to know more about the changes. I have not merged those
changes in this single patch. However, if needed it can be cleanly applied
on top of this single patch.
Thanks
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
Attachments:
v6-0001-Implement-various-jsonpath-methods.patchapplication/octet-stream; name=v6-0001-Implement-various-jsonpath-methods.patchDownload
From d060c848d047a3b34cf3636e99b1f1471cf5bef1 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Thu, 18 Jan 2024 19:41:29 +0530
Subject: [PATCH v6] Implement various jsonpath methods
This commit implements jsonpath .bigint(), .boolean(), .date(),
.decimal([precision [, scale]]), .integer(), .number(), .string(),
.time(), .time_tz(), .timestamp(), and .timestamp_tz() methods.
.bigint() method converts the given JSON string or a numeric value to
the bigint type representation.
.boolean() method converts the given JSON string, numeric, or boolean
value to the boolean type representation. In the numeric case, only
integers are allowed, whereas we use the parse_bool() backend function
to convert string to a bool.
.decimal([precision [, scale]]) method converts the given JSON string
or a numeric value to the numeric type representation. If precision
and scale are provided for .decimal(), then it is converted to the
equivalent numerictypmod and applied to the numeric number.
.integer() and .number() methods converts the given JSON string or a
numeric value to the int4 and numeric type representation.
.string() method uses the datatype's out function to convert numeric
and various date/time types to the string representation.
The JSON string representing a valid date/time is converted to the
specific date or time type representation using jsonpath .date(),
.time(), .time_tz(), .timestamp(), .timestamp_tz() methods. The
changes use the infrastructure of the .datetime() method and perform
the datatype conversion as appropriate. Unlike the .datetime()
method, all these methods don't accept format templates and use ISO
DateTime formats instead. However, except the .date() method, other
date/time related methods take an optional precision to adjust the
fractional seconds.
Jeevan Chalke, reviewed by Peter Eisentraut and Andrew Dunstan.
---
doc/src/sgml/func.sgml | 218 +++
src/backend/catalog/sql_features.txt | 28 +-
src/backend/utils/adt/jsonpath.c | 138 +-
src/backend/utils/adt/jsonpath_exec.c | 715 +++++++++-
src/backend/utils/adt/jsonpath_gram.y | 78 +-
src/backend/utils/adt/jsonpath_scan.l | 11 +
src/include/utils/jsonpath.h | 11 +
src/test/regress/expected/jsonb_jsonpath.out | 1848 +++++++++++++++++++++++++-
src/test/regress/expected/jsonpath.out | 78 ++
src/test/regress/sql/jsonb_jsonpath.sql | 492 +++++++
src/test/regress/sql/jsonpath.sql | 13 +
11 files changed, 3584 insertions(+), 46 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 210c7c0..bdfc571 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -17676,6 +17676,38 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>boolean()</literal>
+ <returnvalue><replaceable>boolean</replaceable></returnvalue>
+ </para>
+ <para>
+ Boolean value converted from a JSON boolean, number, or string
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()')</literal>
+ <returnvalue>[true, true, false]</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>string()</literal>
+ <returnvalue><replaceable>string</replaceable></returnvalue>
+ </para>
+ <para>
+ String value converted from a JSON boolean, number, string, or datetime
+ </para>
+ <para>
+ <literal>jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()')</literal>
+ <returnvalue>["1.23", "xyz", "false"]</returnvalue>
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.datetime().string()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>value</replaceable> <literal>.</literal> <literal>double()</literal>
<returnvalue><replaceable>number</replaceable></returnvalue>
</para>
@@ -17733,6 +17765,62 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>bigint()</literal>
+ <returnvalue><replaceable>bigint</replaceable></returnvalue>
+ </para>
+ <para>
+ Big integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "9876543219"}', '$.len.bigint()')</literal>
+ <returnvalue>9876543219</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>decimal( [ <replaceable>precision</replaceable> [ , <replaceable>scale</replaceable> ] ] )</literal>
+ <returnvalue><replaceable>decimal</replaceable></returnvalue>
+ </para>
+ <para>
+ Rounded decimal value converted from a JSON number or string. <literal>precision</literal> and <literal>scale</literal> must be integer values.
+ </para>
+ <para>
+ <literal>jsonb_path_query('1234.5678', '$.decimal(6, 2)')</literal>
+ <returnvalue>1234.57</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>integer()</literal>
+ <returnvalue><replaceable>integer</replaceable></returnvalue>
+ </para>
+ <para>
+ Integer value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "12345"}', '$.len.integer()')</literal>
+ <returnvalue>12345</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>value</replaceable> <literal>.</literal> <literal>number()</literal>
+ <returnvalue><replaceable>numeric</replaceable></returnvalue>
+ </para>
+ <para>
+ Numeric value converted from a JSON number or string
+ </para>
+ <para>
+ <literal>jsonb_path_query('{"len": "123.45"}', '$.len.number()')</literal>
+ <returnvalue>123.45</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>string</replaceable> <literal>.</literal> <literal>datetime()</literal>
<returnvalue><replaceable>datetime_type</replaceable></returnvalue>
(see note)
@@ -17764,6 +17852,136 @@ strict $.**.HR
<row>
<entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>date()</literal>
+ <returnvalue><replaceable>date</replaceable></returnvalue>
+ </para>
+ <para>
+ Date value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15"', '$.date()')</literal>
+ <returnvalue>"2023-08-15"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time()</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56"', '$.time()')</literal>
+ <returnvalue>"12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time without time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789"', '$.time(2)')</literal>
+ <returnvalue>"12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz()</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()')</literal>
+ <returnvalue>"12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>time_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>time with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Time with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)')</literal>
+ <returnvalue>"12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp()</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()')</literal>
+ <returnvalue>"2023-08-15T12:34:56"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp without time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp without time zone value converted from a string, with
+ fractional seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz()</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()')</literal>
+ <returnvalue>"2023-08-15T12:34:56+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
+ <replaceable>string</replaceable> <literal>.</literal> <literal>timestamp_tz(<replaceable>precision</replaceable>)</literal>
+ <returnvalue><replaceable>timestamp with time zone</replaceable></returnvalue>
+ </para>
+ <para>
+ Timestamp with time zone value converted from a string, with fractional
+ seconds adjusted to the given precision.
+ </para>
+ <para>
+ <literal>jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)')</literal>
+ <returnvalue>"2023-08-15T12:34:56.79+05:30"</returnvalue>
+ </para></entry>
+ </row>
+
+ <row>
+ <entry role="func_table_entry"><para role="func_signature">
<replaceable>object</replaceable> <literal>.</literal> <literal>keyvalue()</literal>
<returnvalue><replaceable>array</replaceable></returnvalue>
</para>
diff --git a/src/backend/catalog/sql_features.txt b/src/backend/catalog/sql_features.txt
index 80c40ea..4085a2d 100644
--- a/src/backend/catalog/sql_features.txt
+++ b/src/backend/catalog/sql_features.txt
@@ -574,20 +574,20 @@ T861 SQL/JSON simplified accessor: case-sensitive JSON member accessor NO
T862 SQL/JSON simplified accessor: wildcard member accessor NO
T863 SQL/JSON simplified accessor: single-quoted string literal as member accessor NO
T864 SQL/JSON simplified accessor NO
-T865 SQL/JSON item method: bigint() NO
-T866 SQL/JSON item method: boolean() NO
-T867 SQL/JSON item method: date() NO
-T868 SQL/JSON item method: decimal() NO
-T869 SQL/JSON item method: decimal() with precision and scale NO
-T870 SQL/JSON item method: integer() NO
-T871 SQL/JSON item method: number() NO
-T872 SQL/JSON item method: string() NO
-T873 SQL/JSON item method: time() NO
-T874 SQL/JSON item method: time_tz() NO
-T875 SQL/JSON item method: time precision NO
-T876 SQL/JSON item method: timestamp() NO
-T877 SQL/JSON item method: timestamp_tz() NO
-T878 SQL/JSON item method: timestamp precision NO
+T865 SQL/JSON item method: bigint() YES
+T866 SQL/JSON item method: boolean() YES
+T867 SQL/JSON item method: date() YES
+T868 SQL/JSON item method: decimal() YES
+T869 SQL/JSON item method: decimal() with precision and scale YES
+T870 SQL/JSON item method: integer() YES
+T871 SQL/JSON item method: number() YES
+T872 SQL/JSON item method: string() YES
+T873 SQL/JSON item method: time() YES
+T874 SQL/JSON item method: time_tz() YES
+T875 SQL/JSON item method: time precision YES
+T876 SQL/JSON item method: timestamp() YES
+T877 SQL/JSON item method: timestamp_tz() YES
+T878 SQL/JSON item method: timestamp precision YES
T879 JSON in equality operations YES with jsonb
T880 JSON in grouping operations YES with jsonb
T881 JSON in ordering operations NO with jsonb, partially supported
diff --git a/src/backend/utils/adt/jsonpath.c b/src/backend/utils/adt/jsonpath.c
index d02c03e..258ed8e 100644
--- a/src/backend/utils/adt/jsonpath.c
+++ b/src/backend/utils/adt/jsonpath.c
@@ -295,6 +295,7 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiDiv:
case jpiMod:
case jpiStartsWith:
+ case jpiDecimal:
{
/*
* First, reserve place for left/right arg's positions, then
@@ -355,6 +356,10 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiMinus:
case jpiExists:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
{
int32 arg = reserveSpaceForItemPointer(buf);
@@ -444,6 +449,12 @@ flattenJsonPathParseItem(StringInfo buf, int *result, struct Node *escontext,
case jpiCeiling:
case jpiDouble:
case jpiKeyValue:
+ case jpiBigint:
+ case jpiBoolean:
+ case jpiDate:
+ case jpiInteger:
+ case jpiNumber:
+ case jpiStringFunc:
break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", item->type);
@@ -742,6 +753,75 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey,
if (printBracketes)
appendStringInfoChar(buf, ')');
break;
+ case jpiBigint:
+ appendStringInfoString(buf, ".bigint()");
+ break;
+ case jpiBoolean:
+ appendStringInfoString(buf, ".boolean()");
+ break;
+ case jpiDate:
+ appendStringInfoString(buf, ".date()");
+ break;
+ case jpiDecimal:
+ appendStringInfoString(buf, ".decimal(");
+ if (v->content.args.left)
+ {
+ jspGetLeftArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ if (v->content.args.right)
+ {
+ appendStringInfoChar(buf, ',');
+ jspGetRightArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiInteger:
+ appendStringInfoString(buf, ".integer()");
+ break;
+ case jpiNumber:
+ appendStringInfoString(buf, ".number()");
+ break;
+ case jpiStringFunc:
+ appendStringInfoString(buf, ".string()");
+ break;
+ case jpiTime:
+ appendStringInfoString(buf, ".time(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimeTz:
+ appendStringInfoString(buf, ".time_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestamp:
+ appendStringInfoString(buf, ".timestamp(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
+ case jpiTimestampTz:
+ appendStringInfoString(buf, ".timestamp_tz(");
+ if (v->content.arg)
+ {
+ jspGetArg(v, &elem);
+ printJsonPathItem(buf, &elem, false, false);
+ }
+ appendStringInfoChar(buf, ')');
+ break;
default:
elog(ERROR, "unrecognized jsonpath item type: %d", v->type);
}
@@ -803,6 +883,28 @@ jspOperationName(JsonPathItemType type)
return "starts with";
case jpiLikeRegex:
return "like_regex";
+ case jpiBigint:
+ return "bigint";
+ case jpiBoolean:
+ return "boolean";
+ case jpiDate:
+ return "date";
+ case jpiDecimal:
+ return "decimal";
+ case jpiInteger:
+ return "integer";
+ case jpiNumber:
+ return "number";
+ case jpiStringFunc:
+ return "string";
+ case jpiTime:
+ return "time";
+ case jpiTimeTz:
+ return "time_tz";
+ case jpiTimestamp:
+ return "timestamp";
+ case jpiTimestampTz:
+ return "timestamp_tz";
default:
elog(ERROR, "unrecognized jsonpath item type: %d", type);
return NULL;
@@ -899,6 +1001,12 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiDouble:
case jpiKeyValue:
case jpiLast:
+ case jpiBigint:
+ case jpiBoolean:
+ case jpiDate:
+ case jpiInteger:
+ case jpiNumber:
+ case jpiStringFunc:
break;
case jpiString:
case jpiKey:
@@ -923,6 +1031,7 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiDiv:
case jpiMod:
case jpiStartsWith:
+ case jpiDecimal:
read_int32(v->content.args.left, base, pos);
read_int32(v->content.args.right, base, pos);
break;
@@ -933,6 +1042,10 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
case jpiMinus:
case jpiFilter:
case jpiDatetime:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
read_int32(v->content.arg, base, pos);
break;
case jpiIndexArray:
@@ -964,7 +1077,11 @@ jspGetArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMinus ||
v->type == jpiFilter ||
v->type == jpiExists ||
- v->type == jpiDatetime);
+ v->type == jpiDatetime ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
jspInitByBuffer(a, v->base, v->content.arg);
}
@@ -1015,7 +1132,18 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
v->type == jpiKeyValue ||
v->type == jpiLast ||
v->type == jpiStartsWith ||
- v->type == jpiLikeRegex);
+ v->type == jpiLikeRegex ||
+ v->type == jpiBigint ||
+ v->type == jpiBoolean ||
+ v->type == jpiDate ||
+ v->type == jpiDecimal ||
+ v->type == jpiInteger ||
+ v->type == jpiNumber ||
+ v->type == jpiStringFunc ||
+ v->type == jpiTime ||
+ v->type == jpiTimeTz ||
+ v->type == jpiTimestamp ||
+ v->type == jpiTimestampTz);
if (a)
jspInitByBuffer(a, v->base, v->nextPos);
@@ -1041,7 +1169,8 @@ jspGetLeftArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
- v->type == jpiStartsWith);
+ v->type == jpiStartsWith ||
+ v->type == jpiDecimal);
jspInitByBuffer(a, v->base, v->content.args.left);
}
@@ -1062,7 +1191,8 @@ jspGetRightArg(JsonPathItem *v, JsonPathItem *a)
v->type == jpiMul ||
v->type == jpiDiv ||
v->type == jpiMod ||
- v->type == jpiStartsWith);
+ v->type == jpiStartsWith ||
+ v->type == jpiDecimal);
jspInitByBuffer(a, v->base, v->content.args.right);
}
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index ac16f5c..665db9c 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1071,6 +1071,11 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
break;
case jpiDatetime:
+ case jpiDate:
+ case jpiTime:
+ case jpiTimeTz:
+ case jpiTimestamp:
+ case jpiTimestampTz:
if (unwrap && JsonbType(jb) == jbvArray)
return executeItemUnwrapTargetArray(cxt, jsp, jb, found, false);
@@ -1110,6 +1115,420 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
break;
+ case jpiBigint:
+ {
+ JsonbValue jbv;
+ Datum datum;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ bool have_error;
+ int64 val;
+
+ val = numeric_int8_opt_error(jb->val.numeric, &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ jspOperationName(jsp->type)))));
+
+ datum = Int64GetDatum(val);
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as bigint */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+ bool noerr;
+
+ noerr = DirectInputFunctionCallSafe(int8in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int8_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiBoolean:
+ {
+ JsonbValue jbv;
+ bool bval;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvBool)
+ {
+ bval = jb->val.boolean;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvNumeric)
+ {
+ int ival;
+ Datum datum;
+ bool noerr;
+ char *tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ jspOperationName(jsp->type)))));
+
+ ival = DatumGetInt32(datum);
+ if (ival == 0)
+ bval = false;
+ else
+ bval = true;
+
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as boolean */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+
+ if (!parse_bool(tmp, &bval))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, or numeric value",
+ jspOperationName(jsp->type)))));
+
+ jb = &jbv;
+ jb->type = jbvBool;
+ jb->val.boolean = bval;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiDecimal:
+ case jpiNumber:
+ {
+ JsonbValue jbv;
+ Numeric num;
+ char *numstr = NULL;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ num = jb->val.numeric;
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->type == jpiDecimal)
+ numstr = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(num)));
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as number */
+ Datum datum;
+ bool noerr;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ numstr = pnstrdup(jb->val.string.val, jb->val.string.len);
+
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(datum);
+ if (numeric_is_nan(num) || numeric_is_inf(num))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ /*
+ * If we have arguments, then they must be the precision and
+ * optional scale used in .decimal(). Convert them to the
+ * typmod equivalent and then truncate the numeric value per
+ * this typmod details.
+ */
+ if (jsp->type == jpiDecimal && jsp->content.args.left)
+ {
+ Datum numdatum;
+ Datum dtypmod;
+ int32 precision;
+ int32 scale = 0;
+ bool have_error;
+ bool noerr;
+ ArrayType *arrtypmod;
+ Datum datums[2];
+ char pstr[12]; /* sign, 10 digits and '\0' */
+ char sstr[12]; /* sign, 10 digits and '\0' */
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ jspGetLeftArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() precision");
+
+ precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("precision of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ if (jsp->content.args.right)
+ {
+ jspGetRightArg(jsp, &elem);
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for .decimal() scale");
+
+ scale = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("scale of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
+ /*
+ * numerictypmodin() takes the precision and scale in the
+ * form of CString arrays.
+ */
+ pg_ltoa(precision, pstr);
+ datums[0] = CStringGetDatum(pstr);
+ pg_ltoa(scale, sstr);
+ datums[1] = CStringGetDatum(sstr);
+ arrtypmod = construct_array_builtin(datums, 2, CSTRINGOID);
+
+ dtypmod = DirectFunctionCall1(numerictypmodin,
+ PointerGetDatum(arrtypmod));
+
+ /* Convert numstr to Numeric with typmod */
+ Assert(numstr != NULL);
+ noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
+ InvalidOid, dtypmod,
+ (Node *) &escontext,
+ &numdatum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ jspOperationName(jsp->type)))));
+
+ num = DatumGetNumeric(numdatum);
+ pfree(arrtypmod);
+ }
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = num;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiInteger:
+ {
+ JsonbValue jbv;
+ Datum datum;
+
+ if (unwrap && JsonbType(jb) == jbvArray)
+ return executeItemUnwrapTargetArray(cxt, jsp, jb, found,
+ false);
+
+ if (jb->type == jbvNumeric)
+ {
+ bool have_error;
+ int32 val;
+
+ val = numeric_int4_opt_error(jb->val.numeric, &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+
+ datum = Int32GetDatum(val);
+ res = jperOk;
+ }
+ else if (jb->type == jbvString)
+ {
+ /* cast string as integer */
+ char *tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+ bool noerr;
+
+ noerr = DirectInputFunctionCallSafe(int4in, tmp,
+ InvalidOid, -1,
+ (Node *) &escontext,
+ &datum);
+
+ if (!noerr || escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ jspOperationName(jsp->type)))));
+ res = jperOk;
+ }
+
+ if (res == jperNotFound)
+ RETURN_ERROR(ereport(ERROR,
+ (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)))));
+
+ jb = &jbv;
+ jb->type = jbvNumeric;
+ jb->val.numeric = DatumGetNumeric(DirectFunctionCall1(int4_numeric,
+ datum));
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
+ case jpiStringFunc:
+ {
+ JsonbValue jbv;
+ char *tmp = NULL;
+
+ switch (JsonbType(jb))
+ {
+ case jbvString:
+
+ /*
+ * Value is not necessarily null-terminated, so we do
+ * pnstrdup() here.
+ */
+ tmp = pnstrdup(jb->val.string.val,
+ jb->val.string.len);
+ break;
+ case jbvNumeric:
+ tmp = DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric)));
+ break;
+ case jbvBool:
+ tmp = (jb->val.boolean) ? "true" : "false";
+ break;
+ case jbvDatetime:
+ {
+ switch (jb->val.datetime.typid)
+ {
+ case DATEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(date_out,
+ jb->val.datetime.value));
+ break;
+ case TIMEOID:
+ tmp = DatumGetCString(DirectFunctionCall1(time_out,
+ jb->val.datetime.value));
+ break;
+ case TIMETZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timetz_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamp_out,
+ jb->val.datetime.value));
+ break;
+ case TIMESTAMPTZOID:
+ tmp = DatumGetCString(DirectFunctionCall1(timestamptz_out,
+ jb->val.datetime.value));
+ break;
+ default:
+ elog(ERROR, "unrecognized SQL/JSON datetime type oid: %u",
+ jb->val.datetime.typid);
+ }
+ }
+ break;
+ case jbvNull:
+ case jbvArray:
+ case jbvObject:
+ case jbvBinary:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("jsonpath item method .%s() can only be applied to a bool, string, numeric, or datetime value",
+ jspOperationName(jsp->type)))));
+ break;
+ }
+
+ res = jperOk;
+
+ jb = &jbv;
+ Assert(tmp != NULL); /* We must have set tmp above */
+ jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.len = strlen(jb->val.string.val);
+ jb->type = jbvString;
+
+ res = executeNextItem(cxt, jsp, NULL, jb, found, true);
+ }
+ break;
+
default:
elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
@@ -1771,11 +2190,15 @@ executeNumericItemMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
/*
- * Implementation of the .datetime() method.
+ * Implementation of the .datetime() and related methods.
*
* 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.
+ *
+ * .date(), .time(), .time_tz(), .timestamp(), .timestamp_tz() methods don't
+ * have a format, so ISO format is used. However, except .date(), they all
+ * take an optional time precision.
*/
static JsonPathExecResult
executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
@@ -1791,6 +2214,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
bool hasNext;
JsonPathExecResult res = jperNotFound;
JsonPathItem elem;
+ int32 time_precision = -1;
if (!(jb = getScalar(jb, jbvString)))
RETURN_ERROR(ereport(ERROR,
@@ -1808,7 +2232,11 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
*/
collid = DEFAULT_COLLATION_OID;
- if (jsp->content.arg)
+ /*
+ * .datetime(template) has an argument, the rest of the methods don't have
+ * an argument. So we handle that separately.
+ */
+ if (jsp->type == jpiDatetime && jsp->content.arg)
{
text *template;
char *template_str;
@@ -1870,6 +2298,30 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
static text *fmt_txt[lengthof(fmt_str)] = {0};
int i;
+ /*
+ * Check for optional precision for methods other than .datetime() and
+ * .date()
+ */
+ if (jsp->type != jpiDatetime && jsp->type != jpiDate &&
+ jsp->content.arg)
+ {
+ bool have_error;
+
+ jspGetArg(jsp, &elem);
+
+ if (elem.type != jpiNumeric)
+ elog(ERROR, "invalid jsonpath item type for %s argument",
+ jspOperationName(jsp->type));
+
+ time_precision = numeric_int4_opt_error(jspGetNumeric(&elem),
+ &have_error);
+ if (have_error)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time precision of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ }
+
/* loop until datetime format fits */
for (i = 0; i < lengthof(fmt_str); i++)
{
@@ -1896,11 +2348,260 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
}
if (res == jperNotFound)
- RETURN_ERROR(ereport(ERROR,
- (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
- errhint("Use a datetime template argument to specify the input data format."))));
+ {
+ if (jsp->type == jpiDatetime)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("datetime format is not recognized: \"%s\"",
+ text_to_cstring(datetime)),
+ errhint("Use a datetime template argument to specify the input data format."))));
+ else
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("%s format is not recognized: \"%s\"",
+ jspOperationName(jsp->type), text_to_cstring(datetime)))));
+
+ }
+ }
+
+ /*
+ * parse_datetime() processes the entire input string per the template or
+ * ISO format and returns the Datum in best fitted datetime type. So, if
+ * this call is for a specific datatype, then we do the conversion here.
+ * Throw an error for incompatible types.
+ */
+ switch (jsp->type)
+ {
+ case jpiDatetime: /* Nothing to do for DATETIME */
+ break;
+ case jpiDate:
+ {
+ /* Convert result type to date */
+ switch (typid)
+ {
+ case DATEOID: /* Nothing to do for DATE */
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("date format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_date,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_date,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ typid = DATEOID;
+ }
+ break;
+ case jpiTime:
+ {
+ /* Convert result type to time without time zone */
+ switch (typid)
+ {
+ case DATEOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID: /* Nothing to do for TIME */
+ break;
+ case TIMETZOID:
+ value = DirectFunctionCall1(timetz_time,
+ value);
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_time,
+ value);
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_time,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeADT result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(false,
+ time_precision);
+ result = DatumGetTimeADT(value);
+ AdjustTimeForTypmod(&result, time_precision);
+ value = TimeADTGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMEOID;
+ }
+ break;
+ case jpiTimeTz:
+ {
+ /* Convert result type to time with time zone */
+ switch (typid)
+ {
+ case DATEOID:
+ case TIMESTAMPOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("time_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMEOID:
+ value = DirectFunctionCall1(time_timetz,
+ value);
+ break;
+ case TIMETZOID: /* Nothing to do for TIMETZ */
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timetz,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ TimeTzADT *result;
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytime_typmod_check(true,
+ time_precision);
+ result = DatumGetTimeTzADTP(value);
+ AdjustTimeForTypmod(&result->time, time_precision);
+ value = TimeTzADTPGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMETZOID;
+ }
+ break;
+ case jpiTimestamp:
+ {
+ /* Convert result type to timestamp without time zone */
+ switch (typid)
+ {
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamp,
+ value);
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
+ break;
+ case TIMESTAMPTZOID:
+ value = DirectFunctionCall1(timestamptz_timestamp,
+ value);
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(false,
+ time_precision);
+ result = DatumGetTimestamp(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPOID;
+ }
+ break;
+ case jpiTimestampTz:
+ {
+ /* Convert result type to timestamp with time zone */
+ switch (typid)
+ {
+ case DATEOID:
+ value = DirectFunctionCall1(date_timestamptz,
+ value);
+ break;
+ case TIMEOID:
+ case TIMETZOID:
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("timestamp_tz format is not recognized: \"%s\"",
+ text_to_cstring(datetime)))));
+ break;
+ case TIMESTAMPOID:
+ value = DirectFunctionCall1(timestamp_timestamptz,
+ value);
+ break;
+ case TIMESTAMPTZOID: /* Nothing to do for TIMESTAMPTZ */
+ break;
+ default:
+ elog(ERROR, "type with oid %d not supported", typid);
+ }
+
+ /* Force the user-given time precision, if any */
+ if (time_precision != -1)
+ {
+ Timestamp result;
+ ErrorSaveContext escontext = {T_ErrorSaveContext};
+
+ /* Get a warning when precision is reduced */
+ time_precision = anytimestamp_typmod_check(true,
+ time_precision);
+ result = DatumGetTimestampTz(value);
+ AdjustTimestampForTypmod(&result, time_precision,
+ (Node *) &escontext);
+ if (escontext.error_occurred)
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ jspOperationName(jsp->type)))));
+ value = TimestampTzGetDatum(result);
+
+ /* Update the typmod value with the user-given precision */
+ typmod = time_precision;
+ }
+
+ typid = TIMESTAMPTZOID;
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized jsonpath item type: %d", jsp->type);
}
pfree(datetime);
diff --git a/src/backend/utils/adt/jsonpath_gram.y b/src/backend/utils/adt/jsonpath_gram.y
index 5e4eb52..8733a0e 100644
--- a/src/backend/utils/adt/jsonpath_gram.y
+++ b/src/backend/utils/adt/jsonpath_gram.y
@@ -82,15 +82,18 @@ static bool makeItemLikeRegex(JsonPathParseItem *expr,
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P KEYVALUE_P
%token <str> DATETIME_P
+%token <str> BIGINT_P BOOLEAN_P DATE_P DECIMAL_P INTEGER_P NUMBER_P
+%token <str> STRINGFUNC_P TIME_P TIME_TZ_P TIMESTAMP_P TIMESTAMP_TZ_P
%type <result> result
%type <value> scalar_value path_primary expr array_accessor
any_path accessor_op key predicate delimited_predicate
index_elem starts_with_initial expr_or_predicate
- datetime_template opt_datetime_template
+ datetime_template opt_datetime_template csv_elem
+ datetime_precision opt_datetime_precision
-%type <elems> accessor_expr
+%type <elems> accessor_expr csv_list opt_csv_list
%type <indexs> index_list
@@ -248,9 +251,59 @@ accessor_op:
| array_accessor { $$ = $1; }
| '.' any_path { $$ = $2; }
| '.' method '(' ')' { $$ = makeItemType($2); }
- | '.' DATETIME_P '(' opt_datetime_template ')'
- { $$ = makeItemUnary(jpiDatetime, $4); }
| '?' '(' predicate ')' { $$ = makeItemUnary(jpiFilter, $3); }
+ | '.' DECIMAL_P '(' opt_csv_list ')'
+ {
+ if (list_length($4) == 0)
+ $$ = makeItemBinary(jpiDecimal, NULL, NULL);
+ else if (list_length($4) == 1)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), NULL);
+ else if (list_length($4) == 2)
+ $$ = makeItemBinary(jpiDecimal, linitial($4), lsecond($4));
+ else
+ ereturn(escontext, false,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid input syntax for type %s", "jsonpath"),
+ errdetail(".decimal() can only have an optional precision[,scale].")));
+ }
+ | '.' DATETIME_P '(' opt_datetime_template ')'
+ { $$ = makeItemUnary(jpiDatetime, $4); }
+ | '.' TIME_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTime, $4); }
+ | '.' TIME_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimeTz, $4); }
+ | '.' TIMESTAMP_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestamp, $4); }
+ | '.' TIMESTAMP_TZ_P '(' opt_datetime_precision ')'
+ { $$ = makeItemUnary(jpiTimestampTz, $4); }
+ ;
+
+csv_elem:
+ INT_P
+ { $$ = makeItemNumeric(&$1); }
+ | '+' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiPlus, makeItemNumeric(&$2)); }
+ | '-' INT_P %prec UMINUS
+ { $$ = makeItemUnary(jpiMinus, makeItemNumeric(&$2)); }
+ ;
+
+csv_list:
+ csv_elem { $$ = list_make1($1); }
+ | csv_list ',' csv_elem { $$ = lappend($1, $3); }
+ ;
+
+opt_csv_list:
+ csv_list { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
+ ;
+
+datetime_precision:
+ INT_P { $$ = makeItemNumeric(&$1); }
+ ;
+
+opt_datetime_precision:
+ datetime_precision { $$ = $1; }
+ | /* EMPTY */ { $$ = NULL; }
;
datetime_template:
@@ -291,6 +344,17 @@ key_name:
| WITH_P
| LIKE_REGEX_P
| FLAG_P
+ | BIGINT_P
+ | BOOLEAN_P
+ | DATE_P
+ | DECIMAL_P
+ | INTEGER_P
+ | NUMBER_P
+ | STRINGFUNC_P
+ | TIME_P
+ | TIME_TZ_P
+ | TIMESTAMP_P
+ | TIMESTAMP_TZ_P
;
method:
@@ -301,6 +365,12 @@ method:
| DOUBLE_P { $$ = jpiDouble; }
| CEILING_P { $$ = jpiCeiling; }
| KEYVALUE_P { $$ = jpiKeyValue; }
+ | BIGINT_P { $$ = jpiBigint; }
+ | BOOLEAN_P { $$ = jpiBoolean; }
+ | DATE_P { $$ = jpiDate; }
+ | INTEGER_P { $$ = jpiInteger; }
+ | NUMBER_P { $$ = jpiNumber; }
+ | STRINGFUNC_P { $$ = jpiStringFunc; }
;
%%
diff --git a/src/backend/utils/adt/jsonpath_scan.l b/src/backend/utils/adt/jsonpath_scan.l
index 757cd95..7acda77 100644
--- a/src/backend/utils/adt/jsonpath_scan.l
+++ b/src/backend/utils/adt/jsonpath_scan.l
@@ -401,24 +401,35 @@ static const JsonPathKeyword keywords[] = {
{ 2, false, TO_P, "to"},
{ 3, false, ABS_P, "abs"},
{ 3, false, LAX_P, "lax"},
+ { 4, false, DATE_P, "date"},
{ 4, false, FLAG_P, "flag"},
{ 4, false, LAST_P, "last"},
{ 4, true, NULL_P, "null"},
{ 4, false, SIZE_P, "size"},
+ { 4, false, TIME_P, "time"},
{ 4, true, TRUE_P, "true"},
{ 4, false, TYPE_P, "type"},
{ 4, false, WITH_P, "with"},
{ 5, true, FALSE_P, "false"},
{ 5, false, FLOOR_P, "floor"},
+ { 6, false, BIGINT_P, "bigint"},
{ 6, false, DOUBLE_P, "double"},
{ 6, false, EXISTS_P, "exists"},
+ { 6, false, NUMBER_P, "number"},
{ 6, false, STARTS_P, "starts"},
{ 6, false, STRICT_P, "strict"},
+ { 6, false, STRINGFUNC_P, "string"},
+ { 7, false, BOOLEAN_P, "boolean"},
{ 7, false, CEILING_P, "ceiling"},
+ { 7, false, DECIMAL_P, "decimal"},
+ { 7, false, INTEGER_P, "integer"},
+ { 7, false, TIME_TZ_P, "time_tz"},
{ 7, false, UNKNOWN_P, "unknown"},
{ 8, false, DATETIME_P, "datetime"},
{ 8, false, KEYVALUE_P, "keyvalue"},
+ { 9, false, TIMESTAMP_P, "timestamp"},
{ 10,false, LIKE_REGEX_P, "like_regex"},
+ { 12,false, TIMESTAMP_TZ_P, "timestamp_tz"},
};
/* Check if current scanstring value is a keyword */
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 9d55c25..0f0e126 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -102,6 +102,17 @@ typedef enum JsonPathItemType
jpiLast, /* LAST array subscript */
jpiStartsWith, /* STARTS WITH predicate */
jpiLikeRegex, /* LIKE_REGEX predicate */
+ jpiBigint, /* .bigint() item method */
+ jpiBoolean, /* .boolean() item method */
+ jpiDate, /* .date() item method */
+ jpiDecimal, /* .decimal() item method */
+ jpiInteger, /* .integer() item method */
+ jpiNumber, /* .number() item method */
+ jpiStringFunc, /* .string() item method */
+ jpiTime, /* .time() item method */
+ jpiTimeTz, /* .time_tz() item method */
+ jpiTimestamp, /* .timestamp() item method */
+ jpiTimestampTz, /* .timestamp_tz() item method */
} JsonPathItemType;
/* XQuery regex mode flags for LIKE_REGEX predicate */
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 6659bc9..e758d72 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1732,7 +1732,1246 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
ERROR: unmatched format character "T"
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
ERROR: unmatched format character "T"
+-- Test .bigint()
+select jsonb_path_query('null', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.bigint()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.bigint()');
+ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"1.23"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('1e1000', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"nan"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"NaN"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"-inf"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1.23', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('1.83', '$.bigint()');
+ jsonb_path_query
+------------------
+ 2
+(1 row)
+
+select jsonb_path_query('1234567890123', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 1234567890123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+select jsonb_path_query('"+123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.bigint()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.bigint() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+-- Test .boolean()
+select jsonb_path_query('null', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('null', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.boolean()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('{}', '$.boolean()');
+ERROR: jsonpath item method .boolean() can only be applied to a bool, string, or numeric value
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"1.23"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('1e1000', '$.boolean()');
+ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+select jsonb_path_query('"nan"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"NaN"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"-inf"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"100"', '$.boolean()');
+ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+select jsonb_path_query('true', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('false', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('0', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('-1', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('100', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"1"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"0"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"true"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"false"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"TRUE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"FALSE"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"yes"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"NO"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"T"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"f"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('"y"', '$.boolean()');
+ jsonb_path_query
+------------------
+ true
+(1 row)
+
+select jsonb_path_query('"N"', '$.boolean()');
+ jsonb_path_query
+------------------
+ false
+(1 row)
+
+select jsonb_path_query('true', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('123', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+ jsonb_path_query
+------------------
+ "boolean"
+(1 row)
+
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+ jsonb_path_query_array
+------------------------
+ [true, true, false]
+(1 row)
+
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('true', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('1', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('[]', '$.date()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('{}', '$.date()');
+ERROR: jsonpath item method .date() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.date()');
+ERROR: date format is not recognized: "bogus"
+select jsonb '"2023-08-15"' @? '$.date()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+ jsonb_path_query
+------------------
+ "date"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+ERROR: date format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+ERROR: date format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+ jsonb_path_query
+------------------
+ "2023-08-15"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ERROR: syntax error at or near "2" of jsonpath input
+LINE 1: select jsonb_path_query('"2023-08-15"', '$.date(2)');
+ ^
+-- Test .decimal()
+select jsonb_path_query('null', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.decimal()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.decimal()');
+ERROR: jsonpath item method .decimal() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.decimal()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.decimal()');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.decimal()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+ jsonb_path_query
+------------------
+ 12345.7
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+ERROR: NUMERIC precision 0 must be between 1 and 1000
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+ERROR: NUMERIC precision 1001 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+ jsonb_path_query
+------------------
+ 1234.57
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ 1200
+(1 row)
+
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+ERROR: NUMERIC precision -6 must be between 1 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+ERROR: NUMERIC scale -1001 must be between -1000 and 1000
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+ERROR: NUMERIC scale 1001 must be between -1000 and 1000
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+ jsonb_path_query
+------------------
+ -1200
+(1 row)
+
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+ jsonb_path_query
+------------------
+ 0.01
+(1 row)
+
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+ jsonb_path_query
+------------------
+ 0.0012
+(1 row)
+
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+ jsonb_path_query
+------------------
+ 0
+(1 row)
+
+select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
+ERROR: precision of jsonpath item method .decimal() is out of range for type integer
+select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+ERROR: scale of jsonpath item method .decimal() is out of range for type integer
+-- Test .integer()
+select jsonb_path_query('null', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.integer()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.integer()');
+ERROR: jsonpath item method .integer() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"1.23"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('1e1000', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"nan"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"NaN"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"-inf"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('1.23', '$.integer()');
+ jsonb_path_query
+------------------
+ 1
+(1 row)
+
+select jsonb_path_query('1.83', '$.integer()');
+ jsonb_path_query
+------------------
+ 2
+(1 row)
+
+select jsonb_path_query('12345678901', '$.integer()');
+ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+select jsonb_path_query('"12345678901"', '$.integer()');
+ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+select jsonb_path_query('"+123"', '$.integer()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('-123', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('"-123"', '$.integer()');
+ jsonb_path_query
+------------------
+ -123
+(1 row)
+
+select jsonb_path_query('123', '$.integer() * 2');
+ jsonb_path_query
+------------------
+ 246
+(1 row)
+
+-- Test .number()
+select jsonb_path_query('null', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('true', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('null', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('true', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.number()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('{}', '$.number()');
+ERROR: jsonpath item method .number() can only be applied to a string or numeric value
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.number()');
+ jsonb_path_query
+------------------
+ 1.23
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('1e1000', '$.number()');
+ jsonb_path_query
+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ 10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
+(1 row)
+
+select jsonb_path_query('"nan"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"NaN"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"-inf"', '$.number()');
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('123', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('"123"', '$.number()');
+ jsonb_path_query
+------------------
+ 123
+(1 row)
+
+select jsonb_path_query('12345678901234567890', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+ jsonb_path_query
+----------------------
+ 12345678901234567890
+(1 row)
+
+select jsonb_path_query('"+12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ 12.3
+(1 row)
+
+select jsonb_path_query('-12.3', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('"-12.3"', '$.number()');
+ jsonb_path_query
+------------------
+ -12.3
+(1 row)
+
+select jsonb_path_query('12.3', '$.number() * 2');
+ jsonb_path_query
+------------------
+ 24.6
+(1 row)
+
+-- Test .string()
+select jsonb_path_query('null', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('null', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('{}', '$.string()');
+ERROR: jsonpath item method .string() can only be applied to a bool, string, numeric, or datetime value
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('{}', '$.string()', silent => true);
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('1.23', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23"
+(1 row)
+
+select jsonb_path_query('"1.23aaa"', '$.string()');
+ jsonb_path_query
+------------------
+ "1.23aaa"
+(1 row)
+
+select jsonb_path_query('1234', '$.string()');
+ jsonb_path_query
+------------------
+ "1234"
+(1 row)
+
+select jsonb_path_query('true', '$.string()');
+ jsonb_path_query
+------------------
+ "true"
+(1 row)
+
+select jsonb_path_query('1234', '$.string().type()');
+ jsonb_path_query
+------------------
+ "string"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+ jsonb_path_query
+----------------------------
+ "Tue Aug 15 00:04:56 2023"
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+ jsonb_path_query_array
+--------------------------
+ ["1.23", "yes", "false"]
+(1 row)
+
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+ jsonb_path_query_array
+--------------------------------
+ ["string", "string", "string"]
+(1 row)
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('true', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('1', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('[]', '$.time()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('{}', '$.time()');
+ERROR: jsonpath item method .time() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time()');
+ERROR: time format is not recognized: "bogus"
+select jsonb '"12:34:56"' @? '$.time()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+ jsonb_path_query
+--------------------------
+ "time without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+ERROR: time format is not recognized: "2023-08-15"
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+ jsonb_path_query
+------------------
+ "12:34:56"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+ ^
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+ERROR: time precision of jsonpath item method .time() is out of range for type integer
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+ jsonb_path_query
+------------------
+ "12:34:57"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+ jsonb_path_query
+------------------
+ "12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+WARNING: TIME(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------
+ "12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+WARNING: TIME(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------
+ "12:34:56.789012"
+(1 row)
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('true', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('1', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.time_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.time_tz()');
+ERROR: jsonpath item method .time_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "bogus"
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+ jsonb_path_query
+-----------------------
+ "time with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15"
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+ERROR: time_tz format is not recognized: "2023-08-15 12:34:56"
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(...
+ ^
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+ERROR: time precision of jsonpath item method .time_tz() is out of range for type integer
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+ jsonb_path_query
+------------------
+ "12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+ jsonb_path_query
+---------------------
+ "12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+WARNING: TIME(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+----------------------
+ "12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+WARNING: TIME(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+-------------------------
+ "12:34:56.789012+05:30"
+(1 row)
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp()');
+ERROR: jsonpath item method .timestamp() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+ jsonb_path_query
+-------------------------------
+ "timestamp without time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+ERROR: timestamp format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...ect jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+ERROR: time precision of jsonpath item method .timestamp() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T12:34:57"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+ jsonb_path_query
+--------------------------
+ "2023-08-15T12:34:56.79"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+WARNING: TIMESTAMP(10) precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------
+ "2023-08-15T12:34:56.789"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+WARNING: TIMESTAMP(8) precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------
+ "2023-08-15T12:34:56.789012"
+(1 row)
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('true', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('1', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('[]', '$.timestamp_tz()');
+ jsonb_path_query
+------------------
+(0 rows)
+
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('{}', '$.timestamp_tz()');
+ERROR: jsonpath item method .timestamp_tz() can only be applied to a string
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "bogus"
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+ ?column?
+----------
+ t
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+ jsonb_path_query
+----------------------------
+ "timestamp with time zone"
+(1 row)
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T07:00:00+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56"
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+ERROR: timestamp_tz format is not recognized: "12:34:56 +05:30"
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+ERROR: syntax error at or near "-" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+ERROR: syntax error at or near "2.0" of jsonpath input
+LINE 1: ...nb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timesta...
+ ^
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+ERROR: time precision of jsonpath item method .timestamp_tz() is out of range for type integer
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:57+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+ jsonb_path_query
+--------------------------------
+ "2023-08-15T12:34:56.79+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+WARNING: TIMESTAMP(10) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+---------------------------------
+ "2023-08-15T12:34:56.789+05:30"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+WARNING: TIMESTAMP(8) WITH TIME ZONE precision reduced to maximum allowed, 6
+ jsonb_path_query
+------------------------------------
+ "2023-08-15T12:34:56.789012+05:30"
+(1 row)
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "07:04:56+00:00"
+(1 row)
+
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "12:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T07:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+00:00"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -1798,6 +3037,36 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "17:04:56+10:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T17:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T02:34:56+00:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
jsonb_path_query
-----------------------
@@ -1863,6 +3132,30 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
(1 row)
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+ jsonb_path_query
+------------------
+ "00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+ jsonb_path_query
+------------------
+ "00:04:56-07:00"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+ jsonb_path_query
+-----------------------
+ "2023-08-15T00:04:56"
+(1 row)
+
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+ jsonb_path_query
+-----------------------------
+ "2023-08-15T12:34:56+05:30"
+(1 row)
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
jsonb_path_query
------------------
@@ -2019,6 +3312,101 @@ select jsonb_path_query_tz(
"2017-03-10T01:02:03+04:00"
(2 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-10T00:00:00"
+ "2017-03-10T03:00:00+03:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10T00:00:00"
+ "2017-03-10T12:34:56"
+ "2017-03-10T03:00:00+03:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-09"
+ "2017-03-10T01:02:03+04:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query
+------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-10"
+ "2017-03-11"
+ "2017-03-10"
+ "2017-03-10"
+ "2017-03-10"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+ jsonb_path_query_tz
+---------------------
+ "2017-03-09"
+ "2017-03-09"
+(2 rows)
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -2031,38 +3419,144 @@ select jsonb_path_query(
ERROR: cannot convert value from time to timetz without time zone usage
HINT: Use *_tz() function for time zone support.
select jsonb_path_query(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
-ERROR: cannot convert value from time to timetz without time zone usage
-HINT: Use *_tz() function for time zone support.
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ERROR: cannot convert value from time to timetz without time zone usage
+HINT: Use *_tz() function for time zone support.
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00"
+ "12:35:00+01:00"
+ "13:35:00+01:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:35:00"
+ "12:36:00"
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+ jsonb_path_query
+------------------
+ "12:34:00"
+ "11:35:00"
+(2 rows)
+
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ == "12:35".datetime("HH24:MI"))');
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
jsonb_path_query_tz
---------------------
"12:35:00"
- "12:35:00+00:00"
-(2 rows)
+ "12:35:00"
+ "12:35:00"
+ "12:35:00"
+(4 rows)
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ >= "12:35".datetime("HH24:MI"))');
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
jsonb_path_query_tz
---------------------
"12:35:00"
"12:36:00"
- "12:35:00+00:00"
-(3 rows)
+ "12:35:00"
+ "12:35:00"
+ "13:35:00"
+ "12:35:00"
+(6 rows)
select jsonb_path_query_tz(
- '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
- '$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
jsonb_path_query_tz
---------------------
"12:34:00"
- "12:35:00+01:00"
- "13:35:00+01:00"
-(3 rows)
+ "11:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+ jsonb_path_query
+------------------
+ "12:35:00.12"
+ "12:36:00.11"
+ "12:35:00.12"
+ "13:35:00.12"
+(4 rows)
-- timetz comparison
select jsonb_path_query(
@@ -2110,6 +3604,110 @@ select jsonb_path_query_tz(
"10:35:00"
(3 rows)
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00"
+ "12:35:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query
+------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+(1 row)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:35:00+01:00"
+ "12:36:00+01:00"
+ "12:35:00-02:00"
+ "11:35:00+00:00"
+ "12:35:00+00:00"
+ "11:35:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+ jsonb_path_query_tz
+---------------------
+ "12:34:00+01:00"
+ "12:35:00+02:00"
+ "10:35:00+00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+ jsonb_path_query
+---------------------
+ "12:35:00.12+01:00"
+ "12:36:00.11+01:00"
+ "12:35:00.12-02:00"
+ "12:35:00.12+00:00"
+ "11:35:00.12+00:00"
+(5 rows)
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2157,6 +3755,111 @@ select jsonb_path_query_tz(
"2017-03-10"
(3 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00+01:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T13:35:00+01:00"
+ "2017-03-10T12:35:00-01:00"
+ "2017-03-11"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:35:00"
+ "2017-03-10T12:36:00"
+ "2017-03-10T12:35:00"
+ "2017-03-10T13:35:00"
+ "2017-03-11T00:00:00"
+(5 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+ jsonb_path_query_tz
+-----------------------
+ "2017-03-10T12:34:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T00:00:00"
+(3 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+ jsonb_path_query
+--------------------------
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T12:36:00.11"
+ "2017-03-10T12:35:00.12"
+ "2017-03-10T13:35:00.1"
+ "2017-03-11T00:00:00"
+(5 rows)
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -2206,6 +3909,117 @@ select jsonb_path_query_tz(
"2017-03-10"
(4 rows)
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00"
+ "2017-03-10T12:35:00"
+ "2017-03-11"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00"
+ "2017-03-10"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T11:35:00+00:00"
+(2 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:35:00+01:00"
+ "2017-03-10T12:36:00+01:00"
+ "2017-03-10T12:35:00-02:00"
+ "2017-03-10T11:35:00+00:00"
+ "2017-03-10T12:35:00+00:00"
+ "2017-03-11T00:00:00+00:00"
+(6 rows)
+
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+ jsonb_path_query_tz
+-----------------------------
+ "2017-03-10T12:34:00+01:00"
+ "2017-03-10T12:35:00+02:00"
+ "2017-03-10T10:35:00+00:00"
+ "2017-03-10T00:00:00+00:00"
+(4 rows)
+
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+ jsonb_path_query
+--------------------------------
+ "2017-03-10T12:35:00.12+01:00"
+ "2017-03-10T12:36:00.11+01:00"
+ "2017-03-10T12:35:00.12-02:00"
+ "2017-03-10T12:35:00.12+00:00"
+ "2017-03-11T00:00:00+00:00"
+(5 rows)
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
jsonb_path_query
diff --git a/src/test/regress/expected/jsonpath.out b/src/test/regress/expected/jsonpath.out
index eeffb38..fd9bd75 100644
--- a/src/test/regress/expected/jsonpath.out
+++ b/src/test/regress/expected/jsonpath.out
@@ -405,6 +405,84 @@ select '$.datetime("datetime template")'::jsonpath;
$.datetime("datetime template")
(1 row)
+select '$.bigint().integer().number().decimal()'::jsonpath;
+ jsonpath
+-----------------------------------------
+ $.bigint().integer().number().decimal()
+(1 row)
+
+select '$.boolean()'::jsonpath;
+ jsonpath
+-------------
+ $.boolean()
+(1 row)
+
+select '$.date()'::jsonpath;
+ jsonpath
+----------
+ $.date()
+(1 row)
+
+select '$.decimal(4,2)'::jsonpath;
+ jsonpath
+----------------
+ $.decimal(4,2)
+(1 row)
+
+select '$.string()'::jsonpath;
+ jsonpath
+------------
+ $.string()
+(1 row)
+
+select '$.time()'::jsonpath;
+ jsonpath
+----------
+ $.time()
+(1 row)
+
+select '$.time(6)'::jsonpath;
+ jsonpath
+-----------
+ $.time(6)
+(1 row)
+
+select '$.time_tz()'::jsonpath;
+ jsonpath
+-------------
+ $.time_tz()
+(1 row)
+
+select '$.time_tz(4)'::jsonpath;
+ jsonpath
+--------------
+ $.time_tz(4)
+(1 row)
+
+select '$.timestamp()'::jsonpath;
+ jsonpath
+---------------
+ $.timestamp()
+(1 row)
+
+select '$.timestamp(2)'::jsonpath;
+ jsonpath
+----------------
+ $.timestamp(2)
+(1 row)
+
+select '$.timestamp_tz()'::jsonpath;
+ jsonpath
+------------------
+ $.timestamp_tz()
+(1 row)
+
+select '$.timestamp_tz(0)'::jsonpath;
+ jsonpath
+-------------------
+ $.timestamp_tz(0)
+(1 row)
+
select '$ ? (@ starts with "abc")'::jsonpath;
jsonpath
-------------------------
diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql
index e0ce509..418eeac 100644
--- a/src/test/regress/sql/jsonb_jsonpath.sql
+++ b/src/test/regress/sql/jsonb_jsonpath.sql
@@ -372,8 +372,335 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2
select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")');
+-- Test .bigint()
+select jsonb_path_query('null', '$.bigint()');
+select jsonb_path_query('true', '$.bigint()');
+select jsonb_path_query('null', '$.bigint()', silent => true);
+select jsonb_path_query('true', '$.bigint()', silent => true);
+select jsonb_path_query('[]', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()');
+select jsonb_path_query('{}', '$.bigint()');
+select jsonb_path_query('[]', 'strict $.bigint()', silent => true);
+select jsonb_path_query('{}', '$.bigint()', silent => true);
+select jsonb_path_query('"1.23"', '$.bigint()');
+select jsonb_path_query('"1.23aaa"', '$.bigint()');
+select jsonb_path_query('1e1000', '$.bigint()');
+select jsonb_path_query('"nan"', '$.bigint()');
+select jsonb_path_query('"NaN"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()');
+select jsonb_path_query('"-inf"', '$.bigint()');
+select jsonb_path_query('"inf"', '$.bigint()', silent => true);
+select jsonb_path_query('"-inf"', '$.bigint()', silent => true);
+select jsonb_path_query('123', '$.bigint()');
+select jsonb_path_query('"123"', '$.bigint()');
+select jsonb_path_query('1.23', '$.bigint()');
+select jsonb_path_query('1.83', '$.bigint()');
+select jsonb_path_query('1234567890123', '$.bigint()');
+select jsonb_path_query('"1234567890123"', '$.bigint()');
+select jsonb_path_query('12345678901234567890', '$.bigint()');
+select jsonb_path_query('"12345678901234567890"', '$.bigint()');
+select jsonb_path_query('"+123"', '$.bigint()');
+select jsonb_path_query('-123', '$.bigint()');
+select jsonb_path_query('"-123"', '$.bigint()');
+select jsonb_path_query('123', '$.bigint() * 2');
+
+-- Test .boolean()
+select jsonb_path_query('null', '$.boolean()');
+select jsonb_path_query('null', '$.boolean()', silent => true);
+select jsonb_path_query('[]', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()');
+select jsonb_path_query('{}', '$.boolean()');
+select jsonb_path_query('[]', 'strict $.boolean()', silent => true);
+select jsonb_path_query('{}', '$.boolean()', silent => true);
+select jsonb_path_query('1.23', '$.boolean()');
+select jsonb_path_query('"1.23"', '$.boolean()');
+select jsonb_path_query('"1.23aaa"', '$.boolean()');
+select jsonb_path_query('1e1000', '$.boolean()');
+select jsonb_path_query('"nan"', '$.boolean()');
+select jsonb_path_query('"NaN"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()');
+select jsonb_path_query('"-inf"', '$.boolean()');
+select jsonb_path_query('"inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
+select jsonb_path_query('"100"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean()');
+select jsonb_path_query('false', '$.boolean()');
+select jsonb_path_query('1', '$.boolean()');
+select jsonb_path_query('0', '$.boolean()');
+select jsonb_path_query('-1', '$.boolean()');
+select jsonb_path_query('100', '$.boolean()');
+select jsonb_path_query('"1"', '$.boolean()');
+select jsonb_path_query('"0"', '$.boolean()');
+select jsonb_path_query('"true"', '$.boolean()');
+select jsonb_path_query('"false"', '$.boolean()');
+select jsonb_path_query('"TRUE"', '$.boolean()');
+select jsonb_path_query('"FALSE"', '$.boolean()');
+select jsonb_path_query('"yes"', '$.boolean()');
+select jsonb_path_query('"NO"', '$.boolean()');
+select jsonb_path_query('"T"', '$.boolean()');
+select jsonb_path_query('"f"', '$.boolean()');
+select jsonb_path_query('"y"', '$.boolean()');
+select jsonb_path_query('"N"', '$.boolean()');
+select jsonb_path_query('true', '$.boolean().type()');
+select jsonb_path_query('123', '$.boolean().type()');
+select jsonb_path_query('"Yes"', '$.boolean().type()');
+select jsonb_path_query_array('[1, "yes", false]', '$[*].boolean()');
+
+-- Test .date()
+select jsonb_path_query('null', '$.date()');
+select jsonb_path_query('true', '$.date()');
+select jsonb_path_query('1', '$.date()');
+select jsonb_path_query('[]', '$.date()');
+select jsonb_path_query('[]', 'strict $.date()');
+select jsonb_path_query('{}', '$.date()');
+select jsonb_path_query('"bogus"', '$.date()');
+
+select jsonb '"2023-08-15"' @? '$.date()';
+select jsonb_path_query('"2023-08-15"', '$.date()');
+select jsonb_path_query('"2023-08-15"', '$.date().type()');
+
+select jsonb_path_query('"12:34:56"', '$.date()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.date()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.date()');
+
+select jsonb_path_query('"2023-08-15"', '$.date(2)');
+
+-- Test .decimal()
+select jsonb_path_query('null', '$.decimal()');
+select jsonb_path_query('true', '$.decimal()');
+select jsonb_path_query('null', '$.decimal()', silent => true);
+select jsonb_path_query('true', '$.decimal()', silent => true);
+select jsonb_path_query('[]', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()');
+select jsonb_path_query('{}', '$.decimal()');
+select jsonb_path_query('[]', 'strict $.decimal()', silent => true);
+select jsonb_path_query('{}', '$.decimal()', silent => true);
+select jsonb_path_query('1.23', '$.decimal()');
+select jsonb_path_query('"1.23"', '$.decimal()');
+select jsonb_path_query('"1.23aaa"', '$.decimal()');
+select jsonb_path_query('1e1000', '$.decimal()');
+select jsonb_path_query('"nan"', '$.decimal()');
+select jsonb_path_query('"NaN"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()');
+select jsonb_path_query('"-inf"', '$.decimal()');
+select jsonb_path_query('"inf"', '$.decimal()', silent => true);
+select jsonb_path_query('"-inf"', '$.decimal()', silent => true);
+select jsonb_path_query('123', '$.decimal()');
+select jsonb_path_query('"123"', '$.decimal()');
+select jsonb_path_query('12345678901234567890', '$.decimal()');
+select jsonb_path_query('"12345678901234567890"', '$.decimal()');
+select jsonb_path_query('"+12.3"', '$.decimal()');
+select jsonb_path_query('-12.3', '$.decimal()');
+select jsonb_path_query('"-12.3"', '$.decimal()');
+select jsonb_path_query('12.3', '$.decimal() * 2');
+select jsonb_path_query('12345.678', '$.decimal(6, 1)');
+select jsonb_path_query('12345.678', '$.decimal(6, 2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
+select jsonb_path_query('12345.678', '$.decimal(4, 6)');
+select jsonb_path_query('12345.678', '$.decimal(0, 6)');
+select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('1234.5678', '$.decimal(-6, +2)');
+select jsonb_path_query('1234.5678', '$.decimal(6, -1001)');
+select jsonb_path_query('1234.5678', '$.decimal(6, 1001)');
+select jsonb_path_query('-1234.5678', '$.decimal(+6, -2)');
+select jsonb_path_query('0.0123456', '$.decimal(1,2)');
+select jsonb_path_query('0.0012345', '$.decimal(2,4)');
+select jsonb_path_query('-0.00123456', '$.decimal(2,-4)');
+select jsonb_path_query('12.3', '$.decimal(12345678901,1)');
+select jsonb_path_query('12.3', '$.decimal(1,12345678901)');
+
+-- Test .integer()
+select jsonb_path_query('null', '$.integer()');
+select jsonb_path_query('true', '$.integer()');
+select jsonb_path_query('null', '$.integer()', silent => true);
+select jsonb_path_query('true', '$.integer()', silent => true);
+select jsonb_path_query('[]', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()');
+select jsonb_path_query('{}', '$.integer()');
+select jsonb_path_query('[]', 'strict $.integer()', silent => true);
+select jsonb_path_query('{}', '$.integer()', silent => true);
+select jsonb_path_query('"1.23"', '$.integer()');
+select jsonb_path_query('"1.23aaa"', '$.integer()');
+select jsonb_path_query('1e1000', '$.integer()');
+select jsonb_path_query('"nan"', '$.integer()');
+select jsonb_path_query('"NaN"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()');
+select jsonb_path_query('"-inf"', '$.integer()');
+select jsonb_path_query('"inf"', '$.integer()', silent => true);
+select jsonb_path_query('"-inf"', '$.integer()', silent => true);
+select jsonb_path_query('123', '$.integer()');
+select jsonb_path_query('"123"', '$.integer()');
+select jsonb_path_query('1.23', '$.integer()');
+select jsonb_path_query('1.83', '$.integer()');
+select jsonb_path_query('12345678901', '$.integer()');
+select jsonb_path_query('"12345678901"', '$.integer()');
+select jsonb_path_query('"+123"', '$.integer()');
+select jsonb_path_query('-123', '$.integer()');
+select jsonb_path_query('"-123"', '$.integer()');
+select jsonb_path_query('123', '$.integer() * 2');
+
+-- Test .number()
+select jsonb_path_query('null', '$.number()');
+select jsonb_path_query('true', '$.number()');
+select jsonb_path_query('null', '$.number()', silent => true);
+select jsonb_path_query('true', '$.number()', silent => true);
+select jsonb_path_query('[]', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()');
+select jsonb_path_query('{}', '$.number()');
+select jsonb_path_query('[]', 'strict $.number()', silent => true);
+select jsonb_path_query('{}', '$.number()', silent => true);
+select jsonb_path_query('1.23', '$.number()');
+select jsonb_path_query('"1.23"', '$.number()');
+select jsonb_path_query('"1.23aaa"', '$.number()');
+select jsonb_path_query('1e1000', '$.number()');
+select jsonb_path_query('"nan"', '$.number()');
+select jsonb_path_query('"NaN"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()');
+select jsonb_path_query('"-inf"', '$.number()');
+select jsonb_path_query('"inf"', '$.number()', silent => true);
+select jsonb_path_query('"-inf"', '$.number()', silent => true);
+select jsonb_path_query('123', '$.number()');
+select jsonb_path_query('"123"', '$.number()');
+select jsonb_path_query('12345678901234567890', '$.number()');
+select jsonb_path_query('"12345678901234567890"', '$.number()');
+select jsonb_path_query('"+12.3"', '$.number()');
+select jsonb_path_query('-12.3', '$.number()');
+select jsonb_path_query('"-12.3"', '$.number()');
+select jsonb_path_query('12.3', '$.number() * 2');
+
+-- Test .string()
+select jsonb_path_query('null', '$.string()');
+select jsonb_path_query('null', '$.string()', silent => true);
+select jsonb_path_query('[]', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()');
+select jsonb_path_query('{}', '$.string()');
+select jsonb_path_query('[]', 'strict $.string()', silent => true);
+select jsonb_path_query('{}', '$.string()', silent => true);
+select jsonb_path_query('1.23', '$.string()');
+select jsonb_path_query('"1.23"', '$.string()');
+select jsonb_path_query('"1.23aaa"', '$.string()');
+select jsonb_path_query('1234', '$.string()');
+select jsonb_path_query('true', '$.string()');
+select jsonb_path_query('1234', '$.string().type()');
+select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string()');
+select jsonb_path_query_array('[1.23, "yes", false]', '$[*].string().type()');
+
+-- Test .time()
+select jsonb_path_query('null', '$.time()');
+select jsonb_path_query('true', '$.time()');
+select jsonb_path_query('1', '$.time()');
+select jsonb_path_query('[]', '$.time()');
+select jsonb_path_query('[]', 'strict $.time()');
+select jsonb_path_query('{}', '$.time()');
+select jsonb_path_query('"bogus"', '$.time()');
+
+select jsonb '"12:34:56"' @? '$.time()';
+select jsonb_path_query('"12:34:56"', '$.time()');
+select jsonb_path_query('"12:34:56"', '$.time().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time()');
+
+select jsonb_path_query('"12:34:56.789"', '$.time(-1)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2.0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(12345678901)');
+select jsonb_path_query('"12:34:56.789"', '$.time(0)');
+select jsonb_path_query('"12:34:56.789"', '$.time(2)');
+select jsonb_path_query('"12:34:56.789"', '$.time(5)');
+select jsonb_path_query('"12:34:56.789"', '$.time(10)');
+select jsonb_path_query('"12:34:56.789012"', '$.time(8)');
+
+-- Test .time_tz()
+select jsonb_path_query('null', '$.time_tz()');
+select jsonb_path_query('true', '$.time_tz()');
+select jsonb_path_query('1', '$.time_tz()');
+select jsonb_path_query('[]', '$.time_tz()');
+select jsonb_path_query('[]', 'strict $.time_tz()');
+select jsonb_path_query('{}', '$.time_tz()');
+select jsonb_path_query('"bogus"', '$.time_tz()');
+
+select jsonb '"12:34:56 +05:30"' @? '$.time_tz()';
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.time_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.time_tz()');
+
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(-1)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2.0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(12345678901)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(0)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(2)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(5)');
+select jsonb_path_query('"12:34:56.789 +05:30"', '$.time_tz(10)');
+select jsonb_path_query('"12:34:56.789012 +05:30"', '$.time_tz(8)');
+
+-- Test .timestamp()
+select jsonb_path_query('null', '$.timestamp()');
+select jsonb_path_query('true', '$.timestamp()');
+select jsonb_path_query('1', '$.timestamp()');
+select jsonb_path_query('[]', '$.timestamp()');
+select jsonb_path_query('[]', 'strict $.timestamp()');
+select jsonb_path_query('{}', '$.timestamp()');
+select jsonb_path_query('"bogus"', '$.timestamp()');
+
+select jsonb '"2023-08-15 12:34:56"' @? '$.timestamp()';
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp()');
+select jsonb_path_query('"12:34:56"', '$.timestamp()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789"', '$.timestamp(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012"', '$.timestamp(8)');
+
+-- Test .timestamp_tz()
+select jsonb_path_query('null', '$.timestamp_tz()');
+select jsonb_path_query('true', '$.timestamp_tz()');
+select jsonb_path_query('1', '$.timestamp_tz()');
+select jsonb_path_query('[]', '$.timestamp_tz()');
+select jsonb_path_query('[]', 'strict $.timestamp_tz()');
+select jsonb_path_query('{}', '$.timestamp_tz()');
+select jsonb_path_query('"bogus"', '$.timestamp_tz()');
+
+select jsonb '"2023-08-15 12:34:56 +05:30"' @? '$.timestamp_tz()';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz().type()');
+
+select jsonb_path_query('"2023-08-15"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"12:34:56 +05:30"', '$.timestamp_tz()');
+
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(-1)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2.0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(12345678901)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(0)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(2)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(5)');
+select jsonb_path_query('"2023-08-15 12:34:56.789 +05:30"', '$.timestamp_tz(10)');
+select jsonb_path_query('"2023-08-15 12:34:56.789012 +05:30"', '$.timestamp_tz(8)');
+
+
set time zone '+00';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"12:34:56"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -389,6 +716,12 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone '+10';
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI")');
select jsonb_path_query('"10-03-2017 12:34"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
select jsonb_path_query('"10-03-2017 12:34 +05"', '$.datetime("dd-mm-yyyy HH24:MI TZH")');
@@ -404,6 +737,11 @@ select jsonb_path_query('"12:34 -05:20"', '$.datetime("HH24:MI TZH:TZM")');
set time zone default;
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.time_tz()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp()');
+select jsonb_path_query('"2023-08-15 12:34:56 +05:30"', '$.timestamp_tz()');
+
select jsonb_path_query('"2017-03-10"', '$.datetime().type()');
select jsonb_path_query('"2017-03-10"', '$.datetime()');
select jsonb_path_query('"2017-03-10 12:34:56"', '$.datetime().type()');
@@ -446,6 +784,34 @@ select jsonb_path_query_tz(
'["2017-03-10", "2017-03-11", "2017-03-09", "12:34:56", "01:02:03+04", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
'$[*].datetime() ? (@ < "10.03.2017".datetime("dd.mm.yyyy"))');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].datetime() ? (@ < "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ == "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ >= "2017-03-10".date())');
+select jsonb_path_query_tz(
+ '["2017-03-10", "2017-03-11", "2017-03-09", "2017-03-10 00:00:00", "2017-03-10 12:34:56", "2017-03-10 01:02:03+04", "2017-03-10 03:00:00+03"]',
+ '$[*].date() ? (@ < "2017-03-10".date())');
+
-- time comparison
select jsonb_path_query(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
@@ -466,6 +832,38 @@ select jsonb_path_query_tz(
'["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
'$[*].datetime() ? (@ < "12:35".datetime("HH24:MI"))');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].datetime() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ == "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ >= "12:35:00".time())');
+select jsonb_path_query_tz(
+ '["12:34:00", "12:35:00", "12:36:00", "12:35:00+00", "12:35:00+01", "13:35:00+01", "2017-03-10 12:35:00", "2017-03-10 12:35:00+01"]',
+ '$[*].time() ? (@ < "12:35:00".time())');
+select jsonb_path_query(
+ '["12:34:00.123", "12:35:00.123", "12:36:00.1123", "12:35:00.1123+00", "12:35:00.123+01", "13:35:00.123+01", "2017-03-10 12:35:00.1", "2017-03-10 12:35:00.123+01"]',
+ '$[*].time(2) ? (@ >= "12:35:00.123".time(2))');
+
+
-- timetz comparison
select jsonb_path_query(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
@@ -486,6 +884,37 @@ select jsonb_path_query_tz(
'["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10", "2017-03-10 12:35:00", "2017-03-10 12:35:00 +1"]',
'$[*].datetime() ? (@ < "12:35 +1".datetime("HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].datetime() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ == "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ >= "12:35:00 +1".time_tz())');
+select jsonb_path_query_tz(
+ '["12:34:00+01", "12:35:00+01", "12:36:00+01", "12:35:00+02", "12:35:00-02", "10:35:00", "11:35:00", "12:35:00", "2017-03-10 12:35:00 +1"]',
+ '$[*].time_tz() ? (@ < "12:35:00 +1".time_tz())');
+select jsonb_path_query(
+ '["12:34:00.123+01", "12:35:00.123+01", "12:36:00.1123+01", "12:35:00.1123+02", "12:35:00.123-02", "10:35:00.123", "11:35:00.1", "12:35:00.123", "2017-03-10 12:35:00.123 +1"]',
+ '$[*].time_tz(2) ? (@ >= "12:35:00.123 +1".time_tz(2))');
+
-- timestamp comparison
select jsonb_path_query(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -506,6 +935,37 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35".datetime("dd.mm.yyyy HH24:MI"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ == "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ >= "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00", "2017-03-10 12:35:00", "2017-03-10 12:36:00", "2017-03-10 12:35:00+01", "2017-03-10 13:35:00+01", "2017-03-10 12:35:00-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp() ? (@ < "2017-03-10 12:35:00".timestamp())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123", "2017-03-10 12:35:00.123", "2017-03-10 12:36:00.1123", "2017-03-10 12:35:00.1123+01", "2017-03-10 13:35:00.123+01", "2017-03-10 12:35:00.1-01", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp(2) ? (@ >= "2017-03-10 12:35:00.123".timestamp(2))');
+
-- timestamptz comparison
select jsonb_path_query(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
@@ -526,6 +986,38 @@ select jsonb_path_query_tz(
'["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11", "12:34:56", "12:34:56+01"]',
'$[*].datetime() ? (@ < "10.03.2017 12:35 +1".datetime("dd.mm.yyyy HH24:MI TZH"))');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].datetime() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ == "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ >= "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query_tz(
+ '["2017-03-10 12:34:00+01", "2017-03-10 12:35:00+01", "2017-03-10 12:36:00+01", "2017-03-10 12:35:00+02", "2017-03-10 12:35:00-02", "2017-03-10 10:35:00", "2017-03-10 11:35:00", "2017-03-10 12:35:00", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz() ? (@ < "2017-03-10 12:35:00 +1".timestamp_tz())');
+select jsonb_path_query(
+ '["2017-03-10 12:34:00.123+01", "2017-03-10 12:35:00.123+01", "2017-03-10 12:36:00.1123+01", "2017-03-10 12:35:00.1123+02", "2017-03-10 12:35:00.123-02", "2017-03-10 10:35:00.123", "2017-03-10 11:35:00.1", "2017-03-10 12:35:00.123", "2017-03-10", "2017-03-11"]',
+ '$[*].timestamp_tz(2) ? (@ >= "2017-03-10 12:35:00.123 +1".timestamp_tz(2))');
+
+
-- overflow during comparison
select jsonb_path_query('"1000000-01-01"', '$.datetime() > "2020-01-01 12:00:00".datetime()'::jsonpath);
diff --git a/src/test/regress/sql/jsonpath.sql b/src/test/regress/sql/jsonpath.sql
index 56e0bef..61a5270 100644
--- a/src/test/regress/sql/jsonpath.sql
+++ b/src/test/regress/sql/jsonpath.sql
@@ -73,6 +73,19 @@ select '$.double().floor().ceiling().abs()'::jsonpath;
select '$.keyvalue().key'::jsonpath;
select '$.datetime()'::jsonpath;
select '$.datetime("datetime template")'::jsonpath;
+select '$.bigint().integer().number().decimal()'::jsonpath;
+select '$.boolean()'::jsonpath;
+select '$.date()'::jsonpath;
+select '$.decimal(4,2)'::jsonpath;
+select '$.string()'::jsonpath;
+select '$.time()'::jsonpath;
+select '$.time(6)'::jsonpath;
+select '$.time_tz()'::jsonpath;
+select '$.time_tz(4)'::jsonpath;
+select '$.timestamp()'::jsonpath;
+select '$.timestamp(2)'::jsonpath;
+select '$.timestamp_tz()'::jsonpath;
+select '$.timestamp_tz(0)'::jsonpath;
select '$ ? (@ starts with "abc")'::jsonpath;
select '$ ? (@ starts with $var)'::jsonpath;
--
1.8.3.1
On 18.01.24 15:25, Jeevan Chalke wrote:
Peter, I didn't understand why the changes you did in your 0002 patch
were required here. I did run the pgindent, and it didn't complain to
me. So, just curious to know more about the changes. I have not merged
those changes in this single patch. However, if needed it can be cleanly
applied on top of this single patch.
I just thought it was a bit wasteful with vertical space. It's not
essential.
On 2024-01-18 Th 09:25, Jeevan Chalke wrote:
On Thu, Jan 18, 2024 at 1:03 AM Peter Eisentraut
<peter@eisentraut.org> wrote:On 17.01.24 10:03, Jeevan Chalke wrote:
I added unary '+' and '-' support as well and thus thought of
having
separate rules altogether rather than folding those in.
Per SQL standard, the precision and scale arguments are unsigned
integers, so unary plus and minus signs are not supported.So my patch
removes that support, but I didn't adjust the regression
tests for that.
However, PostgreSQL numeric casting does support a negative
scale. Here
is an example:
# select '12345'::numeric(4,-2);
numeric
---------
12300
(1 row)And thus thought of supporting those.
Do we want this JSON item method to behave differently here?Ok, it would make sense to support this in SQL/JSON as well.
OK. So with this, we don't need changes done in your 0001 patches.
I will merge them all into one and will try to keep them in the
order
specified in sql_features.txt.
However, for documentation, it makes more sense to keep them inlogical
order than the alphabetical one. What are your views on this?
The documentation can be in a different order.
Thanks, Andrew and Peter for the confirmation.
Attached merged single patch along these lines.
Thanks, I have pushed this.
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
Andrew Dunstan <andrew@dunslane.net> writes:
Thanks, I have pushed this.
The buildfarm is pretty widely unhappy, mostly failing on
select jsonb_path_query('1.23', '$.string()');
On a guess, I tried running that under valgrind, and behold it said
==00:00:00:05.637 435530== Conditional jump or move depends on uninitialised value(s)
==00:00:00:05.637 435530== at 0x8FD131: executeItemOptUnwrapTarget (jsonpath_exec.c:1547)
==00:00:00:05.637 435530== by 0x8FED03: executeItem (jsonpath_exec.c:626)
==00:00:00:05.637 435530== by 0x8FED03: executeNextItem (jsonpath_exec.c:1604)
==00:00:00:05.637 435530== by 0x8FCA58: executeItemOptUnwrapTarget (jsonpath_exec.c:956)
==00:00:00:05.637 435530== by 0x8FFDE4: executeItem (jsonpath_exec.c:626)
==00:00:00:05.637 435530== by 0x8FFDE4: executeJsonPath.constprop.30 (jsonpath_exec.c:612)
==00:00:00:05.637 435530== by 0x8FFF8C: jsonb_path_query_internal (jsonpath_exec.c:438)
It's fairly obviously right about that:
JsonbValue jbv;
...
jb = &jbv;
Assert(tmp != NULL); /* We must have set tmp above */
jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
^^^^^^^^^^^^^^^^^^^^^
Presumably, this is a mistaken attempt to test the type
of the thing previously pointed to by "jb".
On the whole, what I'd be inclined to do here is get rid
of this test altogether and demand that every path through
the preceding "switch" deliver a value that doesn't need
pstrdup(). The only path that doesn't do that already is
case jbvBool:
tmp = (jb->val.boolean) ? "true" : "false";
break;
and TBH I'm not sure that we really need a pstrdup there
either. The constants are immutable enough. Is something
likely to try to pfree the pointer later? I tried
@@ -1544,7 +1544,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
jb = &jbv;
Assert(tmp != NULL); /* We must have set tmp above */
- jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
+ jb->val.string.val = tmp;
jb->val.string.len = strlen(jb->val.string.val);
jb->type = jbvString;
and that quieted valgrind for this particular query and still
passes regression.
(The reported crashes seem to be happening later during a
recursive invocation, seemingly because JsonbType(jb) is
returning garbage. So there may be another bug after this one.)
regards, tom lane
On 2024-01-25 Th 14:31, Tom Lane wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
Thanks, I have pushed this.
The buildfarm is pretty widely unhappy, mostly failing on
select jsonb_path_query('1.23', '$.string()');
On a guess, I tried running that under valgrind, and behold it said
==00:00:00:05.637 435530== Conditional jump or move depends on uninitialised value(s)
==00:00:00:05.637 435530== at 0x8FD131: executeItemOptUnwrapTarget (jsonpath_exec.c:1547)
==00:00:00:05.637 435530== by 0x8FED03: executeItem (jsonpath_exec.c:626)
==00:00:00:05.637 435530== by 0x8FED03: executeNextItem (jsonpath_exec.c:1604)
==00:00:00:05.637 435530== by 0x8FCA58: executeItemOptUnwrapTarget (jsonpath_exec.c:956)
==00:00:00:05.637 435530== by 0x8FFDE4: executeItem (jsonpath_exec.c:626)
==00:00:00:05.637 435530== by 0x8FFDE4: executeJsonPath.constprop.30 (jsonpath_exec.c:612)
==00:00:00:05.637 435530== by 0x8FFF8C: jsonb_path_query_internal (jsonpath_exec.c:438)It's fairly obviously right about that:
JsonbValue jbv;
...
jb = &jbv;
Assert(tmp != NULL); /* We must have set tmp above */
jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
^^^^^^^^^^^^^^^^^^^^^Presumably, this is a mistaken attempt to test the type
of the thing previously pointed to by "jb".On the whole, what I'd be inclined to do here is get rid
of this test altogether and demand that every path through
the preceding "switch" deliver a value that doesn't need
pstrdup(). The only path that doesn't do that already iscase jbvBool:
tmp = (jb->val.boolean) ? "true" : "false";
break;and TBH I'm not sure that we really need a pstrdup there
either. The constants are immutable enough. Is something
likely to try to pfree the pointer later? I tried@@ -1544,7 +1544,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
jb = &jbv; Assert(tmp != NULL); /* We must have set tmp above */ - jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp); + jb->val.string.val = tmp; jb->val.string.len = strlen(jb->val.string.val); jb->type = jbvString;and that quieted valgrind for this particular query and still
passes regression.(The reported crashes seem to be happening later during a
recursive invocation, seemingly because JsonbType(jb) is
returning garbage. So there may be another bug after this one.)
Argh! Will look, thanks.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On 2024-01-25 Th 14:31, Tom Lane wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
Thanks, I have pushed this.
The buildfarm is pretty widely unhappy, mostly failing on
select jsonb_path_query('1.23', '$.string()');
On a guess, I tried running that under valgrind, and behold it said
==00:00:00:05.637 435530== Conditional jump or move depends on uninitialised value(s)
==00:00:00:05.637 435530== at 0x8FD131: executeItemOptUnwrapTarget (jsonpath_exec.c:1547)
==00:00:00:05.637 435530== by 0x8FED03: executeItem (jsonpath_exec.c:626)
==00:00:00:05.637 435530== by 0x8FED03: executeNextItem (jsonpath_exec.c:1604)
==00:00:00:05.637 435530== by 0x8FCA58: executeItemOptUnwrapTarget (jsonpath_exec.c:956)
==00:00:00:05.637 435530== by 0x8FFDE4: executeItem (jsonpath_exec.c:626)
==00:00:00:05.637 435530== by 0x8FFDE4: executeJsonPath.constprop.30 (jsonpath_exec.c:612)
==00:00:00:05.637 435530== by 0x8FFF8C: jsonb_path_query_internal (jsonpath_exec.c:438)It's fairly obviously right about that:
JsonbValue jbv;
...
jb = &jbv;
Assert(tmp != NULL); /* We must have set tmp above */
jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp);
^^^^^^^^^^^^^^^^^^^^^Presumably, this is a mistaken attempt to test the type
of the thing previously pointed to by "jb".On the whole, what I'd be inclined to do here is get rid
of this test altogether and demand that every path through
the preceding "switch" deliver a value that doesn't need
pstrdup(). The only path that doesn't do that already iscase jbvBool:
tmp = (jb->val.boolean) ? "true" : "false";
break;and TBH I'm not sure that we really need a pstrdup there
either. The constants are immutable enough. Is something
likely to try to pfree the pointer later? I tried@@ -1544,7 +1544,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
jb = &jbv; Assert(tmp != NULL); /* We must have set tmp above */ - jb->val.string.val = (jb->type == jbvString) ? tmp : pstrdup(tmp); + jb->val.string.val = tmp; jb->val.string.len = strlen(jb->val.string.val); jb->type = jbvString;and that quieted valgrind for this particular query and still
passes regression.
Your fix looks sane. I also don't see why we need the pstrdup.
(The reported crashes seem to be happening later during a
recursive invocation, seemingly because JsonbType(jb) is
returning garbage. So there may be another bug after this one.)
I don't think so. AIUI The first call deals with the '$' and the second
one deals with the '.string()', which is why we see the error on the
second call.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
Andrew Dunstan <andrew@dunslane.net> writes:
On 2024-01-25 Th 14:31, Tom Lane wrote:
(The reported crashes seem to be happening later during a
recursive invocation, seemingly because JsonbType(jb) is
returning garbage. So there may be another bug after this one.)
I don't think so. AIUI The first call deals with the '$' and the second
one deals with the '.string()', which is why we see the error on the
second call.
There's something else going on, because I'm still getting the
assertion failure on my Mac with this fix in place. Annoyingly,
it goes away if I compile with -O0, so it's kind of hard to
identify what's going wrong.
regards, tom lane
I wrote:
There's something else going on, because I'm still getting the
assertion failure on my Mac with this fix in place. Annoyingly,
it goes away if I compile with -O0, so it's kind of hard to
identify what's going wrong.
No, belay that: I must've got confused about which version I was
testing. It's very unclear to me why the undefined reference
causes the preceding Assert to misbehave, but that is clearly
what's happening. Compiler bug maybe? My Mac has clang 15.0.0,
and the unhappy buildfarm members are also late-model clang.
Anyway, I did note that the preceding line
res = jperOk;
is dead code and might as well get removed while you're at it.
regards, tom lane
On 2024-01-25 Th 15:33, Tom Lane wrote:
Andrew Dunstan <andrew@dunslane.net> writes:
On 2024-01-25 Th 14:31, Tom Lane wrote:
(The reported crashes seem to be happening later during a
recursive invocation, seemingly because JsonbType(jb) is
returning garbage. So there may be another bug after this one.)I don't think so. AIUI The first call deals with the '$' and the second
one deals with the '.string()', which is why we see the error on the
second call.There's something else going on, because I'm still getting the
assertion failure on my Mac with this fix in place. Annoyingly,
it goes away if I compile with -O0, so it's kind of hard to
identify what's going wrong.
Curiouser and curiouser. On my Mac the error is manifest but the fix you
suggested cures it. Built with -O2 -g, clang 15.0.0, Apple Silicon.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On 2024-01-25 Th 15:58, Tom Lane wrote:
I wrote:
There's something else going on, because I'm still getting the
assertion failure on my Mac with this fix in place. Annoyingly,
it goes away if I compile with -O0, so it's kind of hard to
identify what's going wrong.No, belay that: I must've got confused about which version I was
testing. It's very unclear to me why the undefined reference
causes the preceding Assert to misbehave, but that is clearly
what's happening. Compiler bug maybe? My Mac has clang 15.0.0,
and the unhappy buildfarm members are also late-model clang.Anyway, I did note that the preceding line
res = jperOk;
is dead code and might as well get removed while you're at it.
OK, pushed those. Thanks.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
On Fri, Jan 26, 2024 at 2:57 AM Andrew Dunstan <andrew@dunslane.net> wrote:
On 2024-01-25 Th 15:58, Tom Lane wrote:
I wrote:
There's something else going on, because I'm still getting the
assertion failure on my Mac with this fix in place. Annoyingly,
it goes away if I compile with -O0, so it's kind of hard to
identify what's going wrong.No, belay that: I must've got confused about which version I was
testing. It's very unclear to me why the undefined reference
causes the preceding Assert to misbehave, but that is clearly
what's happening. Compiler bug maybe? My Mac has clang 15.0.0,
and the unhappy buildfarm members are also late-model clang.Anyway, I did note that the preceding line
res = jperOk;
is dead code and might as well get removed while you're at it.
OK, pushed those. Thanks.
Thank you all.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
I have two possible issues in a recent commit.
Commit 66ea94e8e6 has introduced the following messages:
(Aplogizies in advance if the commit is not related to this thread.)
jsonpath_exec.c:1287
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
jspOperationName(jsp->type)))));
:1387
noerr = DirectInputFunctionCallSafe(numeric_in, numstr,
...
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
They seem to be suggesting that PostgreSQL has the types "decimal" and
"number". I know of the former, but I don't think PostgreSQL has the
latter type. Perhaps the "number" was intended to refer to "numeric"?
(And I think it is largely helpful if the given string were shown in
the error message, but it would be another issue.)
The same commit has introduced the following set of messages:
%s format is not recognized: "%s"
date format is not recognized: "%s"
time format is not recognized: "%s"
time_tz format is not recognized: "%s"
timestamp format is not recognized: "%s"
timestamp_tz format is not recognized: "%s"
I believe that the first line was intended to cover all the others:p
They are attached to this message separately.
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
Attachments:
jsonpath_exec_fix_a_type_name.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 22f598cd35..c10926a8a2 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1287,7 +1287,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
+ errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or numeric",
jspOperationName(jsp->type)))));
if (jsp->type == jpiDecimal)
@@ -1312,14 +1312,14 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or numeric",
jspOperationName(jsp->type)))));
num = DatumGetNumeric(datum);
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or numeric",
jspOperationName(jsp->type)))));
res = jperOk;
@@ -1401,7 +1401,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or numeric",
jspOperationName(jsp->type)))));
num = DatumGetNumeric(numdatum);
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index eea2af30c8..9d8ce48a25 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -2144,7 +2144,7 @@ select jsonb_path_query('"1.23"', '$.decimal()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('1e1000', '$.decimal()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2152,13 +2152,13 @@ select jsonb_path_query('1e1000', '$.decimal()');
(1 row)
select jsonb_path_query('"nan"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('"NaN"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('"inf"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('"-inf"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('"inf"', '$.decimal()', silent => true);
jsonb_path_query
------------------
@@ -2224,7 +2224,7 @@ select jsonb_path_query('12345.678', '$.decimal(6, 1)');
(1 row)
select jsonb_path_query('12345.678', '$.decimal(6, 2)');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
jsonb_path_query
------------------
@@ -2232,7 +2232,7 @@ select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
(1 row)
select jsonb_path_query('12345.678', '$.decimal(4, 6)');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or numeric
select jsonb_path_query('12345.678', '$.decimal(0, 6)');
ERROR: NUMERIC precision 0 must be between 1 and 1000
select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
@@ -2440,7 +2440,7 @@ select jsonb_path_query('"1.23"', '$.number()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or numeric
select jsonb_path_query('1e1000', '$.number()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2448,13 +2448,13 @@ select jsonb_path_query('1e1000', '$.number()');
(1 row)
select jsonb_path_query('"nan"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or numeric
select jsonb_path_query('"NaN"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or numeric
select jsonb_path_query('"inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or numeric
select jsonb_path_query('"-inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or numeric
select jsonb_path_query('"inf"', '$.number()', silent => true);
jsonb_path_query
------------------
jsonpath_exec_merge_msgs_in_same_pattern.patchtext/x-patch; charset=us-asciiDownload
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index c10926a8a2..4a2a995325 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -2368,8 +2368,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (jsp->type == jpiDatetime)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
+ errmsg("%s format is not recognized: \"%s\"",
+ "datetime", text_to_cstring(datetime)),
errhint("Use a datetime template argument to specify the input data format."))));
else
RETURN_ERROR(ereport(ERROR,
@@ -2401,8 +2401,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("date format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "date", text_to_cstring(datetime)))));
break;
case TIMESTAMPOID:
value = DirectFunctionCall1(timestamp_date,
@@ -2427,8 +2427,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case DATEOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("time format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "time", text_to_cstring(datetime)))));
break;
case TIMEOID: /* Nothing to do for TIME */
break;
@@ -2476,8 +2476,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMESTAMPOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("time_tz format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "time_tz", text_to_cstring(datetime)))));
break;
case TIMEOID:
value = DirectFunctionCall1(time_timetz,
@@ -2525,8 +2525,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("timestamp format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "timestamp", text_to_cstring(datetime)))));
break;
case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
break;
@@ -2577,8 +2577,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("timestamp_tz format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "timestamp_tz", text_to_cstring(datetime)))));
break;
case TIMESTAMPOID:
value = DirectFunctionCall1(timestamp_timestamptz,
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
I have two possible issues in a recent commit.
Commit 66ea94e8e6 has introduced the following messages:
errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
They seem to be suggesting that PostgreSQL has the types "decimal" and
"number". I know of the former, but I don't think PostgreSQL has the
latter type. Perhaps the "number" was intended to refer to "numeric"?
Probably. But I would write just "type numeric". We do not generally
acknowledge "decimal" as a separate type, because for us it's only an
alias for numeric (there is not a pg_type entry for it).
Also, that leads to the thought that "numeric argument ... is out of
range for type numeric" seems either redundant or contradictory
depending on how you look at it. So I suggest wording like
argument "...input string here..." of jsonpath item method .%s() is out of range for type numeric
(And I think it is largely helpful if the given string were shown in
the error message, but it would be another issue.)
Agreed, so I suggest the above.
The same commit has introduced the following set of messages:
%s format is not recognized: "%s"
date format is not recognized: "%s"
time format is not recognized: "%s"
time_tz format is not recognized: "%s"
timestamp format is not recognized: "%s"
timestamp_tz format is not recognized: "%s"
I believe that the first line was intended to cover all the others:p
+1
regards, tom lane
At Sun, 28 Jan 2024 22:47:02 -0500, Tom Lane <tgl@sss.pgh.pa.us>
wrote in
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
They seem to be suggesting that PostgreSQL has the types
"decimal" and
"number". I know of the former, but I don't think PostgreSQL has
the
latter type. Perhaps the "number" was intended to refer to
"numeric"?
Probably. But I would write just "type numeric". We do not
generally
acknowledge "decimal" as a separate type, because for us it's only
an
alias for numeric (there is not a pg_type entry for it).Also, that leads to the thought that "numeric argument ... is out of
range for type numeric" seems either redundant or contradictory
depending on how you look at it. So I suggest wording likeargument "...input string here..." of jsonpath item method .%s() is
out of range for type numeric(And I think it is largely helpful if the given string were shown
in
the error message, but it would be another issue.)
Agreed, so I suggest the above.
Having said that, I'm a bit concerned about the case where an overly
long string is given there. However, considering that we already have
"invalid input syntax for type xxx: %x" messages (including for the
numeric), this concern might be unnecessary.
Another concern is that the input value is already a numeric, not a
string. This situation occurs when the input is NaN of +-Inf. Although
numeric_out could be used, it might cause another error. Therefore,
simply writing out as "argument NaN and Infinity.." would be better.
Once we resolve these issues, another question arises regarding on of
the messages. In the case of NaN of Infinity, the message will be as
the follows:
"argument NaN or Infinity of jsonpath item method .%s() is out of
range for type numeric"
This message appears quite strange and puzzling. I suspect that the
intended message was somewhat different.
The same commit has introduced the following set of messages:
%s format is not recognized: "%s"
date format is not recognized: "%s"
time format is not recognized: "%s"
time_tz format is not recognized: "%s"
timestamp format is not recognized: "%s"
timestamp_tz format is not recognized: "%s"I believe that the first line was intended to cover all the
others:p
+1
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
Having said that, I'm a bit concerned about the case where an overly
long string is given there. However, considering that we already have
"invalid input syntax for type xxx: %x" messages (including for the
numeric), this concern might be unnecessary.
Yeah, we have not worried about that in the past.
Another concern is that the input value is already a numeric, not a
string. This situation occurs when the input is NaN of +-Inf. Although
numeric_out could be used, it might cause another error. Therefore,
simply writing out as "argument NaN and Infinity.." would be better.
Oh! I'd assumed that we were discussing a string that we'd failed to
convert to numeric. If the input is already numeric, then either
the error is unreachable or what we're really doing is rejecting
special values such as NaN on policy grounds. I would ask first
if that policy is sane at all. (I'd lean to "not" --- if we allow
it in the input JSON, why not in the output?) If it is sane, the
error message needs to be far more specific.
regards, tom lane
On Mon, Jan 29, 2024 at 11:03 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
Having said that, I'm a bit concerned about the case where an overly
long string is given there. However, considering that we already have
"invalid input syntax for type xxx: %x" messages (including for the
numeric), this concern might be unnecessary.Yeah, we have not worried about that in the past.
Another concern is that the input value is already a numeric, not a
string. This situation occurs when the input is NaN of +-Inf. Although
numeric_out could be used, it might cause another error. Therefore,
simply writing out as "argument NaN and Infinity.." would be better.Oh! I'd assumed that we were discussing a string that we'd failed to
convert to numeric. If the input is already numeric, then either
the error is unreachable or what we're really doing is rejecting
special values such as NaN on policy grounds. I would ask first
if that policy is sane at all. (I'd lean to "not" --- if we allow
it in the input JSON, why not in the output?) If it is sane, the
error message needs to be far more specific.regards, tom lane
*Consistent error message related to type:*
Agree that the number is not a PostgreSQL type and needs a change. As Tom
suggested, we can say "type numeric" here. However, I have seen two
variants of error messages here: (1) When the input is numeric and (2) when
the input is string. For first, we have error messages like:
numeric argument of jsonpath item method .%s() is out of range for type
double precision
and for the second, it is like:
string argument of jsonpath item method .%s() is not a valid representation
of a double precision number
The second form says "double precision number". So, in the decimal/number
case, should we use "numeric number" and then similarly "big integer
number"?
What if we use phrases like "for type double precision" at both places,
like:
numeric argument of jsonpath item method .%s() is out of range for type
double precision
string argument of jsonpath item method .%s() is not a valid representation
for type double precision
With this, the rest will be like:
for type numeric
for type bigint
for type integer
Suggestions?
---
*Showing input string in the error message:*
argument "...input string here..." of jsonpath item method .%s() is out of
range for type numeric
If we add the input string in the error, then for some errors, it will be
too big, for example:
-ERROR: numeric argument of jsonpath item method .double() is out of range
for type double precision
+ERROR: argument
"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
of jsonpath item method .double() is out of range for type double precision
Also, for non-string input, we need to convert numeric to string just for
the error message, which seems overkill.
On another note, irrespective of these changes, is it good to show the
given input in the error messages? Error messages are logged and may leak
some details.
I think the existing way seems ok.
---
*NaN and Infinity restrictions:*
I am not sure why NaN and Infinity are not allowed in conversion to double
precision (.double() method). I have used the same restriction for
.decimal() and .number(). However, as you said, we should have error
messages more specific. I tried that in the attached patch; please have
your views. I have the following wordings for that error message:
"NaN or Infinity is not allowed for jsonpath item method .%s()"
Suggestions...
Thanks
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
Attachments:
improve_error_for_Nan_Infinity.patch.noapplication/octet-stream; name=improve_error_for_Nan_Infinity.patch.noDownload
commit c1ca75abf0b77b9359b457441aefd3968d0b5a58
Author: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Tue Jan 30 09:24:00 2024 +0530
Improve error message for NaN or Infinity inputs for jsonpath item.
Jeevan Chalke, per suggestion from Tom Lane.
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 23d375d..0567b17 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1049,11 +1049,16 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
tmp,
(Node *) &escontext);
- if (escontext.error_occurred || isinf(val) || isnan(val))
+ if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
jspOperationName(jsp->type)))));
+ if (isinf(val) || isnan(val))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
+ jspOperationName(jsp->type)))));
res = jperOk;
}
else if (jb->type == jbvString)
@@ -1070,11 +1075,16 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
tmp,
(Node *) &escontext);
- if (escontext.error_occurred || isinf(val) || isnan(val))
+ if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
jspOperationName(jsp->type)))));
+ if (isinf(val) || isnan(val))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
+ jspOperationName(jsp->type)))));
jb = &jbv;
jb->type = jbvNumeric;
@@ -1287,7 +1297,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
if (jsp->type == jpiDecimal)
@@ -1319,7 +1329,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
res = jperOk;
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index eea2af3..95408ad 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1500,13 +1500,13 @@ ERROR: string argument of jsonpath item method .double() is not a valid represe
select jsonb_path_query('1e1000', '$.double()');
ERROR: numeric argument of jsonpath item method .double() is out of range for type double precision
select jsonb_path_query('"nan"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"NaN"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"inf"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"-inf"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"inf"', '$.double()', silent => true);
jsonb_path_query
------------------
@@ -2152,13 +2152,13 @@ select jsonb_path_query('1e1000', '$.decimal()');
(1 row)
select jsonb_path_query('"nan"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"NaN"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"inf"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"-inf"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"inf"', '$.decimal()', silent => true);
jsonb_path_query
------------------
@@ -2448,13 +2448,13 @@ select jsonb_path_query('1e1000', '$.number()');
(1 row)
select jsonb_path_query('"nan"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"NaN"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"-inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"inf"', '$.number()', silent => true);
jsonb_path_query
------------------
Thank you for the fix!
At Tue, 30 Jan 2024 13:46:17 +0530, Jeevan Chalke <jeevan.chalke@enterprisedb.com> wrote in
On Mon, Jan 29, 2024 at 11:03 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
Having said that, I'm a bit concerned about the case where an overly
long string is given there. However, considering that we already have
"invalid input syntax for type xxx: %x" messages (including for the
numeric), this concern might be unnecessary.Yeah, we have not worried about that in the past.
Another concern is that the input value is already a numeric, not a
string. This situation occurs when the input is NaN of +-Inf. Although
numeric_out could be used, it might cause another error. Therefore,
simply writing out as "argument NaN and Infinity.." would be better.Oh! I'd assumed that we were discussing a string that we'd failed to
convert to numeric. If the input is already numeric, then either
the error is unreachable or what we're really doing is rejecting
special values such as NaN on policy grounds. I would ask first
if that policy is sane at all. (I'd lean to "not" --- if we allow
it in the input JSON, why not in the output?) If it is sane, the
error message needs to be far more specific.regards, tom lane
*Consistent error message related to type:*
...
What if we use phrases like "for type double precision" at both places,
like:
numeric argument of jsonpath item method .%s() is out of range for type
double precision
string argument of jsonpath item method .%s() is not a valid representation
for type double precisionWith this, the rest will be like:
for type numeric
for type bigint
for type integerSuggestions?
FWIW, I prefer consistently using "for type hoge".
---
*Showing input string in the error message:*
argument "...input string here..." of jsonpath item method .%s() is out of
range for type numericIf we add the input string in the error, then for some errors, it will be
too big, for example:-ERROR: numeric argument of jsonpath item method .double() is out of range for type double precision +ERROR: argument "100000<many zeros follow>" of jsonpath item method .double() is out of range for type double precision
As Tom suggested, given that similar situations have already been
disregarded elsewhere, worrying about excessively long input strings
in this specific instance won't notably improve safety in total.
Also, for non-string input, we need to convert numeric to string just for
the error message, which seems overkill.
As I suggested and you seem to agree, using literally "Nan or
Infinity" would be sufficient.
On another note, irrespective of these changes, is it good to show the
given input in the error messages? Error messages are logged and may leak
some details.I think the existing way seems ok.
In my opinion, it is quite common to include the error-causing value
in error messages. Also, we have already many functions that impliy
the possibility for revealing input values when converting text
representation into internal format, such as with int4in. However, I
don't stick to that way.
---
*NaN and Infinity restrictions:*
I am not sure why NaN and Infinity are not allowed in conversion to double
precision (.double() method). I have used the same restriction for
.decimal() and .number(). However, as you said, we should have error
messages more specific. I tried that in the attached patch; please have
your views. I have the following wordings for that error message:
"NaN or Infinity is not allowed for jsonpath item method .%s()"Suggestions...
They seem good to *me*.
By the way, while playing with this feature, I noticed the following
error message:
select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
The error message seems a bit off to me. For example, "argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
At Thu, 01 Feb 2024 10:49:57 +0900 (JST), Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote in
By the way, while playing with this feature, I noticed the following
error message:select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out of range for type booleanThe error message seems a bit off to me. For example, "argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)
Or, following our general convention, it would be spelled as:
'invalid argument for type Boolean: "1.1"'
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
On Thu, Feb 1, 2024 at 7:20 AM Kyotaro Horiguchi <horikyota.ntt@gmail.com>
wrote:
Thank you for the fix!
At Tue, 30 Jan 2024 13:46:17 +0530, Jeevan Chalke <
jeevan.chalke@enterprisedb.com> wrote inOn Mon, Jan 29, 2024 at 11:03 AM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Kyotaro Horiguchi <horikyota.ntt@gmail.com> writes:
Having said that, I'm a bit concerned about the case where an overly
long string is given there. However, considering that we already have
"invalid input syntax for type xxx: %x" messages (including for the
numeric), this concern might be unnecessary.Yeah, we have not worried about that in the past.
Another concern is that the input value is already a numeric, not a
string. This situation occurs when the input is NaN of +-Inf.Although
numeric_out could be used, it might cause another error. Therefore,
simply writing out as "argument NaN and Infinity.." would be better.Oh! I'd assumed that we were discussing a string that we'd failed to
convert to numeric. If the input is already numeric, then either
the error is unreachable or what we're really doing is rejecting
special values such as NaN on policy grounds. I would ask first
if that policy is sane at all. (I'd lean to "not" --- if we allow
it in the input JSON, why not in the output?) If it is sane, the
error message needs to be far more specific.regards, tom lane
*Consistent error message related to type:*
...
What if we use phrases like "for type double precision" at both places,
like:
numeric argument of jsonpath item method .%s() is out of range for type
double precision
string argument of jsonpath item method .%s() is not a validrepresentation
for type double precision
With this, the rest will be like:
for type numeric
for type bigint
for type integerSuggestions?
FWIW, I prefer consistently using "for type hoge".
OK.
---
*Showing input string in the error message:*
argument "...input string here..." of jsonpath item method .%s() is out
of
range for type numeric
If we add the input string in the error, then for some errors, it will be
too big, for example:-ERROR: numeric argument of jsonpath item method .double() is out of
range
for type double precision
+ERROR: argument
"100000<many zeros follow>"
of jsonpath item method .double() is out of range for type doubleprecision
As Tom suggested, given that similar situations have already been
disregarded elsewhere, worrying about excessively long input strings
in this specific instance won't notably improve safety in total.Also, for non-string input, we need to convert numeric to string just for
the error message, which seems overkill.As I suggested and you seem to agree, using literally "Nan or
Infinity" would be sufficient.
I am more concerned about .bigint() and .integer(). We can have errors when
the numeric input is out of range, but not NaN or Infinity. At those
places, we need to convert numeric to string to put that value into the
error.
Do you mean we should still put "Nan or Infinity" there?
This is the case:
select jsonb_path_query('12345678901', '$.integer()');
ERROR: numeric argument of jsonpath item method .integer() is out of
range for type integer
On another note, irrespective of these changes, is it good to show the
given input in the error messages? Error messages are logged and may leak
some details.I think the existing way seems ok.
In my opinion, it is quite common to include the error-causing value
in error messages. Also, we have already many functions that impliy
the possibility for revealing input values when converting text
representation into internal format, such as with int4in. However, I
don't stick to that way.---
*NaN and Infinity restrictions:*
I am not sure why NaN and Infinity are not allowed in conversion to
double
precision (.double() method). I have used the same restriction for
.decimal() and .number(). However, as you said, we should have error
messages more specific. I tried that in the attached patch; please have
your views. I have the following wordings for that error message:
"NaN or Infinity is not allowed for jsonpath item method .%s()"Suggestions...
They seem good to *me*.
Thanks
By the way, while playing with this feature, I noticed the following
error message:select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out ofrange for type boolean
The error message seems a bit off to me. For example, "argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
On Thu, Feb 1, 2024 at 7:24 AM Kyotaro Horiguchi <horikyota.ntt@gmail.com>
wrote:
At Thu, 01 Feb 2024 10:49:57 +0900 (JST), Kyotaro Horiguchi <
horikyota.ntt@gmail.com> wrote inBy the way, while playing with this feature, I noticed the following
error message:select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out ofrange for type boolean
The error message seems a bit off to me. For example, "argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)Or, following our general convention, it would be spelled as:
'invalid argument for type Boolean: "1.1"'
jsonpath way:
ERROR: argument of jsonpath item method .boolean() is invalid for type
boolean
or, if we add input value, then
ERROR: argument "1.1" of jsonpath item method .boolean() is invalid for
type boolean
And this should work for all the error types, like out of range, not valid,
invalid input, etc, etc. Also, we don't need separate error messages for
string input as well, which currently has the following form:
"string argument of jsonpath item method .%s() is not a valid
representation.."
Thanks
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
At Thu, 1 Feb 2024 09:19:40 +0530, Jeevan Chalke <jeevan.chalke@enterprisedb.com> wrote in
As Tom suggested, given that similar situations have already been
disregarded elsewhere, worrying about excessively long input strings
in this specific instance won't notably improve safety in total.Also, for non-string input, we need to convert numeric to string just for
the error message, which seems overkill.As I suggested and you seem to agree, using literally "Nan or
Infinity" would be sufficient.I am more concerned about .bigint() and .integer(). We can have errors when
the numeric input is out of range, but not NaN or Infinity. At those
places, we need to convert numeric to string to put that value into the
error.
Do you mean we should still put "Nan or Infinity" there?This is the case:
select jsonb_path_query('12345678901', '$.integer()');
ERROR: numeric argument of jsonpath item method .integer() is out of
range for type integer
Ah.. Understood. "NaN or Infinity" cannot be used in those
cases. Additionally, for jpiBoolean and jpiBigint, we lack the text
representation of the value.
By a quick grepping, I found that the following functions call
numeric_out to convert the jbvNumeric values back into text
representation.
JsonbValueAstext, populate_scalar, iterate_jsonb_values,
executeItemOptUnrwapTarget, jsonb_put_escaped_value
The function iterate_jsonb_values(), in particular, iterates over a
values array, calling numeric_out on each iteration.
The following functions re-converts the converted numeric into another type.
jsonb_int[248]() converts the numeric value into int2 using numeric_int[248]().
jsonb_float[48]() converts it into float4 using numeric_float[48]().
Given these facts, it seems more efficient for jbvNumber to retain the
original scalar value, converting it only when necessary. If needed,
we could also add a numeric struct member as a cache for better
performance. I'm not sure we refer the values more than once, though.
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
At Thu, 1 Feb 2024 09:22:22 +0530, Jeevan Chalke <jeevan.chalke@enterprisedb.com> wrote in
On Thu, Feb 1, 2024 at 7:24 AM Kyotaro Horiguchi <horikyota.ntt@gmail.com>
wrote:At Thu, 01 Feb 2024 10:49:57 +0900 (JST), Kyotaro Horiguchi <
horikyota.ntt@gmail.com> wrote inBy the way, while playing with this feature, I noticed the following
error message:select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is out ofrange for type boolean
The error message seems a bit off to me. For example, "argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)Or, following our general convention, it would be spelled as:
'invalid argument for type Boolean: "1.1"'
jsonpath way:
Hmm. I see.
ERROR: argument of jsonpath item method .boolean() is invalid for type
booleanor, if we add input value, then
ERROR: argument "1.1" of jsonpath item method .boolean() is invalid for
type booleanAnd this should work for all the error types, like out of range, not valid,
invalid input, etc, etc. Also, we don't need separate error messages for
string input as well, which currently has the following form:"string argument of jsonpath item method .%s() is not a valid
representation.."
Agreed.
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
Sorry for a minor correction, but..
At Thu, 01 Feb 2024 14:53:57 +0900 (JST), Kyotaro Horiguchi <horikyota.ntt@gmail.com> wrote in
Ah.. Understood. "NaN or Infinity" cannot be used in those
cases. Additionally, for jpiBoolean and jpiBigint, we lack the text
representation of the value.
This "Additionally" was merely left in by mistake.
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
On Thu, Feb 1, 2024 at 11:25 AM Kyotaro Horiguchi <horikyota.ntt@gmail.com>
wrote:
At Thu, 1 Feb 2024 09:22:22 +0530, Jeevan Chalke <
jeevan.chalke@enterprisedb.com> wrote inOn Thu, Feb 1, 2024 at 7:24 AM Kyotaro Horiguchi <
horikyota.ntt@gmail.com>
wrote:
At Thu, 01 Feb 2024 10:49:57 +0900 (JST), Kyotaro Horiguchi <
horikyota.ntt@gmail.com> wrote inBy the way, while playing with this feature, I noticed the following
error message:select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() is outof
range for type boolean
The error message seems a bit off to me. For example, "argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)Or, following our general convention, it would be spelled as:
'invalid argument for type Boolean: "1.1"'
jsonpath way:
Hmm. I see.
ERROR: argument of jsonpath item method .boolean() is invalid for type
booleanor, if we add input value, then
ERROR: argument "1.1" of jsonpath item method .boolean() is invalid for
type booleanAnd this should work for all the error types, like out of range, not
valid,
invalid input, etc, etc. Also, we don't need separate error messages for
string input as well, which currently has the following form:"string argument of jsonpath item method .%s() is not a valid
representation.."Agreed.
Attached are patches based on the discussion.
regards.
--
Kyotaro Horiguchi
NTT Open Source Software Center
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com
Attachments:
v1-0002-Improve-error-message-for-NaN-or-Infinity-inputs-.patchapplication/octet-stream; name=v1-0002-Improve-error-message-for-NaN-or-Infinity-inputs-.patchDownload
From fef57b0b78d3e2c1d815fdfa66593d7b39964478 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Fri, 2 Feb 2024 08:30:05 +0530
Subject: [PATCH v1 2/4] Improve error message for NaN or Infinity inputs for
jsonpath item.
Jeevan Chalke, per suggestion from Tom Lane.
---
src/backend/utils/adt/jsonpath_exec.c | 18 ++++++++++++++----
src/test/regress/expected/jsonb_jsonpath.out | 24 ++++++++++++------------
2 files changed, 26 insertions(+), 16 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 7f11d52..0d1d1b1 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1049,11 +1049,16 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
tmp,
(Node *) &escontext);
- if (escontext.error_occurred || isinf(val) || isnan(val))
+ if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
jspOperationName(jsp->type)))));
+ if (isinf(val) || isnan(val))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
+ jspOperationName(jsp->type)))));
res = jperOk;
}
else if (jb->type == jbvString)
@@ -1070,11 +1075,16 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
tmp,
(Node *) &escontext);
- if (escontext.error_occurred || isinf(val) || isnan(val))
+ if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
jspOperationName(jsp->type)))));
+ if (isinf(val) || isnan(val))
+ RETURN_ERROR(ereport(ERROR,
+ (errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
+ jspOperationName(jsp->type)))));
jb = &jbv;
jb->type = jbvNumeric;
@@ -1287,7 +1297,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type decimal or number",
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
if (jsp->type == jpiDecimal)
@@ -1319,7 +1329,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (numeric_is_nan(num) || numeric_is_inf(num))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("NaN or Infinity is not allowed for jsonpath item method .%s()",
jspOperationName(jsp->type)))));
res = jperOk;
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index eea2af3..95408ad 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1500,13 +1500,13 @@ ERROR: string argument of jsonpath item method .double() is not a valid represe
select jsonb_path_query('1e1000', '$.double()');
ERROR: numeric argument of jsonpath item method .double() is out of range for type double precision
select jsonb_path_query('"nan"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"NaN"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"inf"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"-inf"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"inf"', '$.double()', silent => true);
jsonb_path_query
------------------
@@ -2152,13 +2152,13 @@ select jsonb_path_query('1e1000', '$.decimal()');
(1 row)
select jsonb_path_query('"nan"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"NaN"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"inf"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"-inf"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .decimal()
select jsonb_path_query('"inf"', '$.decimal()', silent => true);
jsonb_path_query
------------------
@@ -2448,13 +2448,13 @@ select jsonb_path_query('1e1000', '$.number()');
(1 row)
select jsonb_path_query('"nan"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"NaN"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"-inf"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: NaN or Infinity is not allowed for jsonpath item method .number()
select jsonb_path_query('"inf"', '$.number()', silent => true);
jsonb_path_query
------------------
--
1.8.3.1
v1-0001-Merge-error-messages-in-the-same-pattern-in-jsonp.patchapplication/octet-stream; name=v1-0001-Merge-error-messages-in-the-same-pattern-in-jsonp.patchDownload
From 8d45e03037bb0c371e1c04e63b6e42618f4d6cfe Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Fri, 2 Feb 2024 08:24:59 +0530
Subject: [PATCH v1 1/4] Merge error messages in the same pattern in
jsonpath_exec.c
Kyotaro Horiguchi, reviewed by Jeevan Chalke.
---
src/backend/utils/adt/jsonpath_exec.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 573b6ce..7f11d52 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -2368,8 +2368,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (jsp->type == jpiDatetime)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("datetime format is not recognized: \"%s\"",
- text_to_cstring(datetime)),
+ errmsg("%s format is not recognized: \"%s\"",
+ "datetime", text_to_cstring(datetime)),
errhint("Use a datetime template argument to specify the input data format."))));
else
RETURN_ERROR(ereport(ERROR,
@@ -2401,8 +2401,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("date format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "date", text_to_cstring(datetime)))));
break;
case TIMESTAMPOID:
value = DirectFunctionCall1(timestamp_date,
@@ -2427,8 +2427,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case DATEOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("time format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "time", text_to_cstring(datetime)))));
break;
case TIMEOID: /* Nothing to do for TIME */
break;
@@ -2476,8 +2476,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMESTAMPOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("time_tz format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "time_tz", text_to_cstring(datetime)))));
break;
case TIMEOID:
value = DirectFunctionCall1(time_timetz,
@@ -2525,8 +2525,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("timestamp format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "timestamp", text_to_cstring(datetime)))));
break;
case TIMESTAMPOID: /* Nothing to do for TIMESTAMP */
break;
@@ -2577,8 +2577,8 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
case TIMETZOID:
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("timestamp_tz format is not recognized: \"%s\"",
- text_to_cstring(datetime)))));
+ errmsg("%s format is not recognized: \"%s\"",
+ "timestamp_tz", text_to_cstring(datetime)))));
break;
case TIMESTAMPOID:
value = DirectFunctionCall1(timestamp_timestamptz,
--
1.8.3.1
v1-0003-Unify-error-messages-for-various-jsonpath-item-me.patchapplication/octet-stream; name=v1-0003-Unify-error-messages-for-various-jsonpath-item-me.patchDownload
From 8a1684d9dc9cb5322d0e11244062ea79b8b53291 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Fri, 2 Feb 2024 09:01:52 +0530
Subject: [PATCH v1 3/4] Unify error messages for various jsonpath item
methods.
For numeric and string inputs, the error message string was different.
Also, for some inputs, the error message slightly seemed odd, like,
in the .boolean() case, we were saying "out of range" when the input
is 1.23. So, try to unify all those messages like the following
construct:
argument of jsonpath item method <name> is invalid for type <typename>
This also fixes the cases where we were saying "decimal or number"
that now say "type numeric" instead.
Jeevan Chalke, per discussion with Kyotaro Horiguchi and Tom Lane.
Discussion: https://www.postgresql.org/message-id/flat/CAM2+6=XjTyqrrqHAOj80r0wVQxJSxc0iyib9bPC55uFO9VKatg@mail.gmail.com
---
src/backend/utils/adt/jsonpath_exec.c | 24 +++++-----
src/test/regress/expected/jsonb_jsonpath.out | 66 ++++++++++++++--------------
2 files changed, 45 insertions(+), 45 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 0d1d1b1..8bb1017 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1052,7 +1052,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type double precision",
+ errmsg("argument of jsonpath item method .%s() is invalid for type double precision",
jspOperationName(jsp->type)))));
if (isinf(val) || isnan(val))
RETURN_ERROR(ereport(ERROR,
@@ -1078,7 +1078,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a double precision number",
+ errmsg("argument of jsonpath item method .%s() is invalid for type double precision",
jspOperationName(jsp->type)))));
if (isinf(val) || isnan(val))
RETURN_ERROR(ereport(ERROR,
@@ -1166,7 +1166,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type bigint",
+ errmsg("argument of jsonpath item method .%s() is invalid for type bigint",
jspOperationName(jsp->type)))));
datum = Int64GetDatum(val);
@@ -1188,7 +1188,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a big integer",
+ errmsg("argument of jsonpath item method .%s() is invalid for type bigint",
jspOperationName(jsp->type)))));
res = jperOk;
}
@@ -1240,7 +1240,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type boolean",
+ errmsg("argument of jsonpath item method .%s() is invalid for type boolean",
jspOperationName(jsp->type)))));
ival = DatumGetInt32(datum);
@@ -1260,7 +1260,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!parse_bool(tmp, &bval))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a boolean",
+ errmsg("argument of jsonpath item method .%s() is invalid for type boolean",
jspOperationName(jsp->type)))));
res = jperOk;
@@ -1322,7 +1322,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("argument of jsonpath item method .%s() is invalid for type numeric",
jspOperationName(jsp->type)))));
num = DatumGetNumeric(datum);
@@ -1411,7 +1411,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of a decimal or number",
+ errmsg("argument of jsonpath item method .%s() is invalid for type numeric",
jspOperationName(jsp->type)))));
num = DatumGetNumeric(numdatum);
@@ -1444,7 +1444,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ errmsg("argument of jsonpath item method .%s() is invalid for type integer",
jspOperationName(jsp->type)))));
datum = Int32GetDatum(val);
@@ -1466,7 +1466,7 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("string argument of jsonpath item method .%s() is not a valid representation of an integer",
+ errmsg("argument of jsonpath item method .%s() is invalid for type integer",
jspOperationName(jsp->type)))));
res = jperOk;
}
@@ -2563,7 +2563,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ errmsg("argument of jsonpath item method .%s() is invalid for type integer",
jspOperationName(jsp->type)))));
value = TimestampGetDatum(result);
@@ -2615,7 +2615,7 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("numeric argument of jsonpath item method .%s() is out of range for type integer",
+ errmsg("argument of jsonpath item method .%s() is invalid for type integer",
jspOperationName(jsp->type)))));
value = TimestampTzGetDatum(result);
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 95408ad..5fc9fea 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1496,9 +1496,9 @@ select jsonb_path_query('"1.23"', '$.double()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.double()');
-ERROR: string argument of jsonpath item method .double() is not a valid representation of a double precision number
+ERROR: argument of jsonpath item method .double() is invalid for type double precision
select jsonb_path_query('1e1000', '$.double()');
-ERROR: numeric argument of jsonpath item method .double() is out of range for type double precision
+ERROR: argument of jsonpath item method .double() is invalid for type double precision
select jsonb_path_query('"nan"', '$.double()');
ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"NaN"', '$.double()');
@@ -1767,19 +1767,19 @@ select jsonb_path_query('{}', '$.bigint()', silent => true);
(0 rows)
select jsonb_path_query('"1.23"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"1.23aaa"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('1e1000', '$.bigint()');
-ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"nan"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"NaN"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"inf"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"-inf"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"inf"', '$.bigint()', silent => true);
jsonb_path_query
------------------
@@ -1827,9 +1827,9 @@ select jsonb_path_query('"1234567890123"', '$.bigint()');
(1 row)
select jsonb_path_query('12345678901234567890', '$.bigint()');
-ERROR: numeric argument of jsonpath item method .bigint() is out of range for type bigint
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"12345678901234567890"', '$.bigint()');
-ERROR: string argument of jsonpath item method .bigint() is not a valid representation of a big integer
+ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"+123"', '$.bigint()');
jsonb_path_query
------------------
@@ -1882,21 +1882,21 @@ select jsonb_path_query('{}', '$.boolean()', silent => true);
(0 rows)
select jsonb_path_query('1.23', '$.boolean()');
-ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"1.23"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"1.23aaa"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('1e1000', '$.boolean()');
-ERROR: numeric argument of jsonpath item method .boolean() is out of range for type boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"nan"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"NaN"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"inf"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"-inf"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"inf"', '$.boolean()', silent => true);
jsonb_path_query
------------------
@@ -1908,7 +1908,7 @@ select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
(0 rows)
select jsonb_path_query('"100"', '$.boolean()');
-ERROR: string argument of jsonpath item method .boolean() is not a valid representation of a boolean
+ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('true', '$.boolean()');
jsonb_path_query
------------------
@@ -2144,7 +2144,7 @@ select jsonb_path_query('"1.23"', '$.decimal()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.decimal()');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: argument of jsonpath item method .decimal() is invalid for type numeric
select jsonb_path_query('1e1000', '$.decimal()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2224,7 +2224,7 @@ select jsonb_path_query('12345.678', '$.decimal(6, 1)');
(1 row)
select jsonb_path_query('12345.678', '$.decimal(6, 2)');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: argument of jsonpath item method .decimal() is invalid for type numeric
select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
jsonb_path_query
------------------
@@ -2232,7 +2232,7 @@ select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
(1 row)
select jsonb_path_query('12345.678', '$.decimal(4, 6)');
-ERROR: string argument of jsonpath item method .decimal() is not a valid representation of a decimal or number
+ERROR: argument of jsonpath item method .decimal() is invalid for type numeric
select jsonb_path_query('12345.678', '$.decimal(0, 6)');
ERROR: NUMERIC precision 0 must be between 1 and 1000
select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
@@ -2318,19 +2318,19 @@ select jsonb_path_query('{}', '$.integer()', silent => true);
(0 rows)
select jsonb_path_query('"1.23"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"1.23aaa"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('1e1000', '$.integer()');
-ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"nan"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"NaN"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"inf"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"-inf"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"inf"', '$.integer()', silent => true);
jsonb_path_query
------------------
@@ -2366,9 +2366,9 @@ select jsonb_path_query('1.83', '$.integer()');
(1 row)
select jsonb_path_query('12345678901', '$.integer()');
-ERROR: numeric argument of jsonpath item method .integer() is out of range for type integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"12345678901"', '$.integer()');
-ERROR: string argument of jsonpath item method .integer() is not a valid representation of an integer
+ERROR: argument of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"+123"', '$.integer()');
jsonb_path_query
------------------
@@ -2440,7 +2440,7 @@ select jsonb_path_query('"1.23"', '$.number()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.number()');
-ERROR: string argument of jsonpath item method .number() is not a valid representation of a decimal or number
+ERROR: argument of jsonpath item method .number() is invalid for type numeric
select jsonb_path_query('1e1000', '$.number()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--
1.8.3.1
v1-0004-Show-input-value-in-the-error-message-of-various-.patchapplication/octet-stream; name=v1-0004-Show-input-value-in-the-error-message-of-various-.patchDownload
From ba7f670d145b22810a744dd3b5eac72f304070ea Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Fri, 2 Feb 2024 09:51:37 +0530
Subject: [PATCH v1 4/4] Show input value in the error message of various
jsonpath item methods.
Jeevan Chalke, per discussion with Kyotaro Horiguchi and Tom Lane.
Discussion: https://www.postgresql.org/message-id/flat/CAM2+6=XjTyqrrqHAOj80r0wVQxJSxc0iyib9bPC55uFO9VKatg@mail.gmail.com
---
src/backend/utils/adt/jsonpath_exec.c | 48 ++++++++++----------
src/test/regress/expected/jsonb_jsonpath.out | 66 ++++++++++++++--------------
2 files changed, 59 insertions(+), 55 deletions(-)
diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c
index 8bb1017..32fb02a 100644
--- a/src/backend/utils/adt/jsonpath_exec.c
+++ b/src/backend/utils/adt/jsonpath_exec.c
@@ -1052,8 +1052,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type double precision",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type double precision",
+ tmp, jspOperationName(jsp->type)))));
if (isinf(val) || isnan(val))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
@@ -1078,8 +1078,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type double precision",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type double precision",
+ tmp, jspOperationName(jsp->type)))));
if (isinf(val) || isnan(val))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
@@ -1166,7 +1166,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type bigint",
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type bigint",
+ DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric))),
jspOperationName(jsp->type)))));
datum = Int64GetDatum(val);
@@ -1188,8 +1190,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type bigint",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type bigint",
+ tmp, jspOperationName(jsp->type)))));
res = jperOk;
}
@@ -1240,8 +1242,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type boolean",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type boolean",
+ tmp, jspOperationName(jsp->type)))));
ival = DatumGetInt32(datum);
if (ival == 0)
@@ -1260,8 +1262,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!parse_bool(tmp, &bval))
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type boolean",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type boolean",
+ tmp, jspOperationName(jsp->type)))));
res = jperOk;
}
@@ -1322,8 +1324,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type numeric",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type numeric",
+ numstr, jspOperationName(jsp->type)))));
num = DatumGetNumeric(datum);
if (numeric_is_nan(num) || numeric_is_inf(num))
@@ -1411,8 +1413,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type numeric",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type numeric",
+ numstr, jspOperationName(jsp->type)))));
num = DatumGetNumeric(numdatum);
pfree(arrtypmod);
@@ -1444,7 +1446,9 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (have_error)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type integer",
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type integer",
+ DatumGetCString(DirectFunctionCall1(numeric_out,
+ NumericGetDatum(jb->val.numeric))),
jspOperationName(jsp->type)))));
datum = Int32GetDatum(val);
@@ -1466,8 +1470,8 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp,
if (!noerr || escontext.error_occurred)
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_NON_NUMERIC_SQL_JSON_ITEM),
- errmsg("argument of jsonpath item method .%s() is invalid for type integer",
- jspOperationName(jsp->type)))));
+ errmsg("argument \"%s\" of jsonpath item method .%s() is invalid for type integer",
+ tmp, jspOperationName(jsp->type)))));
res = jperOk;
}
@@ -2560,10 +2564,10 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
result = DatumGetTimestamp(value);
AdjustTimestampForTypmod(&result, time_precision,
(Node *) &escontext);
- if (escontext.error_occurred)
+ if (escontext.error_occurred) /* should not happen */
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("argument of jsonpath item method .%s() is invalid for type integer",
+ errmsg("time precision of jsonpath item method .%s() is invalid",
jspOperationName(jsp->type)))));
value = TimestampGetDatum(result);
@@ -2612,10 +2616,10 @@ executeDateTimeMethod(JsonPathExecContext *cxt, JsonPathItem *jsp,
result = DatumGetTimestampTz(value);
AdjustTimestampForTypmod(&result, time_precision,
(Node *) &escontext);
- if (escontext.error_occurred)
+ if (escontext.error_occurred) /* should not happen */
RETURN_ERROR(ereport(ERROR,
(errcode(ERRCODE_INVALID_ARGUMENT_FOR_SQL_JSON_DATETIME_FUNCTION),
- errmsg("argument of jsonpath item method .%s() is invalid for type integer",
+ errmsg("time precision of jsonpath item method .%s() is invalid",
jspOperationName(jsp->type)))));
value = TimestampTzGetDatum(result);
diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out
index 5fc9fea..4eac9c9 100644
--- a/src/test/regress/expected/jsonb_jsonpath.out
+++ b/src/test/regress/expected/jsonb_jsonpath.out
@@ -1496,9 +1496,9 @@ select jsonb_path_query('"1.23"', '$.double()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.double()');
-ERROR: argument of jsonpath item method .double() is invalid for type double precision
+ERROR: argument "1.23aaa" of jsonpath item method .double() is invalid for type double precision
select jsonb_path_query('1e1000', '$.double()');
-ERROR: argument of jsonpath item method .double() is invalid for type double precision
+ERROR: argument "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" of jsonpath item method .double() is invalid for type double precision
select jsonb_path_query('"nan"', '$.double()');
ERROR: NaN or Infinity is not allowed for jsonpath item method .double()
select jsonb_path_query('"NaN"', '$.double()');
@@ -1767,19 +1767,19 @@ select jsonb_path_query('{}', '$.bigint()', silent => true);
(0 rows)
select jsonb_path_query('"1.23"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "1.23" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"1.23aaa"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "1.23aaa" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('1e1000', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"nan"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "nan" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"NaN"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "NaN" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"inf"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "inf" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"-inf"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "-inf" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"inf"', '$.bigint()', silent => true);
jsonb_path_query
------------------
@@ -1827,9 +1827,9 @@ select jsonb_path_query('"1234567890123"', '$.bigint()');
(1 row)
select jsonb_path_query('12345678901234567890', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "12345678901234567890" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"12345678901234567890"', '$.bigint()');
-ERROR: argument of jsonpath item method .bigint() is invalid for type bigint
+ERROR: argument "12345678901234567890" of jsonpath item method .bigint() is invalid for type bigint
select jsonb_path_query('"+123"', '$.bigint()');
jsonb_path_query
------------------
@@ -1882,21 +1882,21 @@ select jsonb_path_query('{}', '$.boolean()', silent => true);
(0 rows)
select jsonb_path_query('1.23', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "1.23" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"1.23"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "1.23" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"1.23aaa"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "1.23aaa" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('1e1000', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"nan"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "nan" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"NaN"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "NaN" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"inf"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "inf" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"-inf"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "-inf" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('"inf"', '$.boolean()', silent => true);
jsonb_path_query
------------------
@@ -1908,7 +1908,7 @@ select jsonb_path_query('"-inf"', '$.boolean()', silent => true);
(0 rows)
select jsonb_path_query('"100"', '$.boolean()');
-ERROR: argument of jsonpath item method .boolean() is invalid for type boolean
+ERROR: argument "100" of jsonpath item method .boolean() is invalid for type boolean
select jsonb_path_query('true', '$.boolean()');
jsonb_path_query
------------------
@@ -2144,7 +2144,7 @@ select jsonb_path_query('"1.23"', '$.decimal()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.decimal()');
-ERROR: argument of jsonpath item method .decimal() is invalid for type numeric
+ERROR: argument "1.23aaa" of jsonpath item method .decimal() is invalid for type numeric
select jsonb_path_query('1e1000', '$.decimal()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -2224,7 +2224,7 @@ select jsonb_path_query('12345.678', '$.decimal(6, 1)');
(1 row)
select jsonb_path_query('12345.678', '$.decimal(6, 2)');
-ERROR: argument of jsonpath item method .decimal() is invalid for type numeric
+ERROR: argument "12345.678" of jsonpath item method .decimal() is invalid for type numeric
select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
jsonb_path_query
------------------
@@ -2232,7 +2232,7 @@ select jsonb_path_query('1234.5678', '$.decimal(6, 2)');
(1 row)
select jsonb_path_query('12345.678', '$.decimal(4, 6)');
-ERROR: argument of jsonpath item method .decimal() is invalid for type numeric
+ERROR: argument "12345.678" of jsonpath item method .decimal() is invalid for type numeric
select jsonb_path_query('12345.678', '$.decimal(0, 6)');
ERROR: NUMERIC precision 0 must be between 1 and 1000
select jsonb_path_query('12345.678', '$.decimal(1001, 6)');
@@ -2318,19 +2318,19 @@ select jsonb_path_query('{}', '$.integer()', silent => true);
(0 rows)
select jsonb_path_query('"1.23"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "1.23" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"1.23aaa"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "1.23aaa" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('1e1000', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"nan"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "nan" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"NaN"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "NaN" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"inf"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "inf" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"-inf"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "-inf" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"inf"', '$.integer()', silent => true);
jsonb_path_query
------------------
@@ -2366,9 +2366,9 @@ select jsonb_path_query('1.83', '$.integer()');
(1 row)
select jsonb_path_query('12345678901', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "12345678901" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"12345678901"', '$.integer()');
-ERROR: argument of jsonpath item method .integer() is invalid for type integer
+ERROR: argument "12345678901" of jsonpath item method .integer() is invalid for type integer
select jsonb_path_query('"+123"', '$.integer()');
jsonb_path_query
------------------
@@ -2440,7 +2440,7 @@ select jsonb_path_query('"1.23"', '$.number()');
(1 row)
select jsonb_path_query('"1.23aaa"', '$.number()');
-ERROR: argument of jsonpath item method .number() is invalid for type numeric
+ERROR: argument "1.23aaa" of jsonpath item method .number() is invalid for type numeric
select jsonb_path_query('1e1000', '$.number()');
jsonb_path_query
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--
1.8.3.1
On 2024-02-02 Fr 00:31, Jeevan Chalke wrote:
On Thu, Feb 1, 2024 at 11:25 AM Kyotaro Horiguchi
<horikyota.ntt@gmail.com> wrote:At Thu, 1 Feb 2024 09:22:22 +0530, Jeevan Chalke
<jeevan.chalke@enterprisedb.com> wrote inOn Thu, Feb 1, 2024 at 7:24 AM Kyotaro Horiguchi
<horikyota.ntt@gmail.com>
wrote:
At Thu, 01 Feb 2024 10:49:57 +0900 (JST), Kyotaro Horiguchi <
horikyota.ntt@gmail.com> wrote inBy the way, while playing with this feature, I noticed the
following
error message:
select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method.boolean() is out of
range for type boolean
The error message seems a bit off to me. For example,
"argument '1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on thespelling of
Boolean..)
Or, following our general convention, it would be spelled as:
'invalid argument for type Boolean: "1.1"'
jsonpath way:
Hmm. I see.
ERROR: argument of jsonpath item method .boolean() is invalid
for type
boolean
or, if we add input value, then
ERROR: argument "1.1" of jsonpath item method .boolean() is
invalid for
type boolean
And this should work for all the error types, like out of range,
not valid,
invalid input, etc, etc. Also, we don't need separate error
messages for
string input as well, which currently has the following form:
"string argument of jsonpath item method .%s() is not a valid
representation.."Agreed.
Attached are patches based on the discussion.
Thanks, I combined these and pushed the result.
cheers
andrew
--
Andrew Dunstan
EDB:https://www.enterprisedb.com
On Tue, Feb 27, 2024 at 12:40 PM Andrew Dunstan <andrew@dunslane.net> wrote:
On 2024-02-02 Fr 00:31, Jeevan Chalke wrote:
On Thu, Feb 1, 2024 at 11:25 AM Kyotaro Horiguchi <horikyota.ntt@gmail.com>
wrote:At Thu, 1 Feb 2024 09:22:22 +0530, Jeevan Chalke <
jeevan.chalke@enterprisedb.com> wrote inOn Thu, Feb 1, 2024 at 7:24 AM Kyotaro Horiguchi <
horikyota.ntt@gmail.com>
wrote:
At Thu, 01 Feb 2024 10:49:57 +0900 (JST), Kyotaro Horiguchi <
horikyota.ntt@gmail.com> wrote inBy the way, while playing with this feature, I noticed the following
error message:select jsonb_path_query('1.1' , '$.boolean()');
ERROR: numeric argument of jsonpath item method .boolean() isout of
range for type boolean
The error message seems a bit off to me. For example, "argument
'1.1'
is invalid for type [bB]oolean" seems more appropriate for this
specific issue. (I'm not ceratin about our policy on the spelling of
Boolean..)Or, following our general convention, it would be spelled as:
'invalid argument for type Boolean: "1.1"'
jsonpath way:
Hmm. I see.
ERROR: argument of jsonpath item method .boolean() is invalid for type
booleanor, if we add input value, then
ERROR: argument "1.1" of jsonpath item method .boolean() is invalid for
type booleanAnd this should work for all the error types, like out of range, not
valid,
invalid input, etc, etc. Also, we don't need separate error messages for
string input as well, which currently has the following form:"string argument of jsonpath item method .%s() is not a valid
representation.."Agreed.
Attached are patches based on the discussion.
Thanks, I combined these and pushed the result.
Thank you, Andrew.
cheers
andrew
--
Andrew Dunstan
EDB: https://www.enterprisedb.com
--
Jeevan Chalke
*Principal, ManagerProduct Development*
edbpostgres.com