Skip to content

Commit cc4fede

Browse files
committed
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
1 parent d91da5e commit cc4fede

File tree

5 files changed

+109
-95
lines changed

5 files changed

+109
-95
lines changed

src/backend/utils/adt/date.c

+2-4
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@
4141
#endif
4242

4343

44-
static int time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
45-
static int timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
4644
static int tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result);
4745
static int tm2timetz(struct pg_tm *tm, fsec_t fsec, int tz, TimeTzADT *result);
4846
static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
@@ -1249,7 +1247,7 @@ tm2time(struct pg_tm *tm, fsec_t fsec, TimeADT *result)
12491247
* If out of this range, leave as UTC (in practice that could only happen
12501248
* if pg_time_t is just 32 bits) - thomas 97/05/27
12511249
*/
1252-
static int
1250+
int
12531251
time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec)
12541252
{
12551253
tm->tm_hour = time / USECS_PER_HOUR;
@@ -2073,7 +2071,7 @@ timetztypmodout(PG_FUNCTION_ARGS)
20732071
/* timetz2tm()
20742072
* Convert TIME WITH TIME ZONE data type to POSIX time structure.
20752073
*/
2076-
static int
2074+
int
20772075
timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp)
20782076
{
20792077
TimeOffset trem = time->time;

src/backend/utils/adt/json.c

+93-29
Original file line numberDiff line numberDiff line change
@@ -1503,12 +1503,70 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15031503
pfree(outputstr);
15041504
break;
15051505
case JSONTYPE_DATE:
1506+
{
1507+
char buf[MAXDATELEN + 1];
1508+
1509+
JsonEncodeDateTime(buf, val, DATEOID);
1510+
appendStringInfo(result, "\"%s\"", buf);
1511+
}
1512+
break;
1513+
case JSONTYPE_TIMESTAMP:
1514+
{
1515+
char buf[MAXDATELEN + 1];
1516+
1517+
JsonEncodeDateTime(buf, val, TIMESTAMPOID);
1518+
appendStringInfo(result, "\"%s\"", buf);
1519+
}
1520+
break;
1521+
case JSONTYPE_TIMESTAMPTZ:
1522+
{
1523+
char buf[MAXDATELEN + 1];
1524+
1525+
JsonEncodeDateTime(buf, val, TIMESTAMPTZOID);
1526+
appendStringInfo(result, "\"%s\"", buf);
1527+
}
1528+
break;
1529+
case JSONTYPE_JSON:
1530+
/* JSON and JSONB output will already be escaped */
1531+
outputstr = OidOutputFunctionCall(outfuncoid, val);
1532+
appendStringInfoString(result, outputstr);
1533+
pfree(outputstr);
1534+
break;
1535+
case JSONTYPE_CAST:
1536+
/* outfuncoid refers to a cast function, not an output function */
1537+
jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
1538+
outputstr = text_to_cstring(jsontext);
1539+
appendStringInfoString(result, outputstr);
1540+
pfree(outputstr);
1541+
pfree(jsontext);
1542+
break;
1543+
default:
1544+
outputstr = OidOutputFunctionCall(outfuncoid, val);
1545+
escape_json(result, outputstr);
1546+
pfree(outputstr);
1547+
break;
1548+
}
1549+
}
1550+
1551+
/*
1552+
* Encode 'value' of datetime type 'typid' into JSON string in ISO format using
1553+
* optionally preallocated buffer 'buf'.
1554+
*/
1555+
char *
1556+
JsonEncodeDateTime(char *buf, Datum value, Oid typid)
1557+
{
1558+
if (!buf)
1559+
buf = palloc(MAXDATELEN + 1);
1560+
1561+
switch (typid)
1562+
{
1563+
case DATEOID:
15061564
{
15071565
DateADT date;
15081566
struct pg_tm tm;
1509-
char buf[MAXDATELEN + 1];
15101567

1511-
date = DatumGetDateADT(val);
1568+
date = DatumGetDateADT(value);
1569+
15121570
/* Same as date_out(), but forcing DateStyle */
15131571
if (DATE_NOT_FINITE(date))
15141572
EncodeSpecialDate(date, buf);
@@ -1518,17 +1576,40 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15181576
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
15191577
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
15201578
}
1521-
appendStringInfo(result, "\"%s\"", buf);
15221579
}
15231580
break;
1524-
case JSONTYPE_TIMESTAMP:
1581+
case TIMEOID:
1582+
{
1583+
TimeADT time = DatumGetTimeADT(value);
1584+
struct pg_tm tt,
1585+
*tm = &tt;
1586+
fsec_t fsec;
1587+
1588+
/* Same as time_out(), but forcing DateStyle */
1589+
time2tm(time, tm, &fsec);
1590+
EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
1591+
}
1592+
break;
1593+
case TIMETZOID:
1594+
{
1595+
TimeTzADT *time = DatumGetTimeTzADTP(value);
1596+
struct pg_tm tt,
1597+
*tm = &tt;
1598+
fsec_t fsec;
1599+
int tz;
1600+
1601+
/* Same as timetz_out(), but forcing DateStyle */
1602+
timetz2tm(time, tm, &fsec, &tz);
1603+
EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
1604+
}
1605+
break;
1606+
case TIMESTAMPOID:
15251607
{
15261608
Timestamp timestamp;
15271609
struct pg_tm tm;
15281610
fsec_t fsec;
1529-
char buf[MAXDATELEN + 1];
15301611

1531-
timestamp = DatumGetTimestamp(val);
1612+
timestamp = DatumGetTimestamp(value);
15321613
/* Same as timestamp_out(), but forcing DateStyle */
15331614
if (TIMESTAMP_NOT_FINITE(timestamp))
15341615
EncodeSpecialTimestamp(timestamp, buf);
@@ -1538,19 +1619,17 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15381619
ereport(ERROR,
15391620
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
15401621
errmsg("timestamp out of range")));
1541-
appendStringInfo(result, "\"%s\"", buf);
15421622
}
15431623
break;
1544-
case JSONTYPE_TIMESTAMPTZ:
1624+
case TIMESTAMPTZOID:
15451625
{
15461626
TimestampTz timestamp;
15471627
struct pg_tm tm;
15481628
int tz;
15491629
fsec_t fsec;
15501630
const char *tzn = NULL;
1551-
char buf[MAXDATELEN + 1];
15521631

1553-
timestamp = DatumGetTimestampTz(val);
1632+
timestamp = DatumGetTimestampTz(value);
15541633
/* Same as timestamptz_out(), but forcing DateStyle */
15551634
if (TIMESTAMP_NOT_FINITE(timestamp))
15561635
EncodeSpecialTimestamp(timestamp, buf);
@@ -1560,29 +1639,14 @@ datum_to_json(Datum val, bool is_null, StringInfo result,
15601639
ereport(ERROR,
15611640
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
15621641
errmsg("timestamp out of range")));
1563-
appendStringInfo(result, "\"%s\"", buf);
15641642
}
15651643
break;
1566-
case JSONTYPE_JSON:
1567-
/* JSON and JSONB output will already be escaped */
1568-
outputstr = OidOutputFunctionCall(outfuncoid, val);
1569-
appendStringInfoString(result, outputstr);
1570-
pfree(outputstr);
1571-
break;
1572-
case JSONTYPE_CAST:
1573-
/* outfuncoid refers to a cast function, not an output function */
1574-
jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
1575-
outputstr = text_to_cstring(jsontext);
1576-
appendStringInfoString(result, outputstr);
1577-
pfree(outputstr);
1578-
pfree(jsontext);
1579-
break;
15801644
default:
1581-
outputstr = OidOutputFunctionCall(outfuncoid, val);
1582-
escape_json(result, outputstr);
1583-
pfree(outputstr);
1584-
break;
1645+
elog(ERROR, "unknown jsonb value datetime type oid %d", typid);
1646+
return NULL;
15851647
}
1648+
1649+
return buf;
15861650
}
15871651

