summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Dunstan2015-02-26 17:25:21 +0000
committerAndrew Dunstan2015-02-26 17:25:21 +0000
commitbda76c1c8cfb1d11751ba6be88f0242850481733 (patch)
treede3272dade43151c7f9668e18cb8111f2da70bb6
parentfd6a3f3ad4067f1b8fc28e9de6e99e5936d82161 (diff)
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.
-rw-r--r--src/backend/utils/adt/json.c43
-rw-r--r--src/backend/utils/adt/jsonb.c61
-rw-r--r--src/test/regress/expected/json.out24
-rw-r--r--src/test/regress/expected/json_1.out24
-rw-r--r--src/test/regress/expected/jsonb.out24
-rw-r--r--src/test/regress/expected/jsonb_1.out24
-rw-r--r--src/test/regress/sql/json.sql6
-rw-r--r--src/test/regress/sql/jsonb.sql6
8 files changed, 164 insertions, 48 deletions
diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c
index 951b655400..d0d7206ae9 100644
--- a/src/backend/utils/adt/json.c
+++ b/src/backend/utils/adt/json.c
@@ -32,6 +32,9 @@
#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
@@ -1436,20 +1439,18 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
date = DatumGetDateADT(val);
- /* XSD doesn't support infinite values */
if (DATE_NOT_FINITE(date))
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("date out of range"),
- errdetail("JSON does not support infinite date values.")));
+ {
+ /* we have to format infinity ourselves */
+ appendStringInfoString(result,DT_INFINITY);
+ }
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:
@@ -1461,20 +1462,20 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
timestamp = DatumGetTimestamp(val);
- /* XSD doesn't support infinite values */
if (TIMESTAMP_NOT_FINITE(timestamp))
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range"),
- errdetail("JSON does not support infinite timestamp values.")));
+ {
+ /* we have to format infinity ourselves */
+ appendStringInfoString(result,DT_INFINITY);
+ }
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:
@@ -1488,20 +1489,20 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
timestamp = DatumGetTimestamp(val);
- /* XSD doesn't support infinite values */
if (TIMESTAMP_NOT_FINITE(timestamp))
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range"),
- errdetail("JSON does not support infinite timestamp values.")));
+ {
+ /* we have to format infinity ourselves */
+ appendStringInfoString(result,DT_INFINITY);
+ }
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:
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 644ea6d941..aac97565f9 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -28,6 +28,14 @@
#include "utils/syscache.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
{
JsonbParseState *parseState;
@@ -714,23 +722,21 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
char buf[MAXDATELEN + 1];
date = DatumGetDateADT(val);
+ jb.type = jbvString;
- /* XSD doesn't support infinite values */
if (DATE_NOT_FINITE(date))
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("date out of range"),
- errdetail("JSON does not support infinite date values.")));
+ {
+ jb.val.string.len = strlen(DT_INFINITY);
+ jb.val.string.val = pstrdup(DT_INFINITY);
+ }
else
{
j2date(date + POSTGRES_EPOCH_JDATE,
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
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;
case JSONBTYPE_TIMESTAMP:
@@ -741,23 +747,24 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
char buf[MAXDATELEN + 1];
timestamp = DatumGetTimestamp(val);
+ jb.type = jbvString;
- /* XSD doesn't support infinite values */
if (TIMESTAMP_NOT_FINITE(timestamp))
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range"),
- errdetail("JSON does not support infinite timestamp values.")));
+ {
+ jb.val.string.len = strlen(DT_INFINITY);
+ jb.val.string.val = pstrdup(DT_INFINITY);
+ }
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
+ {
+
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
+ jb.val.string.len = strlen(buf);
+ jb.val.string.val = pstrdup(buf);
+ }
else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
-
- jb.type = jbvString;
- jb.val.string.len = strlen(buf);
- jb.val.string.val = pstrdup(buf);
}
break;
case JSONBTYPE_TIMESTAMPTZ:
@@ -770,23 +777,23 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
char buf[MAXDATELEN + 1];
timestamp = DatumGetTimestamp(val);
+ jb.type = jbvString;
- /* XSD doesn't support infinite values */
if (TIMESTAMP_NOT_FINITE(timestamp))
- ereport(ERROR,
- (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
- errmsg("timestamp out of range"),
- errdetail("JSON does not support infinite timestamp values.")));
+ {
+ jb.val.string.len = strlen(DT_INFINITY);
+ jb.val.string.val = pstrdup(DT_INFINITY);
+ }
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
+ {
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
+ jb.val.string.len = strlen(buf);
+ jb.val.string.val = pstrdup(buf);
+ }
else
ereport(ERROR,
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
errmsg("timestamp out of range")));
-
- jb.type = jbvString;
- jb.val.string.len = strlen(buf);
- jb.val.string.val = pstrdup(buf);
}
break;
case JSONBTYPE_JSONCAST:
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index 16704363dc..3942c3bee9 100644
--- a/src/test/regress/expected/json.out
+++ b/src/test/regress/expected/json.out
@@ -426,6 +426,30 @@ select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
(1 row)
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
SELECT json_agg(q)
FROM ( SELECT $$a$$ || x AS b, y AS c,
diff --git a/src/test/regress/expected/json_1.out b/src/test/regress/expected/json_1.out
index 807814641d..38f1526288 100644
--- a/src/test/regress/expected/json_1.out
+++ b/src/test/regress/expected/json_1.out
@@ -426,6 +426,30 @@ select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
(1 row)
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
SELECT json_agg(q)
FROM ( SELECT $$a$$ || x AS b, y AS c,
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index 6c6ed950f0..0d558901e9 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -330,6 +330,30 @@ select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
(1 row)
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
CREATE TEMP TABLE rows AS
SELECT x, 'txt' || x as y
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
index f30148d51c..694b6ea5f5 100644
--- a/src/test/regress/expected/jsonb_1.out
+++ b/src/test/regress/expected/jsonb_1.out
@@ -330,6 +330,30 @@ select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
(1 row)
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
CREATE TEMP TABLE rows AS
SELECT x, 'txt' || x as y
diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql
index 53a37a8843..53832a01fa 100644
--- a/src/test/regress/sql/json.sql
+++ b/src/test/regress/sql/json.sql
@@ -111,6 +111,12 @@ SET LOCAL TIME ZONE -8;
select to_json(timestamptz '2014-05-28 12:22:35.614298-04');
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
SELECT json_agg(q)
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 53cc2393c6..676e1a7d4c 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -74,6 +74,12 @@ SET LOCAL TIME ZONE -8;
select to_jsonb(timestamptz '2014-05-28 12:22:35.614298-04');
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
CREATE TEMP TABLE rows AS