mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Render infinite date/timestamps as 'infinity' for json/jsonb
Commit ab14a73a6c
raised an error in these cases and later the
behaviour was copied to jsonb. This is what the XML code, which we
then adopted, does, as the XSD types don't accept infinite values.
However, json dates and timestamps are just strings as far as json is
concerned, so there is no reason not to render these values as
'infinity'.
The json portion of this is backpatched to 9.4 where the behaviour was
introduced. The jsonb portion only affects the development branch.
Per gripe on pgsql-general.
This commit is contained in:
@ -32,6 +32,9 @@
|
|||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
#include "utils/syscache.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
|
* The context of the parser is maintained by the recursive descent
|
||||||
* mechanism, but is passed explicitly to the error reporting routine
|
* mechanism, but is passed explicitly to the error reporting routine
|
||||||
@ -1436,20 +1439,18 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
|
|||||||
|
|
||||||
date = DatumGetDateADT(val);
|
date = DatumGetDateADT(val);
|
||||||
|
|
||||||
/* XSD doesn't support infinite values */
|
|
||||||
if (DATE_NOT_FINITE(date))
|
if (DATE_NOT_FINITE(date))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
/* we have to format infinity ourselves */
|
||||||
errmsg("date out of range"),
|
appendStringInfoString(result,DT_INFINITY);
|
||||||
errdetail("JSON does not support infinite date values.")));
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
j2date(date + POSTGRES_EPOCH_JDATE,
|
j2date(date + POSTGRES_EPOCH_JDATE,
|
||||||
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
||||||
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
|
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
|
||||||
|
appendStringInfo(result, "\"%s\"", buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
appendStringInfo(result, "\"%s\"", buf);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONTYPE_TIMESTAMP:
|
case JSONTYPE_TIMESTAMP:
|
||||||
@ -1461,20 +1462,20 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
|
|||||||
|
|
||||||
timestamp = DatumGetTimestamp(val);
|
timestamp = DatumGetTimestamp(val);
|
||||||
|
|
||||||
/* XSD doesn't support infinite values */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
/* we have to format infinity ourselves */
|
||||||
errmsg("timestamp out of range"),
|
appendStringInfoString(result,DT_INFINITY);
|
||||||
errdetail("JSON does not support infinite timestamp values.")));
|
}
|
||||||
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
||||||
|
{
|
||||||
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
||||||
|
appendStringInfo(result, "\"%s\"", buf);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("timestamp out of range")));
|
errmsg("timestamp out of range")));
|
||||||
|
|
||||||
appendStringInfo(result, "\"%s\"", buf);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONTYPE_TIMESTAMPTZ:
|
case JSONTYPE_TIMESTAMPTZ:
|
||||||
@ -1488,20 +1489,20 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
|
|||||||
|
|
||||||
timestamp = DatumGetTimestamp(val);
|
timestamp = DatumGetTimestamp(val);
|
||||||
|
|
||||||
/* XSD doesn't support infinite values */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
/* we have to format infinity ourselves */
|
||||||
errmsg("timestamp out of range"),
|
appendStringInfoString(result,DT_INFINITY);
|
||||||
errdetail("JSON does not support infinite timestamp values.")));
|
}
|
||||||
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
||||||
|
{
|
||||||
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
||||||
|
appendStringInfo(result, "\"%s\"", buf);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("timestamp out of range")));
|
errmsg("timestamp out of range")));
|
||||||
|
|
||||||
appendStringInfo(result, "\"%s\"", buf);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONTYPE_JSON:
|
case JSONTYPE_JSON:
|
||||||
|
@ -28,6 +28,14 @@
|
|||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
#include "utils/typcache.h"
|
#include "utils/typcache.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* String to output for infinite dates and timestamps.
|
||||||
|
* Note the we don't use embedded quotes, unlike for json, because
|
||||||
|
* we store jsonb strings dequoted.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_INFINITY "infinity"
|
||||||
|
|
||||||
typedef struct JsonbInState
|
typedef struct JsonbInState
|
||||||
{
|
{
|
||||||
JsonbParseState *parseState;
|
JsonbParseState *parseState;
|
||||||
@ -714,23 +722,21 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
|
|||||||
char buf[MAXDATELEN + 1];
|
char buf[MAXDATELEN + 1];
|
||||||
|
|
||||||
date = DatumGetDateADT(val);
|
date = DatumGetDateADT(val);
|
||||||
|
jb.type = jbvString;
|
||||||
|
|
||||||
/* XSD doesn't support infinite values */
|
|
||||||
if (DATE_NOT_FINITE(date))
|
if (DATE_NOT_FINITE(date))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
jb.val.string.len = strlen(DT_INFINITY);
|
||||||
errmsg("date out of range"),
|
jb.val.string.val = pstrdup(DT_INFINITY);
|
||||||
errdetail("JSON does not support infinite date values.")));
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
j2date(date + POSTGRES_EPOCH_JDATE,
|
j2date(date + POSTGRES_EPOCH_JDATE,
|
||||||
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
||||||
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
|
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
|
||||||
|
jb.val.string.len = strlen(buf);
|
||||||
|
jb.val.string.val = pstrdup(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
jb.type = jbvString;
|
|
||||||
jb.val.string.len = strlen(buf);
|
|
||||||
jb.val.string.val = pstrdup(buf);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_TIMESTAMP:
|
case JSONBTYPE_TIMESTAMP:
|
||||||
@ -741,23 +747,24 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
|
|||||||
char buf[MAXDATELEN + 1];
|
char buf[MAXDATELEN + 1];
|
||||||
|
|
||||||
timestamp = DatumGetTimestamp(val);
|
timestamp = DatumGetTimestamp(val);
|
||||||
|
jb.type = jbvString;
|
||||||
|
|
||||||
/* XSD doesn't support infinite values */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
jb.val.string.len = strlen(DT_INFINITY);
|
||||||
errmsg("timestamp out of range"),
|
jb.val.string.val = pstrdup(DT_INFINITY);
|
||||||
errdetail("JSON does not support infinite timestamp values.")));
|
}
|
||||||
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
||||||
|
{
|
||||||
|
|
||||||
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
||||||
|
jb.val.string.len = strlen(buf);
|
||||||
|
jb.val.string.val = pstrdup(buf);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("timestamp out of range")));
|
errmsg("timestamp out of range")));
|
||||||
|
|
||||||
jb.type = jbvString;
|
|
||||||
jb.val.string.len = strlen(buf);
|
|
||||||
jb.val.string.val = pstrdup(buf);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_TIMESTAMPTZ:
|
case JSONBTYPE_TIMESTAMPTZ:
|
||||||
@ -770,23 +777,23 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
|
|||||||
char buf[MAXDATELEN + 1];
|
char buf[MAXDATELEN + 1];
|
||||||
|
|
||||||
timestamp = DatumGetTimestamp(val);
|
timestamp = DatumGetTimestamp(val);
|
||||||
|
jb.type = jbvString;
|
||||||
|
|
||||||
/* XSD doesn't support infinite values */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
ereport(ERROR,
|
{
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
jb.val.string.len = strlen(DT_INFINITY);
|
||||||
errmsg("timestamp out of range"),
|
jb.val.string.val = pstrdup(DT_INFINITY);
|
||||||
errdetail("JSON does not support infinite timestamp values.")));
|
}
|
||||||
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
||||||
|
{
|
||||||
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
||||||
|
jb.val.string.len = strlen(buf);
|
||||||
|
jb.val.string.val = pstrdup(buf);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
errmsg("timestamp out of range")));
|
errmsg("timestamp out of range")));
|
||||||
|
|
||||||
jb.type = jbvString;
|
|
||||||
jb.val.string.len = strlen(buf);
|
|
||||||
jb.val.string.val = pstrdup(buf);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_JSONCAST:
|
case JSONBTYPE_JSONCAST:
|
||||||
|
@ -426,6 +426,30 @@ select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
select to_json(date '2014-05-28');
|
||||||
|
to_json
|
||||||
|
--------------
|
||||||
|
"2014-05-28"
|
||||||
|
(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(timestamptz 'Infinity');
|
||||||
|
to_json
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
--json_agg
|
--json_agg
|
||||||
SELECT json_agg(q)
|
SELECT json_agg(q)
|
||||||
FROM ( SELECT $$a$$ || x AS b, y AS c,
|
FROM ( SELECT $$a$$ || x AS b, y AS c,
|
||||||
|
@ -426,6 +426,30 @@ select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
select to_json(date '2014-05-28');
|
||||||
|
to_json
|
||||||
|
--------------
|
||||||
|
"2014-05-28"
|
||||||
|
(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(timestamptz 'Infinity');
|
||||||
|
to_json
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
--json_agg
|
--json_agg
|
||||||
SELECT json_agg(q)
|
SELECT json_agg(q)
|
||||||
FROM ( SELECT $$a$$ || x AS b, y AS c,
|
FROM ( SELECT $$a$$ || x AS b, y AS c,
|
||||||
|
@ -330,6 +330,30 @@ select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
select to_jsonb(date '2014-05-28');
|
||||||
|
to_jsonb
|
||||||
|
--------------
|
||||||
|
"2014-05-28"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select to_jsonb(date 'Infinity');
|
||||||
|
to_jsonb
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select to_jsonb(timestamp 'Infinity');
|
||||||
|
to_jsonb
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select to_jsonb(timestamptz 'Infinity');
|
||||||
|
to_jsonb
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
--jsonb_agg
|
--jsonb_agg
|
||||||
CREATE TEMP TABLE rows AS
|
CREATE TEMP TABLE rows AS
|
||||||
SELECT x, 'txt' || x as y
|
SELECT x, 'txt' || x as y
|
||||||
|
@ -330,6 +330,30 @@ select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
|
|||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
select to_jsonb(date '2014-05-28');
|
||||||
|
to_jsonb
|
||||||
|
--------------
|
||||||
|
"2014-05-28"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select to_jsonb(date 'Infinity');
|
||||||
|
to_jsonb
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select to_jsonb(timestamp 'Infinity');
|
||||||
|
to_jsonb
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select to_jsonb(timestamptz 'Infinity');
|
||||||
|
to_jsonb
|
||||||
|
------------
|
||||||
|
"infinity"
|
||||||
|
(1 row)
|
||||||
|
|
||||||
--jsonb_agg
|
--jsonb_agg
|
||||||
CREATE TEMP TABLE rows AS
|
CREATE TEMP TABLE rows AS
|
||||||
SELECT x, 'txt' || x as y
|
SELECT x, 'txt' || x as y
|
||||||
|
@ -111,6 +111,12 @@ SET LOCAL TIME ZONE -8;
|
|||||||
select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
|
select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
select to_json(date '2014-05-28');
|
||||||
|
|
||||||
|
select to_json(date 'Infinity');
|
||||||
|
select to_json(timestamp 'Infinity');
|
||||||
|
select to_json(timestamptz 'Infinity');
|
||||||
|
|
||||||
--json_agg
|
--json_agg
|
||||||
|
|
||||||
SELECT json_agg(q)
|
SELECT json_agg(q)
|
||||||
|
@ -74,6 +74,12 @@ SET LOCAL TIME ZONE -8;
|
|||||||
select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
|
select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
select to_jsonb(date '2014-05-28');
|
||||||
|
|
||||||
|
select to_jsonb(date 'Infinity');
|
||||||
|
select to_jsonb(timestamp 'Infinity');
|
||||||
|
select to_jsonb(timestamptz 'Infinity');
|
||||||
|
|
||||||
--jsonb_agg
|
--jsonb_agg
|
||||||
|
|
||||||
CREATE TEMP TABLE rows AS
|
CREATE TEMP TABLE rows AS
|
||||||
|
Reference in New Issue
Block a user