Make jsonpath .string() be immutable for datetimes.
authorTom Lane <[email protected]>
Thu, 12 Sep 2024 18:30:29 +0000 (14:30 -0400)
committerTom Lane <[email protected]>
Thu, 12 Sep 2024 18:30:29 +0000 (14:30 -0400)
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

doc/src/sgml/func.sgml
src/backend/utils/adt/jsonpath_exec.c
src/test/regress/expected/jsonb_jsonpath.out
src/test/regress/sql/jsonb_jsonpath.sql

index e468ee62ff44bfddd363acb7f78b65206cde388d..d36e4979b677c7e2ecbebd803ffe65d0dbe01f7d 100644 (file)
@@ -18017,16 +18017,15 @@ ERROR:  jsonpath member accessor can only be applied to an object
        </para>
        <para>
         String value converted from a JSON boolean, number, string, or
-        datetime (the output format for datetimes is determined by
-        the <xref linkend="guc-datestyle"/> parameter)
+        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>
+        <literal>jsonb_path_query('"2023-08-15 12:34:56"', '$.timestamp().string()')</literal>
+        <returnvalue>"2023-08-15T12:34:56"</returnvalue>
        </para></entry>
       </row>
 
index e569c7efb83cdf8ca7212c4af0070d35cd7bde9d..1184cba983af06ef6e96084de77fc2604b8b5da1 100644 (file)
@@ -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:
index 70eeb655a2a74bf1241e0094310bfcad85368985..acdf7e436f9172fbcc4a4688cbd652de5636e2dc 100644 (file)
@@ -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
index 4d57e13eda0c6de30de89a40abf985a9b7602f6c..da3f7969ca2900b080961e844ae3ffc0dbdc3d31 100644 (file)
@@ -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()');