Pg: Add "time" type, change default format of "date" type but support "$date_str"

Using a consistent numeric interpretation for timestamps, dates and
times simplifies a bunch of operations. It certainly simplifies
conversion between the Postgres formats.
This commit is contained in:
Yorhel 2025-02-28 13:03:00 +01:00
parent 0734bc4991
commit baf0f90bd5
3 changed files with 78 additions and 9 deletions

View file

@ -553,17 +553,26 @@ SENDFN(uuid) {
RECVFN(timestamp) {
RLEN(8);
IV ts = fu_frombeI(64, buf);
return newSVnv(((NV)ts / 1000000) + UNIX_PG_EPOCH);
return newSVnv(((NV)fu_frombeI(64, buf) / 1000000) + UNIX_PG_EPOCH);
}
SENDFN(timestamp) {
if (!looks_like_number(val)) SERR("expected a number");
IV ts = (SvNV(val) - UNIX_PG_EPOCH) * 1000000;
I64 ts = (SvNV(val) - UNIX_PG_EPOCH) * 1000000;
fustr_writebeI(64, out, ts);
}
RECVFN(date) {
RLEN(4);
return newSVuv(((UV)fu_frombeI(32, buf)) * 86400 + UNIX_PG_EPOCH);
}
SENDFN(date) {
if (!looks_like_number(val)) SERR("expected a number");
fustr_writebeI(32, out, (SvIV(val) - UNIX_PG_EPOCH) / 86400);
}
RECVFN(date_str) {
RLEN(4);
time_t ts = ((time_t)fu_frombeI(32, buf)) * 86400 + UNIX_PG_EPOCH;
struct tm tm;
@ -571,7 +580,7 @@ RECVFN(date) {
return newSVpvf("%04d-%02d-%02d", tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday);
}
SENDFN(date) {
SENDFN(date_str) {
int year, month, day;
if (sscanf(SvPV_nolen(val), "%4d-%2d-%2d", &year, &month, &day) != 3) SERR("invalid date format");
/* Can't use mktime() hackery here because libc has no UTC variant. Code
@ -593,6 +602,16 @@ SENDFN(date) {
#undef UNIX_PG_EPOCH
RECVFN(time) {
RLEN(8);
return newSVnv(((NV)fu_frombeI(64, buf)) / 1000000);
}
SENDFN(time) {
if (!looks_like_number(val)) SERR("expected a number");
fustr_writebeI(64, out, SvNV(val) * 1000000);
}
#undef SIV
#undef RLEN
#undef RECVFN
@ -696,7 +715,7 @@ SENDFN(date) {
B( 1042, "bpchar", text )\
B( 1043, "varchar", text )\
B( 1082, "date", date )\
/* 1083 time */\
B( 1083, "time", time )\
B( 1114, "timestamp", timestamp)\
A( 1115, "_timestamp", 1114 )\
A( 1182, "_date", 1082 )\
@ -770,9 +789,23 @@ static const fupg_type fupg_builtin[] = {
};
#undef BUILTINS
#define FUPG_BUILTIN (sizeof(fupg_builtin) / sizeof(fupg_type))
/* List of special types for use with set_type() */
#define SPECIALS\
T("$date_str", date_str)
static const fupg_type fupg_specials[] = {
#define T(name, fun) { 0, 0, {name"\0"}, fupg_send_##fun, fupg_recv_##fun },
SPECIALS
#undef T
};
#undef SPECIALS
#define FUPG_SPECIALS (sizeof(fupg_specials) / sizeof(fupg_type))
static const fupg_type fupg_type_perlcb = { 0, 0, {"$perl_cb"}, fupg_send_perlcb, fupg_recv_perlcb };
@ -793,6 +826,12 @@ static const fupg_type *fupg_builtin_byoid(Oid oid) {
static const fupg_type *fupg_builtin_byname(const char *name) {
size_t i;
/* XXX: Can use binary search here if the list of specials grows.
* That list does not have to be ordered by oid. */
for (i=0; i<FUPG_SPECIALS; i++)
if (strcmp(fupg_specials[i].name.n, name) == 0)
return fupg_specials+i;
for (i=0; i<FUPG_BUILTIN; i++)
if (strcmp(fupg_builtin[i].name.n, name) == 0)
return fupg_builtin+i;