Skip to content

Commit 6dda292

Browse files
committed
Allow datetime values in JsonbValue
SQL/JSON standard allows manipulation with datetime values. So, it appears to be convinient to allow datetime values to be represented in JsonbValue struct. These datetime values are allowed for temporary representation only. During serialization datetime values are converted into strings. SQL/JSON requires writing timestamps with timezone in the same timezone offset as they were parsed. This is why we allow storage of timezone offset in JsonbValue struct. For the same reason timezone offset argument is added to JsonEncodeDateTime() function. Extracted from original patch by Nikita Glukhov, Teodor Sigaev, Oleg Bartunov. Revised by me. Comments were adjusted by Liudmila Mantrova. Discussion: https://postgr.es/m/fcc6fc6a-b497-f39a-923d-aa34d0c588e8%402ndQuadrant.com Discussion: https://postgr.es/m/CAPpHfdsZgYEra_PeCLGNoXOWYx6iU-S3wF8aX0ObQUcZU%2B4XTw%40mail.gmail.com Author: Nikita Glukhov, Teodor Sigaev, Oleg Bartunov, Alexander Korotkov, Liudmila Mantrova Reviewed-by: Anastasia Lubennikova, Peter Eisentraut
1 parent 5bc4506 commit 6dda292

File tree

5 files changed

+94
-13
lines changed

5 files changed

+94
-13
lines changed

src/backend/utils/adt/json.c

+26-6
Original file line numberDiff line numberDiff line change
@@ -1506,23 +1506,23 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15061506
{
15071507
char buf[MAXDATELEN + 1];
15081508

1509-
JsonEncodeDateTime(buf, val, DATEOID);
1509+
JsonEncodeDateTime(buf, val, DATEOID, NULL);
15101510
appendStringInfo(result, "\"%s\"", buf);
15111511
}
15121512
break;
15131513
case JSONTYPE_TIMESTAMP:
15141514
{
15151515
char buf[MAXDATELEN + 1];
15161516

1517-
JsonEncodeDateTime(buf, val, TIMESTAMPOID);
1517+
JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
15181518
appendStringInfo(result, "\"%s\"", buf);
15191519
}
15201520
break;
15211521
case JSONTYPE_TIMESTAMPTZ:
15221522
{
15231523
char buf[MAXDATELEN + 1];
15241524

1525-
JsonEncodeDateTime(buf, val, TIMESTAMPTZOID);
1525+
JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
15261526
appendStringInfo(result, "\"%s\"", buf);
15271527
}
15281528
break;
@@ -1550,10 +1550,11 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15501550

15511551
/*
15521552
* Encode 'value' of datetime type 'typid' into JSON string in ISO format using
1553-
* optionally preallocated buffer 'buf'.
1553+
* optionally preallocated buffer 'buf'. Optional 'tzp' determines time-zone
1554+
* offset (in seconds) in which we want to show timestamptz.
15541555
*/
15551556
char *
1556-
JsonEncodeDateTime(char *buf, Datum value, Oid typid)
1557+
JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
15571558
{
15581559
if (!buf)
15591560
buf = palloc(MAXDATELEN + 1);
@@ -1630,11 +1631,30 @@ JsonEncodeDateTime(char *buf, Datum value, Oid typid)
16301631
const char *tzn = NULL;
16311632

16321633
timestamp = DatumGetTimestampTz(value);
1634+
1635+
/*
1636+
* If a time zone is specified, we apply the time-zone shift,
1637+
* convert timestamptz to pg_tm as if it were without a time
1638+
* zone, and then use the specified time zone for converting
1639+
* the timestamp into a string.
1640+
*/
1641+
if (tzp)
1642+
{
1643+
tz = *tzp;
1644+
timestamp -= (TimestampTz) tz * USECS_PER_SEC;
1645+
}
1646+
16331647
/* Same as timestamptz_out(), but forcing DateStyle */
16341648
if (TIMESTAMP_NOT_FINITE(timestamp))
16351649
EncodeSpecialTimestamp(timestamp, buf);
1636-
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
1650+
else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
1651+
tzp ? NULL : &tzn, NULL) == 0)
1652+
{
1653+
if (tzp)
1654+
tm.tm_isdst = 1; /* set time-zone presence flag */
1655+
16371656
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
1657+
}
16381658
else
16391659
ereport(ERROR,
16401660
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),

src/backend/utils/adt/jsonb.c

