Commit e56ec50c authored by Tom Lane's avatar Tom Lane

Use ISO 8601 format for dates converted to JSON, too.

Commit f30015b6 made this happen for
timestamp and timestamptz, but it seems pretty inconsistent to not
do it for simple dates as well.

(In passing, I re-pgindent'd json.c.)
parent 3e9f70f1
...@@ -120,7 +120,7 @@ ...@@ -120,7 +120,7 @@
<listitem> <listitem>
<para> <para>
When converting values of type <type>timestamp</> When converting values of type <type>date</>, <type>timestamp</>
or <type>timestamptz</> or <type>timestamptz</>
to <link linkend="datatype-json"><type>JSON</type></link>, render the to <link linkend="datatype-json"><type>JSON</type></link>, render the
values in a format compliant with ISO 8601 (Andrew Dunstan) values in a format compliant with ISO 8601 (Andrew Dunstan)
...@@ -129,7 +129,10 @@ ...@@ -129,7 +129,10 @@
<para> <para>
Previously such values were rendered according to the current Previously such values were rendered according to the current
<xref linkend="guc-datestyle"> setting; but many JSON processors <xref linkend="guc-datestyle"> setting; but many JSON processors
require timestamps to be in ISO 8601 format. require timestamps to be in ISO 8601 format. If necessary, the
previous behavior can be obtained by explicitly casting the datetime
value to <type>text</> before passing it to the JSON conversion
function.
</para> </para>
</listitem> </listitem>
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "parser/parse_coerce.h" #include "parser/parse_coerce.h"
#include "utils/array.h" #include "utils/array.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/date.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/json.h" #include "utils/json.h"
...@@ -55,8 +56,9 @@ typedef enum /* type categories for datum_to_json */ ...@@ -55,8 +56,9 @@ typedef enum /* type categories for datum_to_json */
JSONTYPE_NULL, /* null, so we didn't bother to identify */ JSONTYPE_NULL, /* null, so we didn't bother to identify */
JSONTYPE_BOOL, /* boolean (built-in types only) */ JSONTYPE_BOOL, /* boolean (built-in types only) */
JSONTYPE_NUMERIC, /* numeric (ditto) */ JSONTYPE_NUMERIC, /* numeric (ditto) */
JSONTYPE_TIMESTAMP, /* we use special formatting for timestamp */ JSONTYPE_DATE, /* we use special formatting for datetimes */
JSONTYPE_TIMESTAMPTZ, /* ... and timestamptz */ JSONTYPE_TIMESTAMP,
JSONTYPE_TIMESTAMPTZ,
JSONTYPE_JSON, /* JSON itself (and JSONB) */ JSONTYPE_JSON, /* JSON itself (and JSONB) */
JSONTYPE_ARRAY, /* array */ JSONTYPE_ARRAY, /* array */
JSONTYPE_COMPOSITE, /* composite */ JSONTYPE_COMPOSITE, /* composite */
...@@ -1267,6 +1269,10 @@ json_categorize_type(Oid typoid, ...@@ -1267,6 +1269,10 @@ json_categorize_type(Oid typoid,
*tcategory = JSONTYPE_NUMERIC; *tcategory = JSONTYPE_NUMERIC;
break; break;
case DATEOID:
*tcategory = JSONTYPE_DATE;
break;
case TIMESTAMPOID: case TIMESTAMPOID:
*tcategory = JSONTYPE_TIMESTAMP; *tcategory = JSONTYPE_TIMESTAMP;
break; break;
...@@ -1348,7 +1354,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result, ...@@ -1348,7 +1354,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
tcategory == JSONTYPE_CAST)) tcategory == JSONTYPE_CAST))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("key value must be scalar, not array, composite, or json"))); errmsg("key value must be scalar, not array, composite, or json")));
switch (tcategory) switch (tcategory)
{ {
...@@ -1388,6 +1394,30 @@ datum_to_json(Datum val, bool is_null, StringInfo result, ...@@ -1388,6 +1394,30 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
} }
pfree(outputstr); pfree(outputstr);
break; break;
case JSONTYPE_DATE:
{
DateADT date;
struct pg_tm tm;
char buf[MAXDATELEN + 1];
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.")));
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);
}
break;
case JSONTYPE_TIMESTAMP: case JSONTYPE_TIMESTAMP:
{ {
Timestamp timestamp; Timestamp timestamp;
...@@ -1410,7 +1440,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result, ...@@ -1410,7 +1440,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
(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); appendStringInfo(result, "\"%s\"", buf);
} }
break; break;
case JSONTYPE_TIMESTAMPTZ: case JSONTYPE_TIMESTAMPTZ:
...@@ -1437,7 +1467,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result, ...@@ -1437,7 +1467,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
(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); appendStringInfo(result, "\"%s\"", buf);
} }
break; break;
case JSONTYPE_JSON: case JSONTYPE_JSON:
...@@ -2305,20 +2335,21 @@ escape_json(StringInfo buf, const char *str) ...@@ -2305,20 +2335,21 @@ escape_json(StringInfo buf, const char *str)
appendStringInfoString(buf, "\\\""); appendStringInfoString(buf, "\\\"");
break; break;
case '\\': case '\\':
/* /*
* Unicode escapes are passed through as is. There is no * Unicode escapes are passed through as is. There is no
* requirement that they denote a valid character in the * requirement that they denote a valid character in the
* server encoding - indeed that is a big part of their * server encoding - indeed that is a big part of their
* usefulness. * usefulness.
* *
* All we require is that they consist of \uXXXX where * All we require is that they consist of \uXXXX where the Xs
* the Xs are hexadecimal digits. It is the responsibility * are hexadecimal digits. It is the responsibility of the
* of the caller of, say, to_json() to make sure that the * caller of, say, to_json() to make sure that the unicode
* unicode escape is valid. * escape is valid.
* *
* In the case of a jsonb string value being escaped, the * In the case of a jsonb string value being escaped, the only
* only unicode escape that should be present is \u0000, * unicode escape that should be present is \u0000, all the
* all the other unicode escapes will have been resolved. * other unicode escapes will have been resolved.
*/ */
if (p[1] == 'u' && if (p[1] == 'u' &&
isxdigit((unsigned char) p[2]) && isxdigit((unsigned char) p[2]) &&
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment