Fix incorrect translation of minus-infinity datetimes for json/jsonb.
authorTom Lane
Tue, 20 Oct 2015 18:06:24 +0000 (11:06 -0700)
committerTom Lane
Tue, 20 Oct 2015 18:06:24 +0000 (11:06 -0700)
Commit bda76c1c8cfb1d11751ba6be88f0242850481733 caused both plus and
minus infinity to be rendered as "infinity", which is not only wrong
but inconsistent with the pre-9.4 behavior of to_json().  Fix that by
duplicating the coding in date_out/timestamp_out/timestamptz_out more
closely.  Per bug #13687 from Stepan Perlov.  Back-patch to 9.4, like
the previous commit.

In passing, also re-pgindent json.c, since it had gotten a bit messed up by
recent patches (and I was already annoyed by indentation-related problems
in back-patching this fix ...)

src/backend/utils/adt/date.c
src/backend/utils/adt/json.c
src/backend/utils/adt/timestamp.c
src/include/utils/date.h
src/include/utils/datetime.h
src/test/regress/expected/json.out
src/test/regress/sql/json.sql

index bb23b12c1712ed69c1ab7dfc8226e0dbe312688d..02f0afba4c1820f3d0728fe6664373feaaaa8660 100644 (file)
@@ -40,7 +40,6 @@
 #endif
 
 
-static void EncodeSpecialDate(DateADT dt, char *str);
 static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
 static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
 static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
@@ -273,7 +272,7 @@ make_date(PG_FUNCTION_ARGS)
 /*
  * Convert reserved date values to string.
  */
-static void
+void
 EncodeSpecialDate(DateADT dt, char *str)
 {
    if (DATE_IS_NOBEGIN(dt))
index 679315b65871a5f20d082f4674e802c5aa315282..c3df54c018c00a8bed4eaa54e3b122f94b5d82a1 100644 (file)
@@ -33,9 +33,6 @@
 #include "utils/typcache.h"
 #include "utils/syscache.h"
 
-/* String to output for infinite dates and timestamps */
-#define DT_INFINITY "\"infinity\""
-
 /*
  * The context of the parser is maintained by the recursive descent
  * mechanism, but is passed explicitly to the error reporting routine
@@ -1435,19 +1432,16 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                char        buf[MAXDATELEN + 1];
 
                date = DatumGetDateADT(val);
-
+               /* Same as date_out(), but forcing DateStyle */
                if (DATE_NOT_FINITE(date))
-               {
-                   /* we have to format infinity ourselves */
-                   appendStringInfoString(result,DT_INFINITY);
-               }
+                   EncodeSpecialDate(date, buf);
                else
                {
                    j2date(date + POSTGRES_EPOCH_JDATE,
                           &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
                    EncodeDateOnly(&tm, USE_XSD_DATES, buf);
-                   appendStringInfo(result, "\"%s\"", buf);
                }
+               appendStringInfo(result, "\"%s\"", buf);
            }
            break;
        case JSONTYPE_TIMESTAMP:
@@ -1458,21 +1452,16 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                char        buf[MAXDATELEN + 1];
 
                timestamp = DatumGetTimestamp(val);
-
+               /* Same as timestamp_out(), but forcing DateStyle */
                if (TIMESTAMP_NOT_FINITE(timestamp))
-               {
-                   /* we have to format infinity ourselves */
-                   appendStringInfoString(result,DT_INFINITY);
-               }
+                   EncodeSpecialTimestamp(timestamp, buf);
                else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
-               {
                    EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
-                   appendStringInfo(result, "\"%s\"", buf);
-               }
                else
                    ereport(ERROR,
                            (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                             errmsg("timestamp out of range")));
+               appendStringInfo(result, "\"%s\"", buf);
            }
            break;
        case JSONTYPE_TIMESTAMPTZ:
@@ -1484,22 +1473,17 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
                const char *tzn = NULL;
                char        buf[MAXDATELEN + 1];
 
