pg: timestamp(tz) types + more docs
This commit is contained in:
parent
8036b8f0bf
commit
6f1583ddad
3 changed files with 121 additions and 5 deletions
99
FU/Pg.pm
99
FU/Pg.pm
|
|
@ -176,11 +176,13 @@ Enable or disable caching of the prepared statement for this particular query.
|
|||
|
||||
=item $st->text_params($enable)
|
||||
|
||||
Enable or disable sending bind parameters in the text format.
|
||||
Enable or disable sending bind parameters in the text format. See
|
||||
L</"Formats and Types"> below for what this means.
|
||||
|
||||
=item $st->text_results($enable)
|
||||
|
||||
Enable or disable receiving query results in the text format.
|
||||
Enable or disable receiving query results in the text format. See
|
||||
L</"Formats and Types"> below for what this means.
|
||||
|
||||
=item $st->text($enable)
|
||||
|
||||
|
|
@ -463,7 +465,98 @@ the same time, that won't end well.
|
|||
|
||||
=head2 Formats and Types
|
||||
|
||||
I<TODO>
|
||||
The PostgreSQL wire protocol supports sending bind parameters and receiving
|
||||
query results in two different formats: text and binary. While the exact wire
|
||||
protocol is an implementation detail that you don't have to worry about, this
|
||||
module does have a different approach to processing the two formats.
|
||||
|
||||
When you enable C<text> mode, your bind parameters are sent verbatim, as text,
|
||||
to the PostgreSQL server, where they are then parsed, validated and
|
||||
interpreted. Likewise, when receiving query results in text mode, it is the
|
||||
PostreSQL server that is formatting the data into textual strings. Text mode is
|
||||
essentially a way to tell this module: "don't try to interpret my data, just
|
||||
send and receive everything as text!"
|
||||
|
||||
Instead, in the (default) C<binary> mode, the responsibility of converting
|
||||
Postgres data to and from Perl values lies with this module. This allows for a
|
||||
lot of type-specific conveniences, but has the downside of requiring special
|
||||
code for each supported PostgreSQL type. Most of the Postgres core types are
|
||||
supported by this module and convert in an intuitive way, but here's a few
|
||||
type-specific notes:
|
||||
|
||||
=over
|
||||
|
||||
=item bool
|
||||
|
||||
Boolean values are converted to C<builtin::true> and C<builtin::false>. As bind
|
||||
parameters, Perl's idea of truthiness is used: C<0>, C<false> and C<""> are
|
||||
false, everything else is true. Objects that overload I<bool> are also
|
||||
supported. C<undef> always converts to SQL C<NULL>.
|
||||
|
||||
=item bytea
|
||||
|
||||
The C<bytea> type represents arbitrary binary data and this module will pass
|
||||
that along as raw binary strings.
|
||||
|
||||
=item timestamp / timestamptz
|
||||
|
||||
These are converted to and from seconds since the Unix epoch as a floating
|
||||
point value, similar to the C<time()> (or better: C<Time::HiRes::time()>)
|
||||
functions.
|
||||
|
||||
The timestamp types in Postgres have microsecond accuracy. Floating point can
|
||||
represent that without loss for dates that are near enough to the epoch (still
|
||||
seems to be fine in 2025, at least), but this conversion may be lossy for dates
|
||||
far beyond or before the epoch.
|
||||
|
||||
=item json / jsonb
|
||||
|
||||
These types are converted through C<json_parse()> and C<json_format()> from
|
||||
L<FU::Util>.
|
||||
|
||||
While C<null> is a valid JSON value, there's currently no way to distinguish
|
||||
that from SQL C<NULL>. When sending C<undef> as bind parameter, it is sent as
|
||||
SQL C<NULL>.
|
||||
|
||||
=item arrays
|
||||
|
||||
PostgreSQL arrays automatically convert to and from Perl arrays as you'd
|
||||
expect. Arrays in PostgreSQL have the rather unusual feature that the starting
|
||||
index can be changed for each individual array, but this module doesn't support
|
||||
that. All arrays received from Postgres will use Perl's usual 0-based indexing
|
||||
and all arrays sent to Postgres will use their default 1-based indexing.
|
||||
|
||||
=item records / row types
|
||||
|
||||
These are converted to and from hashrefs.
|
||||
|
||||
=item geometric types
|
||||
|
||||
=item numeric
|
||||
|
||||
=item macaddr
|
||||
|
||||
=item money
|
||||
|
||||
=item date / time / timetz
|
||||
|
||||
=item bit / varbit
|
||||
|
||||
=item tsvector / tsquery
|
||||
|
||||
=item Extension types
|
||||
|
||||
These are not supported at the moment. Not that they're hard to implement (I
|
||||
think), I simply haven't looked into them yet. Open a bug report if you need
|
||||
any of these.
|
||||
|
||||
=back
|
||||
|
||||
I<TODO:> Methods to convert between the various formats.
|
||||
|
||||
I<TODO:> Methods to query type info.
|
||||
|
||||
I<TODO:> Custom per-type configuration.
|
||||
|
||||
=head2 Errors
|
||||
|
||||
|
|
|
|||
21
c/pgtypes.c
21
c/pgtypes.c
|
|
@ -486,6 +486,23 @@ SENDFN(uuid) {
|
|||
if (dig != 0x10 || bytes != 16) SERR("invalid UUID");
|
||||
}
|
||||
|
||||
/* Postgres uses 2000-01-01 as epoch, we stick with POSIX 1970-01-01 */
|
||||
#define UNIX_PG_EPOCH (10957*86400)
|
||||
|
||||
RECVFN(timestamp) {
|
||||
RLEN(8);
|
||||
IV ts = fu_frombeI(64, buf);
|
||||
return newSVnv(((double)ts / 1000000) + UNIX_PG_EPOCH);
|
||||
}
|
||||
|
||||
SENDFN(timestamp) {
|
||||
if (!looks_like_number(val)) SERR("expected a number");
|
||||
IV ts = (SvNV(val) - UNIX_PG_EPOCH) * 1000000;
|
||||
fustr_writebeI(64, out, ts);
|
||||
}
|
||||
|
||||
#undef UNIX_PG_EPOCH
|
||||
|
||||
#undef SIV
|
||||
#undef RLEN
|
||||
#undef RECVFN
|
||||
|
|
@ -590,11 +607,11 @@ SENDFN(uuid) {
|
|||
B( 1043, "varchar", text )\
|
||||
/* 1082 date */\
|
||||
/* 1083 time */\
|
||||
B( 1114, "timestamp", timestamp)\
|
||||
A( 1115, "_timestamp", 1114 )\
|
||||
/* 1114 timestamp */\
|
||||
A( 1182, "_date", 1082 )\
|
||||
A( 1183, "_time", 1083 )\
|
||||
/* 1184 timestamptz */\
|
||||
B( 1184, "timestamptz", timestamp)\
|
||||
A( 1185, "_timestamptz", 1184 )\
|
||||
/* 1186 interval */\
|
||||
A( 1187, "_interval", 1186 )\
|
||||
|
|
|
|||
|
|
@ -107,6 +107,12 @@ f uuid => 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a111';
|
|||
f uuid => 'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a1';
|
||||
f uuid => 'a0egbc99-9c0b-4ef8-bb6d-6bb9bd380a11';
|
||||
|
||||
$conn->exec('SET timezone=utc');
|
||||
v timestamptz => 0, undef, '1970-01-01 00:00:00+00';
|
||||
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 => 1740133814.705915, undef, '2025-02-21 10:30:14.705915';
|
||||
|
||||
v 'int[]', [], undef, '{}';
|
||||
v 'int[]', [1], undef, '{1}';
|
||||
v 'int[]', [1,-3,undef,3], undef, '{1,-3,NULL,3}';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue