Skip to content

Commit d435542

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 984ae04 commit d435542

File tree

10 files changed

+90
-86
lines changed

10 files changed

+90
-86
lines changed

src/backend/utils/adt/date.c

Lines changed: 1 addition & 2 deletions
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

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@
3232
#include "utils/typcache.h"
3333
#include "utils/syscache.h"
3434

35-
/* String to output for infinite dates and timestamps */
36-
#define DT_INFINITY "\"infinity\""
37-
3835
/*
3936
* The context of the parser is maintained by the recursive descent
4037
* mechanism, but is passed explicitly to the error reporting routine
@@ -70,11 +67,11 @@ typedef enum /* type categories for datum_to_json */
7067

7168
typedef struct JsonAggState
7269
{
73-
StringInfo str;
74-
JsonTypeCategory key_category;
75-
Oid key_output_func;
76-
JsonTypeCategory val_category;
77-
Oid val_output_func;
70+
StringInfo str;
71+
JsonTypeCategory key_category;
72+
Oid key_output_func;
73+
JsonTypeCategory val_category;
74+
Oid val_output_func;
7875
} JsonAggState;
7976

8077
static inline void json_lex(JsonLexContext *lex);
@@ -360,16 +357,16 @@ pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
360357
int
361358
json_count_array_elements(JsonLexContext *lex)
362359
{
363-
JsonLexContext copylex;
364-
int count;
360+
JsonLexContext copylex;
361+
int count;
365362

366363
/*
367364
* It's safe to do this with a shallow copy because the lexical routines
368-
* don't scribble on the input. They do scribble on the other pointers etc,
369-
* so doing this with a copy makes that safe.
365+
* don't scribble on the input. They do scribble on the other pointers
366+
* etc, so doing this with a copy makes that safe.
370367
*/
371368
memcpy(&copylex, lex, sizeof(JsonLexContext));
372-
copylex.strval = NULL; /* not interested in values here */
369+
copylex.strval = NULL; /* not interested in values here */
373370
copylex.lex_level++;
374371

375372
count = 0;
@@ -1492,19 +1489,16 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
14921489
char buf[MAXDATELEN + 1];
14931490

14941491
date = DatumGetDateADT(val);
1495-
1492+
/* Same as date_out(), but forcing DateStyle */
14961493
if (DATE_NOT_FINITE(date))
1497-
{
1498-
/* we have to format infinity ourselves */
1499-
appendStringInfoString(result, DT_INFINITY);
1500-
}
1494+
EncodeSpecialDate(date, buf);
15011495
else
15021496
{
15031497
j2date(date + POSTGRES_EPOCH_JDATE,
15041498
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
15051499
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
1506-
appendStringInfo(result, "\"%s\"", buf);
15071500
}
1501+
appendStringInfo(result, "\"%s\"", buf);
15081502
}
15091503
break;
15101504
case JSONTYPE_TIMESTAMP:
@@ -1515,21 +1509,16 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15151509
char buf[MAXDATELEN + 1];
15161510

15171511
timestamp = DatumGetTimestamp(val);
1518-
1512+
/* Same as timestamp_out(), but forcing DateStyle */
15191513
if (TIMESTAMP_NOT_FINITE(timestamp))
1520-
{
1521-
/* we have to format infinity ourselves */
1522-
appendStringInfoString(result, DT_INFINITY);
1523-
}
1514+
EncodeSpecialTimestamp(timestamp, buf);
15241515
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
1525-
{
15261516
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
1527-
appendStringInfo(result, "\"%s\"", buf);
1528-
}
15291517
else
15301518
ereport(ERROR,
15311519
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
15321520
errmsg("timestamp out of range")));
1521+
appendStringInfo(result, "\"%s\"", buf);
15331522
}
15341523
break;
15351524
case JSONTYPE_TIMESTAMPTZ:
@@ -1541,22 +1530,17 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15411530
const char *tzn = NULL;
15421531
char buf[MAXDATELEN + 1];
15431532

1544-
timestamp = DatumGetTimestamp(val);
1545-
1533+
timestamp = DatumGetTimestampTz(val);
1534+
/* Same as timestamptz_out(), but forcing DateStyle */
15461535
if (TIMESTAMP_NOT_FINITE(timestamp))
1547-
{
1548-
/* we have to format infinity ourselves */
1549-
appendStringInfoString(result, DT_INFINITY);
1550-
}
1536+
EncodeSpecialTimestamp(timestamp, buf);
15511537
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
1552-
{
15531538
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
1554-
appendStringInfo(result, "\"%s\"", buf);
1555-
}
15561539
else
15571540
ereport(ERROR,
15581541
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
15591542
errmsg("timestamp out of range")));
1543+
appendStringInfo(result, "\"%s\"", buf);
15601544
}
15611545
break;
15621546
case JSONTYPE_JSON:
@@ -1875,7 +1859,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
18751859
{
18761860
MemoryContext aggcontext,
18771861
oldcontext;
1878-
JsonAggState *state;
1862+
JsonAggState *state;
18791863
Datum val;
18801864

18811865
if (!AggCheckCallContext(fcinfo, &aggcontext))
@@ -1886,7 +1870,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
18861870

18871871
if (PG_ARGISNULL(0))
18881872
{
1889-
Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1873+
Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
18901874

18911875
if (arg_type == InvalidOid)
18921876
ereport(ERROR,
@@ -1905,7 +1889,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
19051889
MemoryContextSwitchTo(oldcontext);
19061890

19071891
appendStringInfoChar(state->str, '[');
1908-
json_categorize_type(arg_type,&state->val_category,
1892+
json_categorize_type(arg_type, &state->val_category,
19091893
&state->val_output_func);
19101894
}
19111895
else
@@ -1949,7 +1933,7 @@ json_agg_transfn(PG_FUNCTION_ARGS)
19491933
Datum
19501934
json_agg_finalfn(PG_FUNCTION_ARGS)
19511935
{
1952-
JsonAggState *state;
1936+
JsonAggState *state;
19531937

19541938
/* cannot be called directly because of internal-type argument */
19551939
Assert(AggCheckCallContext(fcinfo, NULL));
@@ -1976,7 +1960,7 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
19761960
{
19771961
MemoryContext aggcontext,
19781962
oldcontext;
1979-
JsonAggState *state;
1963+
JsonAggState *state;
19801964
Datum arg;
19811965

19821966
if (!AggCheckCallContext(fcinfo, &aggcontext))
@@ -2007,7 +1991,7 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
20071991
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20081992
errmsg("could not determine data type for argument 1")));
20091993

2010-
json_categorize_type(arg_type,&state->key_category,
1994+
json_categorize_type(arg_type, &state->key_category,
20111995
&state->key_output_func);
20121996

20131997
arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
@@ -2017,7 +2001,7 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
20172001
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20182002
errmsg("could not determine data type for argument 2")));
20192003

2020-
json_categorize_type(arg_type,&state->val_category,
2004+
json_categorize_type(arg_type, &state->val_category,
20212005
&state->val_output_func);
20222006

20232007
appendStringInfoString(state->str, "{ ");
@@ -2065,7 +2049,7 @@ json_object_agg_transfn(PG_FUNCTION_ARGS)
20652049
Datum
20662050
json_object_agg_finalfn(PG_FUNCTION_ARGS)
20672051
{
2068-
JsonAggState *state;
2052+
JsonAggState *state;
20692053

20702054
/* cannot be called directly because of internal-type argument */
20712055
Assert(AggCheckCallContext(fcinfo, NULL));

src/backend/utils/adt/jsonb.c

Lines changed: 16 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -28,14 +28,6 @@
2828
#include "utils/syscache.h"
2929
#include "utils/typcache.h"
3030

31-
/*
32-
* String to output for infinite dates and timestamps.
33-
* Note the we don't use embedded quotes, unlike for json, because
34-
* we store jsonb strings dequoted.
35-
*/
36-
37-
#define DT_INFINITY "infinity"
38-
3931
typedef struct JsonbInState
4032
{
4133
JsonbParseState *parseState;
@@ -798,21 +790,18 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
798790
char buf[MAXDATELEN + 1];
799791

800792
date = DatumGetDateADT(val);
801-
jb.type = jbvString;
802-
793+
/* Same as date_out(), but forcing DateStyle */
803794
if (DATE_NOT_FINITE(date))
804-
{
805-
jb.val.string.len = strlen(DT_INFINITY);
806-
jb.val.string.val = pstrdup(DT_INFINITY);
807-
}
795+
EncodeSpecialDate(date, buf);
808796
else
809797
{
810798
j2date(date + POSTGRES_EPOCH_JDATE,
811799
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
812800
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
813-
jb.val.string.len = strlen(buf);
814-
jb.val.string.val = pstrdup(buf);
815801
}
802+
jb.type = jbvString;
803+
jb.val.string.len = strlen(buf);
804+
jb.val.string.val = pstrdup(buf);
816805
}
817806
break;
818807
case JSONBTYPE_TIMESTAMP:
@@ -823,24 +812,18 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
823812
char buf[MAXDATELEN + 1];
824813

825814
timestamp = DatumGetTimestamp(val);
826-
jb.type = jbvString;
827-
815+
/* Same as timestamp_out(), but forcing DateStyle */
828816
if (TIMESTAMP_NOT_FINITE(timestamp))
829-
{
830-
jb.val.string.len = strlen(DT_INFINITY);
831-
jb.val.string.val = pstrdup(DT_INFINITY);
832-
}
817+
EncodeSpecialTimestamp(timestamp, buf);
833818
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
834-
{
835-
836819
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
837-
jb.val.string.len = strlen(buf);
838-
jb.val.string.val = pstrdup(buf);
839-
}
840820
else
841821
ereport(ERROR,
842822
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
843823
errmsg("timestamp out of range")));
824+
jb.type = jbvString;
825+
jb.val.string.len = strlen(buf);
826+
jb.val.string.val = pstrdup(buf);
844827
}
845828
break;
846829
case JSONBTYPE_TIMESTAMPTZ:
@@ -852,24 +835,19 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
852835
const char *tzn = NULL;
853836
char buf[MAXDATELEN + 1];
854837

855-
timestamp = DatumGetTimestamp(val);
856-
jb.type = jbvString;
857-
838+
timestamp = DatumGetTimestampTz(val);
839+
/* Same as timestamptz_out(), but forcing DateStyle */
858840
if (TIMESTAMP_NOT_FINITE(timestamp))
859-
{
860-
jb.val.string.len = strlen(DT_INFINITY);
861-
jb.val.string.val = pstrdup(DT_INFINITY);
862-
}
841+
EncodeSpecialTimestamp(timestamp, buf);
863842
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
864-
{
865843
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
866-
jb.val.string.len = strlen(buf);
867-
jb.val.string.val = pstrdup(buf);
868-
}
869844
else
870845
ereport(ERROR,
871846
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
872847
errmsg("timestamp out of range")));
848+
jb.type = jbvString;
849+
jb.val.string.len = strlen(buf);
850+
jb.val.string.val = pstrdup(buf);
873851
}
874852
break;
875853
case JSONBTYPE_JSONCAST:

src/backend/utils/adt/timestamp.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ typedef struct
6868

6969

7070
static TimeOffset time2t(const int hour, const int min, const int sec, const fsec_t fsec);
71-
static void EncodeSpecialTimestamp(Timestamp dt, char *str);
7271
static Timestamp dt2local(Timestamp dt, int timezone);
7372
static void AdjustTimestampForTypmod(Timestamp *time, int32 typmod);
7473
static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
@@ -1500,7 +1499,7 @@ make_interval(PG_FUNCTION_ARGS)
15001499
/* EncodeSpecialTimestamp()
15011500
* Convert reserved timestamp data type to string.
15021501
*/
1503-
static void
1502+
void
15041503
EncodeSpecialTimestamp(Timestamp dt, char *str)
15051504
{
15061505
if (TIMESTAMP_IS_NOBEGIN(dt))

src/include/utils/date.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ typedef struct
9090

9191
/* date.c */
9292
extern double date2timestamp_no_overflow(DateADT dateVal);
93+
extern void EncodeSpecialDate(DateADT dt, char *str);
9394

9495
extern Datum date_in(PG_FUNCTION_ARGS);
9596
extern Datum date_out(PG_FUNCTION_ARGS);

src/include/utils/datetime.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ extern void EncodeDateOnly(struct pg_tm * tm, int style, char *str);
326326
extern void EncodeTimeOnly(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, int style, char *str);
327327
extern void EncodeDateTime(struct pg_tm * tm, fsec_t fsec, bool print_tz, int tz, const char *tzn, int style, char *str);
328328
extern void EncodeInterval(struct pg_tm * tm, fsec_t fsec, int style, char *str);
329+
extern void EncodeSpecialTimestamp(Timestamp dt, char *str);
329330

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

src/test/regress/expected/json.out

Lines changed: 18 additions & 0 deletions
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,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy