postgres.git / summary / log / commit / refs

commit    79afe6e66f022ea9cd6c34c334214b2fda870faa
Author:   Andrew Dunstan <andrew@dunslane.net>
Date:     Thu Feb 26 17:34:43 2015 +0000
First released in: 9.5.0, 9.4.2

    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.


src/backend/utils/adt/json.c | 43 ++++++++++++++++++------------------ src/test/regress/expected/json.out | 24 ++++++++++++++++++++ src/test/regress/expected/json_1.out | 24 ++++++++++++++++++++ src/test/regress/sql/json.sql | 6 +++++ 4 files changed, 76 insertions(+), 21 deletions(-) diff --git a/src/backend/utils/adt/json.c b/src/backend/utils/adt/json.c index 48f03e0b36a..ed27f72f1dd 100644 --- a/src/backend/utils/adt/json.c +++ b/src/backend/utils/adt/json.c @@ -33,6 +33,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 @@ -1427,20 +1430,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: @@ -1452,20 +1453,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: @@ -1479,20 +1480,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/test/regress/expected/json.out b/src/test/regress/expected/json.out index c916678427d..bc64512eb10 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 ce63bfb227e..2738485e5fa 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/sql/json.sql b/src/test/regress/sql/json.sql index a4eaa1fbc0b..ab2dd2ed0d5 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) [parent: d7211511270b]