15881652
/*

src/backend/utils/adt/jsonb.c

+9-61
Original file line numberDiff line numberDiff line change
@@ -786,71 +786,19 @@ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
786786
}
787787
break;
788788
case JSONBTYPE_DATE:
789-
{
790-
DateADT date;
791-
struct pg_tm tm;
792-
char buf[MAXDATELEN + 1];
793-
794-
date = DatumGetDateADT(val);
795-
/* Same as date_out(), but forcing DateStyle */
796-
if (DATE_NOT_FINITE(date))
797-
EncodeSpecialDate(date, buf);
798-
else
799-
{
800-
j2date(date + POSTGRES_EPOCH_JDATE,
801-
&(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
802-
EncodeDateOnly(&tm, USE_XSD_DATES, buf);
803-
}
804-
jb.type = jbvString;
805-
jb.val.string.len = strlen(buf);
806-
jb.val.string.val = pstrdup(buf);
807-
}
789+
jb.type = jbvString;
790+
jb.val.string.val = JsonEncodeDateTime(NULL, val, DATEOID);
791+
jb.val.string.len = strlen(jb.val.string.val);
808792
break;
809793
case JSONBTYPE_TIMESTAMP:
810-
{
811-
Timestamp timestamp;
812-
struct pg_tm tm;
813-
fsec_t fsec;
814-
char buf[MAXDATELEN + 1];
815-
816-
timestamp = DatumGetTimestamp(val);
817-
/* Same as timestamp_out(), but forcing DateStyle */
818-
if (TIMESTAMP_NOT_FINITE(timestamp))
819-
EncodeSpecialTimestamp(timestamp, buf);
820-
else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
821-
EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
822-
else
823-
ereport(ERROR,
824-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
825-
errmsg("timestamp out of range")));
826-
jb.type = jbvString;
827-
jb.val.string.len = strlen(buf);
828-
jb.val.string.val = pstrdup(buf);
829-
}
794+
jb.type = jbvString;
795+
jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPOID);
796+
jb.val.string.len = strlen(jb.val.string.val);
830797
break;
831798
case JSONBTYPE_TIMESTAMPTZ:
832-
{
833-
TimestampTz timestamp;
834-
struct pg_tm tm;
835-
int tz;
836-
fsec_t fsec;
837-
const char *tzn = NULL;
838-
char buf[MAXDATELEN + 1];
839-
840-
timestamp = DatumGetTimestampTz(val);
841-
/* Same as timestamptz_out(), but forcing DateStyle */
842-
if (TIMESTAMP_NOT_FINITE(timestamp))
843-
EncodeSpecialTimestamp(timestamp, buf);
844-
else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
845-
EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
846-
else
847-
ereport(ERROR,
848-
(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
849-
errmsg("timestamp out of range")));
850-
jb.type = jbvString;
851-
jb.val.string.len = strlen(buf);
852-
jb.val.string.val = pstrdup(buf);
853-
}
799+
jb.type = jbvString;
800+
jb.val.string.val = JsonEncodeDateTime(NULL, val, TIMESTAMPTZOID);
801+
jb.val.string.len = strlen(jb.val.string.val);
854802
break;
855803
case JSONBTYPE_JSONCAST:
856804
case JSONBTYPE_JSON:

src/include/utils/date.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
#include <math.h>
1818

1919
#include "fmgr.h"
20-
20+
#include "datatype/timestamp.h"
2121

2222
typedef int32 DateADT;
2323

@@ -73,5 +73,7 @@ extern void EncodeSpecialDate(DateADT dt, char *str);
7373
extern DateADT GetSQLCurrentDate(void);
7474
extern TimeTzADT *GetSQLCurrentTime(int32 typmod);
7575
extern TimeADT GetSQLLocalTime(int32 typmod);
76+
extern int time2tm(TimeADT time, struct pg_tm *tm, fsec_t *fsec);
77+
extern int timetz2tm(TimeTzADT *time, struct pg_tm *tm, fsec_t *fsec, int *tzp);
7678

7779
#endif /* DATE_H */

src/include/utils/jsonapi.h

+2
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,6 @@ extern Jsonb *transform_jsonb_string_values(Jsonb *jsonb, void *action_state,
147147
extern text *transform_json_string_values(text *json, void *action_state,
148148
JsonTransformStringValuesAction transform_action);
149149

150+
extern char *JsonEncodeDateTime(char *buf, Datum value, Oid typid);
151+
150152
#endif /* JSONAPI_H */

0 commit comments

Comments
 (0)