Skip to content

Commit 4f33572

Browse files
committed
Fix incorrect translation of minus-infinity datetimes for json/jsonb.
Commit bda76c1 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 ...)
1 parent 7fc7125 commit 4f33572

File tree

7 files changed

+35
-30
lines changed

7 files changed

+35
-30
lines changed

src/backend/utils/adt/date.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
#endif
4141

4242

43-
static void EncodeSpecialDate(DateADT dt, char *str);
4443
static int time2tm(TimeADT time, struct pg_tm * tm, fsec_t *fsec);
4544
static int timetz2tm(TimeTzADT *time, struct pg_tm * tm, fsec_t *fsec, int *tzp);
4645
static int tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
@@ -273,7 +272,7 @@ make_date(PG_FUNCTION_ARGS)
273272
/*
274273
* Convert reserved date values to string.
275274
*/
276-
static void
275+
void
277276
EncodeSpecialDate(DateADT dt, char *str)
278277
{
279278
if (DATE_IS_NOBEGIN(dt))

src/backend/utils/adt/json.c

+10-26
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,6 @@
3333
#include "utils/typcache.h"
3434
#include "utils/syscache.h"
3535

36-
/* String to output for infinite dates and timestamps */
37-
#define DT_INFINITY "\"infinity\""
38-
3936
/*
4037
* The context of the parser is maintained by the recursive descent
4138
* mechanism, but is passed explicitly to the error reporting routine
@@ -1435,19 +1432,16 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
14351432
char buf[MAXDATELEN + 1];
14361433

14371434
date = DatumGetDateADT(val);
1438-
1435+
/* Same as date_out(), but forcing DateStyle */
14391436
if (DATE_NOT_FINITE(date))
1440-
{
1441-
/* we have to format infinity ourselves */
1442-
appendStringInfoString(result,DT_INFINITY);
1443-
}
1437+
EncodeSpecialDate(date, buf);
14441438
else
14451439
{
14461440
j2date(date + POSTGRES_EPOCH_JDATE,
14471441
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
14481442
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
1449-
appendStringInfo(result, "\"%s\"", buf);
14501443
}
1444+
appendStringInfo(result, "\"%s\"", buf);
14511445
}
14521446
break;
14531447
case JSONTYPE_TIMESTAMP:
@@ -1458,21 +1452,16 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
14581452
char buf[MAXDATELEN + 1];
14591453

14601454
timestamp = DatumGetTimestamp(val);
1461-
1455+
/* Same as timestamp_out(), but forcing DateStyle */
14621456
if (TIMESTAMP_NOT_FINITE(timestamp))
1463-
{
1464-
/* we have to format infinity ourselves */
1465-
appendStringInfoString(result,DT_INFINITY);
1466-
}
1457+
EncodeSpecialTimestamp(timestamp, buf);
14671458
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
1468-
{
14691459
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
1470-
appendStringInfo(result, "\"%s\"", buf);
1471-
}
14721460
else
14731461
ereport(ERROR,
14741462
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
14751463
errmsg("timestamp out of range")));
1464+
appendStringInfo(result, "\"%s\"", buf);
14761465
}
14771466
break;
14781467
case JSONTYPE_TIMESTAMPTZ:
@@ -1484,22 +1473,17 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
14841473
const char *tzn = NULL;
14851474
char buf[MAXDATELEN + 1];
14861475

1487-
timestamp = DatumGetTimestamp(val);
1488-
1476+
timestamp = DatumGetTimestampTz(val);
1477+
/* Same as timestamptz_out(), but forcing DateStyle */
14891478
if (TIMESTAMP_NOT_FINITE(timestamp))
1490-
{
1491-
/* we have to format infinity ourselves */
1492-
appendStringInfoString(result,DT_INFINITY);
1493-
}
1479+
EncodeSpecialTimestamp(timestamp, buf);
14941480
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
1495-
{
14961481
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
1497-
appendStringInfo(result, "\"%s\"", buf);
1498-
}
14991482
else
15001483
ereport(ERROR,
15011484
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
15021485
errmsg("timestamp out of range")));
1486+
appendStringInfo(result, "\"%s\"", buf);
15031487
}
15041488
break;
15051489
case JSONTYPE_JSON:

src/backend/utils/adt/timestamp.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ typedef struct
7575

7676

7777
static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
78-
static void EncodeSpecialTimestamp(Timestamp dt, char *str);
7978
static Timestamp dt2local(Timestamp dt, int timezone);
8079
static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
8180
static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
@@ -1507,7 +1506,7 @@ make_interval(PG_FUNCTION_ARGS)
15071506
/* EncodeSpecialTimestamp()
15081507
* Convert reserved timestamp data type to string.
15091508
*/
1510-
static void
1509+
void
15111510
EncodeSpecialTimestamp(Timestamp dt, char *str)
15121511
{
15131512
if (TIMESTAMP_IS_NOBEGIN(dt))

src/include/utils/date.h

+1
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ typedef struct
9292

9393
/* date.c */
9494
extern double date2timestamp_no_overflow(DateADT dateVal);
95+
extern void EncodeSpecialDate(DateADT dt, char *str);
9596

9697
extern Datum date_in(PG_FUNCTION_ARGS);
9798
extern Datum date_out(PG_FUNCTION_ARGS);

src/include/utils/datetime.h

+1
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ extern void EncodeDateOnly(struct pg_tm * tm, int style, char *str);
319319
extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str);
320320
extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str);
321321
extern void EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str);
322+
extern void EncodeSpecialTimestamp(Timestamp dt, char *str);
322323

323324
extern int ValidateDate(int fmask, bool isjulian, bool is2digits, bool bc,
324325
struct pg_tm * tm);

src/test/regress/expected/json.out

+18
Original file line numberDiff line numberDiff line change
@@ -418,18 +418,36 @@ select to_json(date 'Infinity');
418418
"infinity"
419419
(1 row)
420420

421+
select to_json(date '-Infinity');
422+
to_json
423+
-------------
424+
"-infinity"
425+
(1 row)
426+
421427
select to_json(timestamp 'Infinity');
422428
to_json
423429
------------
424430
"infinity"
425431
(1 row)
426432

433+
select to_json(timestamp '-Infinity');
434+
to_json
435+
-------------
436+
"-infinity"
437+
(1 row)
438+
427439
select to_json(timestamptz 'Infinity');
428440
to_json
429441
------------
430442
"infinity"
431443
(1 row)
432444

445+
select to_json(timestamptz '-Infinity');
446+
to_json
447+
-------------
448+
"-infinity"
449+
(1 row)
450+
433451
--json_agg
434452
SELECT json_agg(q)
435453
FROM ( SELECT $$a$$ || x AS b, y AS c,

src/test/regress/sql/json.sql

+3
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,11 @@ COMMIT;
116116
select to_json(date '2014-05-28');
117117

118118
select to_json(date 'Infinity');
119+
select to_json(date '-Infinity');
119120
select to_json(timestamp 'Infinity');
121+
select to_json(timestamp '-Infinity');
120122
select to_json(timestamptz 'Infinity');
123+
select to_json(timestamptz '-Infinity');
121124

122125
--json_agg
123126

0 commit comments

Comments
 (0)