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:
parent
0734bc4991
commit
baf0f90bd5
3 changed files with 78 additions and 9 deletions
22
FU/Pg.pm
22
FU/Pg.pm
|
|
@ -638,8 +638,24 @@ stored in a 64-bit integer. If you prefer that, use:
|
||||||
|
|
||||||
=item date
|
=item date
|
||||||
|
|
||||||
Converted between strings in C<YYYY-MM-DD> format. Postgres accepts a bunch of
|
Converted between seconds since Unix epoch as an integer, with the time fixed
|
||||||
alternative date formats, this module does not.
|
at C<00:00:00 UTC>. When used as bind parameter, the time part is truncated.
|
||||||
|
This format makes for easy comparison with other timestamps, but if you prefer
|
||||||
|
to work with strings in the C<YYYY-MM-DD> format instead, use:
|
||||||
|
|
||||||
|
$conn->set_type(date => '$date_str');
|
||||||
|
|
||||||
|
Postgres accepts a bunch of alternative date formats for bind paramaters, this
|
||||||
|
module does not.
|
||||||
|
|
||||||
|
=item time
|
||||||
|
|
||||||
|
Converted between floating point seconds since C<00:00:00>, supporting
|
||||||
|
microsecond precision. This format allows for easy comparison against Unix
|
||||||
|
timestamps (time of day = C<$timestamp % 86400>) and can be added to an integer
|
||||||
|
date value to form a complete timestamp.
|
||||||
|
|
||||||
|
(There's no support for the string format yet)
|
||||||
|
|
||||||
=item json / jsonb
|
=item json / jsonb
|
||||||
|
|
||||||
|
|
@ -689,7 +705,7 @@ can work around.
|
||||||
|
|
||||||
=item money
|
=item money
|
||||||
|
|
||||||
=item time / timetz
|
=item timetz
|
||||||
|
|
||||||
=item bit / varbit
|
=item bit / varbit
|
||||||
|
|
||||||
|
|
|
||||||
51
c/pgtypes.c
51
c/pgtypes.c
|
|
@ -553,17 +553,26 @@ SENDFN(uuid) {
|
||||||
|
|
||||||
RECVFN(timestamp) {
|
RECVFN(timestamp) {
|
||||||
RLEN(8);
|
RLEN(8);
|
||||||
IV ts = fu_frombeI(64, buf);
|
return newSVnv(((NV)fu_frombeI(64, buf) / 1000000) + UNIX_PG_EPOCH);
|
||||||
return newSVnv(((NV)ts / 1000000) + UNIX_PG_EPOCH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SENDFN(timestamp) {
|
SENDFN(timestamp) {
|
||||||
if (!looks_like_number(val)) SERR("expected a number");
|
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);
|
fustr_writebeI(64, out, ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
RECVFN(date) {
|
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);
|
RLEN(4);
|
||||||
time_t ts = ((time_t)fu_frombeI(32, buf)) * 86400 + UNIX_PG_EPOCH;
|
time_t ts = ((time_t)fu_frombeI(32, buf)) * 86400 + UNIX_PG_EPOCH;
|
||||||
struct tm tm;
|
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);
|
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;
|
int year, month, day;
|
||||||
if (sscanf(SvPV_nolen(val), "%4d-%2d-%2d", &year, &month, &day) != 3) SERR("invalid date format");
|
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
|
/* Can't use mktime() hackery here because libc has no UTC variant. Code
|
||||||
|
|
@ -593,6 +602,16 @@ SENDFN(date) {
|
||||||
|
|
||||||
#undef UNIX_PG_EPOCH
|
#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 SIV
|
||||||
#undef RLEN
|
#undef RLEN
|
||||||
#undef RECVFN
|
#undef RECVFN
|
||||||
|
|
@ -696,7 +715,7 @@ SENDFN(date) {
|
||||||
B( 1042, "bpchar", text )\
|
B( 1042, "bpchar", text )\
|
||||||
B( 1043, "varchar", text )\
|
B( 1043, "varchar", text )\
|
||||||
B( 1082, "date", date )\
|
B( 1082, "date", date )\
|
||||||
/* 1083 time */\
|
B( 1083, "time", time )\
|
||||||
B( 1114, "timestamp", timestamp)\
|
B( 1114, "timestamp", timestamp)\
|
||||||
A( 1115, "_timestamp", 1114 )\
|
A( 1115, "_timestamp", 1114 )\
|
||||||
A( 1182, "_date", 1082 )\
|
A( 1182, "_date", 1082 )\
|
||||||
|
|
@ -770,9 +789,23 @@ static const fupg_type fupg_builtin[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef BUILTINS
|
#undef BUILTINS
|
||||||
|
|
||||||
#define FUPG_BUILTIN (sizeof(fupg_builtin) / sizeof(fupg_type))
|
#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 };
|
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) {
|
static const fupg_type *fupg_builtin_byname(const char *name) {
|
||||||
size_t i;
|
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++)
|
for (i=0; i<FUPG_BUILTIN; i++)
|
||||||
if (strcmp(fupg_builtin[i].name.n, name) == 0)
|
if (strcmp(fupg_builtin[i].name.n, name) == 0)
|
||||||
return fupg_builtin+i;
|
return fupg_builtin+i;
|
||||||
|
|
|
||||||
14
t/pgtypes.t
14
t/pgtypes.t
|
|
@ -116,6 +116,13 @@ v timestamptz => 1740133814.705915, undef, '2025-02-21 10:30:14.705915+00';
|
||||||
v timestamp => 0, undef, '1970-01-01 00:00:00';
|
v timestamp => 0, undef, '1970-01-01 00:00:00';
|
||||||
v timestamp => 1740133814.705915, undef, '2025-02-21 10:30:14.705915';
|
v timestamp => 1740133814.705915, undef, '2025-02-21 10:30:14.705915';
|
||||||
|
|
||||||
|
v date => 0, undef, '1970-01-01';
|
||||||
|
v date => 915753600, undef, '1999-01-08';
|
||||||
|
v date => 1740355200, undef, '2025-02-24';
|
||||||
|
f date => '';
|
||||||
|
f date => '1970-01-01';
|
||||||
|
|
||||||
|
$conn->set_type(date => '$date_str');
|
||||||
v date => '1970-01-01';
|
v date => '1970-01-01';
|
||||||
v date => '1999-01-08';
|
v date => '1999-01-08';
|
||||||
v date => '2025-02-24';
|
v date => '2025-02-24';
|
||||||
|
|
@ -124,6 +131,13 @@ f date => '2025-';
|
||||||
f date => '2025-02-';
|
f date => '2025-02-';
|
||||||
f date => '1999-Jan-08';
|
f date => '1999-Jan-08';
|
||||||
|
|
||||||
|
v time => 0, undef, '00:00:00';
|
||||||
|
v time => 60.123456, undef, '00:01:00.123456';
|
||||||
|
v time => 3600 * 13 + 43 * 60 + 19 + 0.987654, undef, '13:43:19.987654';
|
||||||
|
f time => '';
|
||||||
|
f time => -1;
|
||||||
|
f time => 86400.1;
|
||||||
|
|
||||||
v 'int[]', [], undef, '{}';
|
v 'int[]', [], undef, '{}';
|
||||||
v 'int[]', [1], undef, '{1}';
|
v 'int[]', [1], undef, '{1}';
|
||||||
v 'int[]', [1,-3,undef,3], undef, '{1,-3,NULL,3}';
|
v 'int[]', [1,-3,undef,3], undef, '{1,-3,NULL,3}';
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue