From: Tom Lane Date: Thu, 12 Sep 2024 18:30:29 +0000 (-0400) Subject: Make jsonpath .string() be immutable for datetimes. X-Git-Tag: REL_18_BETA1~1937 X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=cb599b9ddfccd15e77f4c167c4e5bdf1ddc3af38;p=postgresql.git Make jsonpath .string() be immutable for datetimes. Discussion of commit ed055d249 revealed that we don't actually want jsonpath's .string() method to depend on DateStyle, nor TimeZone either, because the non-"_tz" jsonpath functions are supposed to be immutable. Potentially we could allow a TimeZone dependency in the "_tz" variants, but it seems better to just uniformly define this method as returning the same string that jsonb text output would do. That's easier to implement too, saving a couple dozen lines. Patch by me, per complaint from Peter Eisentraut. Back-patch to v17 where this feature came in (in 66ea94e8e). Also back-patch ed055d249 to provide test cases. Discussion: https://postgr.es/m/5e8879d0-a3c8-4be2-950f-d83aa2af953a@eisentraut.org --- diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index e468ee62ff4..d36e4979b67 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18017,16 +18017,15 @@ ERROR: jsonpath member accessor can only be applied to an object String value converted from a JSON boolean, number, string, or - datetime (the output format for datetimes is determined by - the parameter) + datetime jsonb_path_query_array('[1.23, "xyz", false]', '$[*].string()') ["1.23", "xyz", "false"] - jsonb_path_query('"2023-08-15"', '$.datetime().string()') - "2023-08-15" + jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()') + "2023-08-15T12:34:56" diff --git a/src/backend/utils/adt/jsonpath_exec.c b/src/backend/utils/adt/jsonpath_exec.c index e569c7efb83..1184cba983a 100644 --- a/src/backend/utils/adt/jsonpath_exec.c +++ b/src/backend/utils/adt/jsonpath_exec.c @@ -72,6 +72,7 @@ #include "utils/datetime.h" #include "utils/float.h" #include "utils/formatting.h" +#include "utils/json.h" #include "utils/jsonpath.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -1629,32 +1630,13 @@ executeItemOptUnwrapTarget(JsonPathExecContext *cxt, JsonPathItem *jsp, 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); - } + char buf[MAXDATELEN + 1]; + + JsonEncodeDateTime(buf, + jb->val.datetime.value, + jb->val.datetime.typid, + &jb->val.datetime.tz); + tmp = pstrdup(buf); } break; case jbvNull: diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index 70eeb655a2a..acdf7e436f9 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -2652,30 +2652,30 @@ select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()') ERROR: cannot convert value from timestamptz to timestamp without time zone usage HINT: Use *_tz() function for time zone support. select jsonb_path_query_tz('"2023-08-15 12:34:56 +5:30"', '$.timestamp().string()'); -- should work - jsonb_path_query_tz ----------------------------- - "Tue Aug 15 00:04:56 2023" + jsonb_path_query_tz +----------------------- + "2023-08-15T00:04:56" (1 row) select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp_tz().string()'); ERROR: cannot convert value from timestamp to timestamptz without time zone usage HINT: Use *_tz() function for time zone support. select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp_tz().string()'); -- should work - jsonb_path_query_tz --------------------------------- - "Tue Aug 15 12:34:56 2023 PDT" + jsonb_path_query_tz +----------------------------- + "2023-08-15T12:34:56-07:00" (1 row) select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp_tz().string()'); - jsonb_path_query --------------------------------- - "Tue Aug 15 00:04:56 2023 PDT" + jsonb_path_query +----------------------------- + "2023-08-15T12:34:56+05:30" (1 row) select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()'); - jsonb_path_query ----------------------------- - "Tue Aug 15 12:34:56 2023" + jsonb_path_query +----------------------- + "2023-08-15T12:34:56" (1 row) select jsonb_path_query('"12:34:56 +5:30"', '$.time_tz().string()'); @@ -2687,7 +2687,7 @@ select jsonb_path_query('"12:34:56 +5:30"', '$.time_tz().string()'); select jsonb_path_query_tz('"12:34:56"', '$.time_tz().string()'); jsonb_path_query_tz --------------------- - "12:34:56-07" + "12:34:56-07:00" (1 row) select jsonb_path_query('"12:34:56"', '$.time().string()'); @@ -2699,53 +2699,26 @@ select jsonb_path_query('"12:34:56"', '$.time().string()'); select jsonb_path_query('"2023-08-15"', '$.date().string()'); jsonb_path_query ------------------ - "08-15-2023" -(1 row) - -set datestyle = 'ISO'; -select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp_tz().string()'); - jsonb_path_query_tz --------------------------- - "2023-08-15 12:34:56-07" + "2023-08-15" (1 row) +-- .string() does not react to timezone or datestyle +begin; +set local timezone = 'UTC'; +set local datestyle = 'German'; select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp_tz().string()'); - jsonb_path_query --------------------------- - "2023-08-15 00:04:56-07" + jsonb_path_query +----------------------------- + "2023-08-15T12:34:56+05:30" (1 row) select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()'); jsonb_path_query ----------------------- - "2023-08-15 12:34:56" -(1 row) - -select jsonb_path_query('"12:34:56 +5:30"', '$.time_tz().string()'); - jsonb_path_query ------------------- - "12:34:56+05:30" -(1 row) - -select jsonb_path_query_tz('"12:34:56"', '$.time_tz().string()'); - jsonb_path_query_tz ---------------------- - "12:34:56-07" -(1 row) - -select jsonb_path_query('"12:34:56"', '$.time().string()'); - jsonb_path_query ------------------- - "12:34:56" -(1 row) - -select jsonb_path_query('"2023-08-15"', '$.date().string()'); - jsonb_path_query ------------------- - "2023-08-15" + "2023-08-15T12:34:56" (1 row) -reset datestyle; +rollback; -- Test .time() select jsonb_path_query('null', '$.time()'); ERROR: jsonpath item method .time() can only be applied to a string diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index 4d57e13eda0..da3f7969ca2 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -611,15 +611,13 @@ select jsonb_path_query_tz('"12:34:56"', '$.time_tz().string()'); select jsonb_path_query('"12:34:56"', '$.time().string()'); select jsonb_path_query('"2023-08-15"', '$.date().string()'); -set datestyle = 'ISO'; -select jsonb_path_query_tz('"2023-08-15 12:34:56"', '$.timestamp_tz().string()'); +-- .string() does not react to timezone or datestyle +begin; +set local timezone = 'UTC'; +set local datestyle = 'German'; select jsonb_path_query('"2023-08-15 12:34:56 +5:30"', '$.timestamp_tz().string()'); select jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()'); -select jsonb_path_query('"12:34:56 +5:30"', '$.time_tz().string()'); -select jsonb_path_query_tz('"12:34:56"', '$.time_tz().string()'); -select jsonb_path_query('"12:34:56"', '$.time().string()'); -select jsonb_path_query('"2023-08-15"', '$.date().string()'); -reset datestyle; +rollback; -- Test .time() select jsonb_path_query('null', '$.time()');