mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
Centralize json and jsonb handling of datetime types
The creates a single function JsonEncodeDateTime which will format these data types in an efficient and consistent manner. This will be all the more important when we come to jsonpath so we don't have to implement yet more code doing the same thing in two more places. This also extends the code to handle time and timetz types which were not previously handled specially. This requires exposing the time2tm and timetz2tm functions. Patch from Nikita Glukhov
This commit is contained in:
parent
d91da5eced
commit
cc4feded0a
@ -41,8 +41,6 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static int time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
|
|
||||||
static int timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
|
|
||||||
static int tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result);
|
static int tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result);
|
||||||
static int tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result);
|
static int tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result);
|
||||||
static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
|
static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
|
||||||
@ -1249,7 +1247,7 @@ tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
|
|||||||
* If out of this range, leave as UTC (in practice that could only happen
|
* If out of this range, leave as UTC (in practice that could only happen
|
||||||
* if pg_time_t is just 32 bits) - thomas 97/05/27
|
* if pg_time_t is just 32 bits) - thomas 97/05/27
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
|
time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
|
||||||
{
|
{
|
||||||
tm->tm_hour = time / USECS_PER_HOUR;
|
tm->tm_hour = time / USECS_PER_HOUR;
|
||||||
@ -2073,7 +2071,7 @@ timetztypmodout(PG_FUNCTION_ARGS)
|
|||||||
/* timetz2tm()
|
/* timetz2tm()
|
||||||
* Convert TIME WITH TIME ZONE data type to POSIX time structure.
|
* Convert TIME WITH TIME ZONE data type to POSIX time structure.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
|
timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
|
||||||
{
|
{
|
||||||
TimeOffset trem = time->time;
|
TimeOffset trem = time->time;
|
||||||
|
@ -1504,62 +1504,25 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
|
|||||||
break;
|
break;
|
||||||
case JSONTYPE_DATE:
|
case JSONTYPE_DATE:
|
||||||
{
|
{
|
||||||
DateADT date;
|
|
||||||
struct pg_tm tm;
|
|
||||||
char buf[MAXDATELEN + 1];
|
char buf[MAXDATELEN + 1];
|
||||||
|
|
||||||
date = DatumGetDateADT(val);
|
JsonEncodeDateTime(buf, val, DATEOID);
|
||||||
/* Same as date_out(), but forcing DateStyle */
|
|
||||||
if (DATE_NOT_FINITE(date))
|
|
||||||
EncodeSpecialDate(date, buf);
|
|
||||||
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;
|
break;
|
||||||
case JSONTYPE_TIMESTAMP:
|
case JSONTYPE_TIMESTAMP:
|
||||||
{
|
{
|
||||||
Timestamp timestamp;
|
|
||||||
struct pg_tm tm;
|
|
||||||
fsec_t fsec;
|
|
||||||
char buf[MAXDATELEN + 1];
|
char buf[MAXDATELEN + 1];
|
||||||
|
|
||||||
timestamp = DatumGetTimestamp(val);
|
JsonEncodeDateTime(buf, val, TIMESTAMPOID);
|
||||||
/* Same as timestamp_out(), but forcing DateStyle */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
|
||||||
EncodeSpecialTimestamp(timestamp, buf);
|
|
||||||
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
|
||||||
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
appendStringInfo(result, "\"%s\"", buf);
|
appendStringInfo(result, "\"%s\"", buf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONTYPE_TIMESTAMPTZ:
|
case JSONTYPE_TIMESTAMPTZ:
|
||||||
{
|
{
|
||||||
TimestampTz timestamp;
|
|
||||||
struct pg_tm tm;
|
|
||||||
int tz;
|
|
||||||
fsec_t fsec;
|
|
||||||
const char *tzn = NULL;
|
|
||||||
char buf[MAXDATELEN + 1];
|
char buf[MAXDATELEN + 1];
|
||||||
|
|
||||||
timestamp = DatumGetTimestampTz(val);
|
JsonEncodeDateTime(buf, val, TIMESTAMPTZOID);
|
||||||
/* Same as timestamptz_out(), but forcing DateStyle */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
|
||||||
EncodeSpecialTimestamp(timestamp, buf);
|
|
||||||
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
|
||||||
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
appendStringInfo(result, "\"%s\"", buf);
|
appendStringInfo(result, "\"%s\"", buf);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1585,6 +1548,107 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Encode 'value' of datetime type 'typid' into JSON string in ISO format using
|
||||||
|
* optionally preallocated buffer 'buf'.
|
||||||
|
*/
|
||||||
|
char *
|
||||||
|
JsonEncodeDateTime(char *buf, Datum value, Oid typid)
|
||||||
|
{
|
||||||
|
if (!buf)
|
||||||
|
buf = palloc(MAXDATELEN + 1);
|
||||||
|
|
||||||
|
switch (typid)
|
||||||
|
{
|
||||||
|
case DATEOID:
|
||||||
|
{
|
||||||
|
DateADT date;
|
||||||
|
struct pg_tm tm;
|
||||||
|
|
||||||
|
date = DatumGetDateADT(value);
|
||||||
|
|
||||||
|
/* Same as date_out(), but forcing DateStyle */
|
||||||
|
if (DATE_NOT_FINITE(date))
|
||||||
|
EncodeSpecialDate(date, buf);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
j2date(date + POSTGRES_EPOCH_JDATE,
|
||||||
|
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
||||||
|
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMEOID:
|
||||||
|
{
|
||||||
|
TimeADT time = DatumGetTimeADT(value);
|
||||||
|
struct pg_tm tt,
|
||||||
|
*tm = &tt;
|
||||||
|
fsec_t fsec;
|
||||||
|
|
||||||
|
/* Same as time_out(), but forcing DateStyle */
|
||||||
|
time2tm(time, tm, &fsec);
|
||||||
|
EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMETZOID:
|
||||||
|
{
|
||||||
|
TimeTzADT *time = DatumGetTimeTzADTP(value);
|
||||||
|
struct pg_tm tt,
|
||||||
|
*tm = &tt;
|
||||||
|
fsec_t fsec;
|
||||||
|
int tz;
|
||||||
|
|
||||||
|
/* Same as timetz_out(), but forcing DateStyle */
|
||||||
|
timetz2tm(time, tm, &fsec, &tz);
|
||||||
|
EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMESTAMPOID:
|
||||||
|
{
|
||||||
|
Timestamp timestamp;
|
||||||
|
struct pg_tm tm;
|
||||||
|
fsec_t fsec;
|
||||||
|
|
||||||
|
timestamp = DatumGetTimestamp(value);
|
||||||
|
/* Same as timestamp_out(), but forcing DateStyle */
|
||||||
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
|
EncodeSpecialTimestamp(timestamp, buf);
|
||||||
|
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
||||||
|
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("timestamp out of range")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMESTAMPTZOID:
|
||||||
|
{
|
||||||
|
TimestampTz timestamp;
|
||||||
|
struct pg_tm tm;
|
||||||
|
int tz;
|
||||||
|
fsec_t fsec;
|
||||||
|
const char *tzn = NULL;
|
||||||
|
|
||||||
|
timestamp = DatumGetTimestampTz(value);
|
||||||
|
/* Same as timestamptz_out(), but forcing DateStyle */
|
||||||
|
if (TIMESTAMP_NOT_FINITE(timestamp))
|
||||||
|
EncodeSpecialTimestamp(timestamp, buf);
|
||||||
|
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
||||||
|
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
||||||
|
else
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
||||||
|
errmsg("timestamp out of range")));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
elog(ERROR, "unknown jsonb value datetime type oid %d", typid);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Process a single dimension of an array.
|
* Process a single dimension of an array.
|
||||||
* If it's the innermost dimension, output the values, otherwise call
|
* If it's the innermost dimension, output the values, otherwise call
|
||||||
|
@ -786,71 +786,19 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_DATE:
|
case JSONBTYPE_DATE:
|
||||||
{
|
|
||||||
DateADT date;
|
|
||||||
struct pg_tm tm;
|
|
||||||
char buf[MAXDATELEN + 1];
|
|
||||||
|
|
||||||
date = DatumGetDateADT(val);
|
|
||||||
/* Same as date_out(), but forcing DateStyle */
|
|
||||||
if (DATE_NOT_FINITE(date))
|
|
||||||
EncodeSpecialDate(date, buf);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
j2date(date + POSTGRES_EPOCH_JDATE,
|
|
||||||
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
|
|
||||||
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
|
|
||||||
}
|
|
||||||
jb.type = jbvString;
|
jb.type = jbvString;
|
||||||
jb.val.string.len = strlen(buf);
|
jb.val.string.val = JsonEncodeDateTime(NULL, val, DATEOID);
|
||||||
jb.val.string.val = pstrdup(buf);
|
jb.val.string.len = strlen(jb.val.string.val);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_TIMESTAMP:
|
case JSONBTYPE_TIMESTAMP:
|
||||||
{
|
|
||||||
Timestamp timestamp;
|
|
||||||
struct pg_tm tm;
|
|
||||||
fsec_t fsec;
|
|
||||||
char buf[MAXDATELEN + 1];
|
|
||||||
|
|
||||||
timestamp = DatumGetTimestamp(val);
|
|
||||||
/* Same as timestamp_out(), but forcing DateStyle */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
|
||||||
EncodeSpecialTimestamp(timestamp, buf);
|
|
||||||
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
|
|
||||||
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
jb.type = jbvString;
|
jb.type = jbvString;
|
||||||
jb.val.string.len = strlen(buf);
|
jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPOID);
|
||||||
jb.val.string.val = pstrdup(buf);
|
jb.val.string.len = strlen(jb.val.string.val);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_TIMESTAMPTZ:
|
case JSONBTYPE_TIMESTAMPTZ:
|
||||||
{
|
|
||||||
TimestampTz timestamp;
|
|
||||||
struct pg_tm tm;
|
|
||||||
int tz;
|
|
||||||
fsec_t fsec;
|
|
||||||
const char *tzn = NULL;
|
|
||||||
char buf[MAXDATELEN + 1];
|
|
||||||
|
|
||||||
timestamp = DatumGetTimestampTz(val);
|
|
||||||
/* Same as timestamptz_out(), but forcing DateStyle */
|
|
||||||
if (TIMESTAMP_NOT_FINITE(timestamp))
|
|
||||||
EncodeSpecialTimestamp(timestamp, buf);
|
|
||||||
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
|
|
||||||
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
|
|
||||||
else
|
|
||||||
ereport(ERROR,
|
|
||||||
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
|
|
||||||
errmsg("timestamp out of range")));
|
|
||||||
jb.type = jbvString;
|
jb.type = jbvString;
|
||||||
jb.val.string.len = strlen(buf);
|
jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPTZOID);
|
||||||
jb.val.string.val = pstrdup(buf);
|
jb.val.string.len = strlen(jb.val.string.val);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case JSONBTYPE_JSONCAST:
|
case JSONBTYPE_JSONCAST:
|
||||||
case JSONBTYPE_JSON:
|
case JSONBTYPE_JSON:
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include "fmgr.h"
|
#include "fmgr.h"
|
||||||
|
#include "datatype/timestamp.h"
|
||||||
|
|
||||||
typedef int32 DateADT;
|
typedef int32 DateADT;
|
||||||
|
|
||||||
@ -73,5 +73,7 @@ extern void EncodeSpecialDate(DateADT dt, char *str);
|
|||||||
extern DateADT GetSQLCurrentDate(void);
|
extern DateADT GetSQLCurrentDate(void);
|
||||||
extern TimeTzADT *GetSQLCurrentTime(int32 typmod);
|
extern TimeTzADT *GetSQLCurrentTime(int32 typmod);
|
||||||
extern TimeADT GetSQLLocalTime(int32 typmod);
|
extern TimeADT GetSQLLocalTime(int32 typmod);
|
||||||
|
extern int time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
|
||||||
|
extern int timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
|
||||||
|
|
||||||
#endif /* DATE_H */
|
#endif /* DATE_H */
|
||||||
|
@ -147,4 +147,6 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
|
|||||||
extern text *transform_json_string_values(text *json, void *action_state,
|
extern text *transform_json_string_values(text *json, void *action_state,
|
||||||
JsonTransformStringValuesAction transform_action);
|
JsonTransformStringValuesAction transform_action);
|
||||||
|
|
||||||
|
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid);
|
||||||
|
|
||||||
#endif /* JSONAPI_H */
|
#endif /* JSONAPI_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user