-               timestamp = DatumGetTimestamp(val);
-
+               timestamp = DatumGetTimestampTz(val);
+               /* Same as timestamptz_out(), but forcing DateStyle */
                if (TIMESTAMP_NOT_FINITE(timestamp))
-               {
-                   /* we have to format infinity ourselves */
-                   appendStringInfoString(result,DT_INFINITY);
-               }
+                   EncodeSpecialTimestamp(timestamp, buf);
                else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
-               {
                    EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
-                   appendStringInfo(result, "\"%s\"", buf);
-               }
                else
                    ereport(ERROR,
                            (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
                             errmsg("timestamp out of range")));
+               appendStringInfo(result, "\"%s\"", buf);
            }
            break;
        case JSONTYPE_JSON:
index 59108273cdee0b37f65c6d13ef9f9b28a7051fdd..37c99559b8b25d0ed2e0929738baf32252927790 100644 (file)
@@ -75,7 +75,6 @@ typedef struct
 
 
 static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
-static void EncodeSpecialTimestamp(Timestamp dt, char *str);
 static Timestamp dt2local(Timestamp dt, int timezone);
 static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
 static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
@@ -1507,7 +1506,7 @@ make_interval(PG_FUNCTION_ARGS)
 /* EncodeSpecialTimestamp()
  * Convert reserved timestamp data type to string.
  */
-static void
+void
 EncodeSpecialTimestamp(Timestamp dt, char *str)
 {
    if (TIMESTAMP_IS_NOBEGIN(dt))
index 622aa194901c2397da4c491b5380f28b2368a038..03513e07d7d914a240ca2e134e94f8c5ed3f9275 100644 (file)
@@ -92,6 +92,7 @@ typedef struct
 
 /* date.c */
 extern double date2timestamp_no_overflow(DateADT dateVal);
+extern void EncodeSpecialDate(DateADT dt, char *str);
 
 extern Datum date_in(PG_FUNCTION_ARGS);
 extern Datum date_out(PG_FUNCTION_ARGS);
index 9b53ee38ccf93b13586a18eafddfac67d5bc4f3a..a091f6fa04b162e15a61956da2077a1e4679d5df 100644 (file)
@@ -319,6 +319,7 @@ extern void EncodeDateOnly(struct pg_tm * tm, int style, char *str);
 extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str);
 extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str);
 extern void EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str);
+extern void EncodeSpecialTimestamp(Timestamp dt, char *str);
 
 extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
             struct pg_tm * tm);
index 7aa114ba1f0260ed85794e9e788f870c4627bfd7..65c43c9b65c8e5bc5164e8ac3596b7f8fbb22daf 100644 (file)
@@ -418,18 +418,36 @@ select to_json(date 'Infinity');
  "infinity"
 (1 row)
 
+select to_json(date '-Infinity');
+   to_json   
+-------------
+ "-infinity"
+(1 row)
+
 select to_json(timestamp 'Infinity');
   to_json   
 ------------
  "infinity"
 (1 row)
 
+select to_json(timestamp '-Infinity');
+   to_json   
+-------------
+ "-infinity"
+(1 row)
+
 select to_json(timestamptz 'Infinity');
   to_json   
 ------------
  "infinity"
 (1 row)
 
+select to_json(timestamptz '-Infinity');
+   to_json   
+-------------
+ "-infinity"
+(1 row)
+
 --json_agg
 SELECT json_agg(q)
   FROM ( SELECT $$a$$ || x AS b, y AS c,
index 26da2b37401f84410e0b88f04745a66eafc71533..bf540c06a5fedb421ec06489594fdde2d25f265c 100644 (file)
@@ -116,8 +116,11 @@ COMMIT;
 select to_json(date '2014-05-28');
 
 select to_json(date 'Infinity');
+select to_json(date '-Infinity');
 select to_json(timestamp 'Infinity');
+select to_json(timestamp '-Infinity');
 select to_json(timestamptz 'Infinity');
+select to_json(timestamptz '-Infinity');
 
 --json_agg