+24-3
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,24 @@ JsonbTypeName(JsonbValue *jbv)
206206
return "boolean";
207207
case jbvNull:
208208
return "null";
209+
case jbvDatetime:
210+
switch (jbv->val.datetime.typid)
211+
{
212+
case DATEOID:
213+
return "date";
214+
case TIMEOID:
215+
return "time without time zone";
216+
case TIMETZOID:
217+
return "time with time zone";
218+
case TIMESTAMPOID:
219+
return "timestamp without time zone";
220+
case TIMESTAMPTZOID:
221+
return "timestamp with time zone";
222+
default:
223+
elog(ERROR, "unrecognized jsonb value datetime type: %d",
224+
jbv->val.datetime.typid);
225+
}
226+
return "unknown";
209227
default:
210228
elog(ERROR, "unrecognized jsonb value type: %d", jbv->type);
211229
return "unknown";
@@ -805,17 +823,20 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
805823
break;
806824
case JSONBTYPE_DATE:
807825
jb.type = jbvString;
808-
jb.val.string.val = JsonEncodeDateTime(NULL, val, DATEOID);
826+
jb.val.string.val = JsonEncodeDateTime(NULL, val,
827+
DATEOID, NULL);
809828
jb.val.string.len = strlen(jb.val.string.val);
810829
break;
811830
case JSONBTYPE_TIMESTAMP:
812831
jb.type = jbvString;
813-
jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPOID);
832+
jb.val.string.val = JsonEncodeDateTime(NULL, val,
833+
TIMESTAMPOID, NULL);
814834
jb.val.string.len = strlen(jb.val.string.val);
815835
break;
816836
case JSONBTYPE_TIMESTAMPTZ:
817837
jb.type = jbvString;
818-
jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPTZOID);
838+
jb.val.string.val = JsonEncodeDateTime(NULL, val,
839+
TIMESTAMPTZOID, NULL);
819840
jb.val.string.len = strlen(jb.val.string.val);
820841
break;
821842
case JSONBTYPE_JSONCAST:

src/backend/utils/adt/jsonb_util.c

+21
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#include "postgres.h"
1515

1616
#include "catalog/pg_collation.h"
17+
#include "catalog/pg_type.h"
1718
#include "miscadmin.h"
1819
#include "utils/builtins.h"
20+
#include "utils/datetime.h"
1921
#include "utils/hashutils.h"
22+
#include "utils/jsonapi.h"
2023
#include "utils/jsonb.h"
2124
#include "utils/memutils.h"
2225
#include "utils/varlena.h"
@@ -244,6 +247,8 @@ compareJsonbContainers(JsonbContainer *a, JsonbContainer *b)
244247
break;
245248
case jbvBinary:
246249
elog(ERROR, "unexpected jbvBinary value");
250+
case jbvDatetime:
251+
elog(ERROR, "unexpected jbvDatetime value");
247252
}
248253
}
249254
else
@@ -1786,6 +1791,22 @@ convertJsonbScalar(StringInfo buffer, JEntry *jentry, JsonbValue *scalarVal)
17861791
JENTRY_ISBOOL_TRUE : JENTRY_ISBOOL_FALSE;
17871792
break;
17881793

1794+
case jbvDatetime:
1795+
{
1796+
char buf[MAXDATELEN + 1];
1797+
size_t len;
1798+
1799+
JsonEncodeDateTime(buf,
1800+
scalarVal->val.datetime.value,
1801+
scalarVal->val.datetime.typid,
1802+
&scalarVal->val.datetime.tz);
1803+
len = strlen(buf);
1804+
appendToBuffer(buffer, buf, len);
1805+
1806+
*jentry = len;
1807+
}
1808+
break;
1809+
17891810
default:
17901811
elog(ERROR, "invalid jsonb scalar type");
17911812
}

src/include/utils/jsonapi.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
161161
extern text *transform_json_string_values(text *json, void *action_state,
162162
JsonTransformStringValuesAction transform_action);
163163

164-
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid);
164+
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid,
165+
const int *tzp);
165166

166167
#endif /* JSONAPI_H */

src/include/utils/jsonb.h

+21-3
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,15 @@ enum jbvType
241241
jbvArray = 0x10,
242242
jbvObject,
243243
/* Binary (i.e. struct Jsonb) jbvArray/jbvObject */
244-
jbvBinary
244+
jbvBinary,
245+
246+
/*
247+
* Virtual types.
248+
*
249+
* These types are used only for in-memory JSON processing and serialized
250+
* into JSON strings when outputted to json/jsonb.
251+
*/
252+
jbvDatetime = 0x20,
245253
};
246254

247255
/*
@@ -282,11 +290,21 @@ struct JsonbValue
282290
int len;
283291
JsonbContainer *data;
284292
} binary; /* Array or object, in on-disk format */
293+
294+
struct
295+
{
296+
Datum value;
297+
Oid typid;
298+
int32 typmod;
299+
int tz; /* Numeric time zone, in seconds, for
300+
* TimestampTz data type */
301+
} datetime;
285302
} val;
286303
};
287304

288-
#define IsAJsonbScalar(jsonbval) ((jsonbval)->type >= jbvNull && \
289-
(jsonbval)->type <= jbvBool)
305+
#define IsAJsonbScalar(jsonbval) (((jsonbval)->type >= jbvNull && \
306+
(jsonbval)->type <= jbvBool) || \
307+
(jsonbval)->type == jbvDatetime)
290308

291309
/*
292310
* Key/value pair within an Object.

0 commit comments

Comments
 (0)