pg: Module rename + more docs
This commit is contained in:
parent
ccc2f1dbf0
commit
33fe0d98a8
7 changed files with 186 additions and 101 deletions
|
|
@ -1,14 +1,14 @@
|
|||
package FU::PG 0.1;
|
||||
package FU::Pg 0.1;
|
||||
use v5.36;
|
||||
use FU::XS;
|
||||
|
||||
_load_libpq();
|
||||
|
||||
package FU::PG::conn {
|
||||
sub lib_version { FU::PG::lib_version() }
|
||||
package FU::Pg::conn {
|
||||
sub lib_version { FU::Pg::lib_version() }
|
||||
};
|
||||
|
||||
package FU::PG::error {
|
||||
package FU::Pg::error {
|
||||
use overload '""' => sub($e, @) { $e->{full_message} };
|
||||
}
|
||||
|
||||
|
|
@ -17,15 +17,16 @@ __END__
|
|||
|
||||
=head1 NAME
|
||||
|
||||
FU::PG - Another PostgreSQL client module
|
||||
FU::Pg - The Ultimate (synchronous) Interface to PostgreSQL
|
||||
|
||||
=head1 SYNOPSYS
|
||||
|
||||
my $conn = FU::PG->connect("dbname=test user=test password=nottest");
|
||||
my $conn = FU::Pg->connect("dbname=test user=test password=nottest");
|
||||
|
||||
$conn->exec('CREATE TABLE books (id SERIAL, title text)');
|
||||
$conn->exec('CREATE TABLE books (id SERIAL, title text, read bool)');
|
||||
|
||||
$conn->q('INSERT INTO books (title) VALUES ($1)', 'Revelation Space')->exec;
|
||||
$conn->q('INSERT INTO books (title) VALUES ($1)', 'The Invincible')->exec;
|
||||
|
||||
for my ($id, $title) ($conn->q('SELECT * FROM books')->flat->@*) {
|
||||
print "$id: $title\n";
|
||||
|
|
@ -33,33 +34,17 @@ FU::PG - Another PostgreSQL client module
|
|||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
FU::PG is a PostgreSQL client module that (attempts) to set itself apart from
|
||||
the existing alternatives by offering the following features:
|
||||
|
||||
=over
|
||||
|
||||
=item * Automatic conversion of complex types (like JSON, hstore, records, etc)
|
||||
to and from convenient corresponding perl values.
|
||||
|
||||
=item * Support for custom types.
|
||||
|
||||
=item * Configurable Perl representation of timestamp values (or, well, really
|
||||
for any type).
|
||||
|
||||
=item * Data is transfered in the binary format (which may or may not be more
|
||||
efficient, need benchmarks).
|
||||
|
||||
=item * Convenient and high-level API.
|
||||
|
||||
=back
|
||||
FU::Pg is a client module for PostgreSQL with a convenient high-level API and
|
||||
support for flexible and complex type conversions. This module interfaces
|
||||
directly with C<libpq>.
|
||||
|
||||
=head2 Connection setup
|
||||
|
||||
=over
|
||||
|
||||
=item B<< FU::PG->connect($string) >>
|
||||
=item B<< FU::Pg->connect($string) >>
|
||||
|
||||
Connect to the PostgreSQL server and return a new C<FU::PG::conn> object.
|
||||
Connect to the PostgreSQL server and return a new C<FU::Pg::conn> object.
|
||||
C<$string> can either be in key=value format or a URI, refer to L<the
|
||||
PostgreSQL
|
||||
documentation|https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING>
|
||||
|
|
@ -75,7 +60,7 @@ C<$major * 10000 + $minor>. For example, returns 170002 for PostgreSQL 17.2.
|
|||
=item B<< $conn->lib_version >>
|
||||
|
||||
Returns the libpq version in the same format as the C<server_version> method.
|
||||
Also available directly as C<FU::PG::lib_version()>.
|
||||
Also available directly as C<FU::Pg::lib_version()>.
|
||||
|
||||
=item B<< $conn->status >>
|
||||
|
||||
|
|
@ -135,7 +120,7 @@ attempts to use C<$conn> throw an error.
|
|||
|
||||
Execute one or more SQL commands, separated by a semicolon. Returns the number
|
||||
of rows affected by the last statement or I<undef> if that information is not
|
||||
available for the given command (like `CREATE TABLE`).
|
||||
available for the given command (like with C<CREATE TABLE>).
|
||||
|
||||
=item B<< $conn->q($sql, @params) >>
|
||||
|
||||
|
|
@ -182,19 +167,16 @@ Statement objects can be inspected with the following two methods:
|
|||
|
||||
=over
|
||||
|
||||
=item B<< $st->params >>
|
||||
=item B<< $st->param_types >>
|
||||
|
||||
Returns an arrayref of hashrefs describing each parameter in the given C<$sql>
|
||||
string. Each parameter only has a single key for now: C<oid>, indicating the
|
||||
type Oid. Example:
|
||||
Returns an arrayref of integers indicating the type (as I<oid>) of each
|
||||
parameter in the given C<$sql> string. Example:
|
||||
|
||||
my $params = $conn->q('SELECT id FROM books WHERE id = $1')->params;
|
||||
# $params = [ { oid => 23 } ]
|
||||
my $oids = $conn->q('SELECT id FROM books WHERE id = $1 AND title = $2')->param_types;
|
||||
# $oids = [23,25]
|
||||
|
||||
my $params = $conn->q('SELECT id FROM books')->params;
|
||||
# $params = []
|
||||
|
||||
I<TODO: Resolve the oid to a more human-readable type>
|
||||
my $oids = $conn->q('SELECT id FROM books')->params;
|
||||
# $oids = []
|
||||
|
||||
=item B<< $st->columns >>
|
||||
|
||||
|
|
@ -220,34 +202,114 @@ how you'd like to obtain the results:
|
|||
Execute the query and return the number of rows affected. Similar to C<<
|
||||
$conn->exec >>.
|
||||
|
||||
my $v = $conn->q('UPDATE books SET read = true WHERE id = 1')->exec;
|
||||
# $v = 1
|
||||
|
||||
=item B<< $st->val >>
|
||||
|
||||
Return the first column of the first row. Throws an error if the query does not
|
||||
return exactly one column, or if multiple rows are returned. Returns I<undef>
|
||||
if no rows are returned or if its value is I<NULL>.
|
||||
|
||||
my $v = $conn->q('SELECT COUNT(*) FROM books')->val;
|
||||
# $v = 2
|
||||
|
||||
=item B<< $st->rowl >>
|
||||
|
||||
Return the first row as a list. Throws an error if the query does not return
|
||||
exactly one row.
|
||||
|
||||
my($id, $title) = $conn->q('SELECT id, title FROM books LIMIT 1')->rowl;
|
||||
# ($id, $title) = (1, 'Revelation Space');
|
||||
|
||||
=item B<< $st->rowa >>
|
||||
|
||||
Return the first row as an arrayref, equivalent to C<< [$st->rowl] >> but
|
||||
probably slightly more efficient.
|
||||
might be slightly more efficient.
|
||||
|
||||
my $row = $conn->q('SELECT id, title FROM books LIMIT 1')->rowa;
|
||||
# $row = [1, 'Revelation Space'];
|
||||
|
||||
=item B<< $st->rowh >>
|
||||
|
||||
Return the first row as a hashref. Also throws an error if the query returns
|
||||
multiple columns with the same name.
|
||||
|
||||
my $row = $conn->q('SELECT id, title FROM books LIMIT 1')->rowh;
|
||||
# $row = { id => 1, title => 'Revelation Space' };
|
||||
|
||||
=item B<< $st->alla >>
|
||||
|
||||
Return all rows as an arrayref of arrayrefs.
|
||||
|
||||
my $data = $conn->q('SELECT id, title FROM books')->alla;
|
||||
# $data = [
|
||||
# [ 1, 'Revelation Space' ],
|
||||
# [ 2, 'The Invincible' ],
|
||||
# ];
|
||||
|
||||
=item B<< $st->allh >>
|
||||
|
||||
Return all rows as an arrayref of hashrefs. Throws an error if the query
|
||||
returns multiple columns with the same name.
|
||||
|
||||
my $data = $conn->q('SELECT id, title FROM books')->allh;
|
||||
# $data = [
|
||||
# { id => 1, title => 'Revelation Space' },
|
||||
# { id => 2, title => 'The Invincible' },
|
||||
# ];
|
||||
|
||||
=item B<< $st->flat >>
|
||||
|
||||
Return an arrayref with all rows flattened.
|
||||
|
||||
my $data = $conn->q('SELECT id, title FROM books')->flat;
|
||||
# $data = [
|
||||
# 1, 'Revelation Space',
|
||||
# 2, 'The Invincible',
|
||||
# ];
|
||||
|
||||
=item B<< $st->kvv >>
|
||||
|
||||
Return a hashref where the first result column is used as key and the second
|
||||
column as value. If the query only returns a single column, C<true> is used as
|
||||
value instead. An error is thrown if the query returns 3 or more columns.
|
||||
|
||||
my $data = $conn->q('SELECT id, title FROM books')->kvv;
|
||||
# $data = {
|
||||
# 1 => 'Revelation Space',
|
||||
# 2 => 'The Invincible',
|
||||
# };
|
||||
|
||||
=item B<< $st->kva >>
|
||||
|
||||
Return a hashref where the first result column is used as key and the remaining
|
||||
columns are stored as arrayref.
|
||||
|
||||
my $data = $conn->q('SELECT id, title, read FROM books')->kva;
|
||||
# $data = {
|
||||
# 1 => [ 'Revelation Space', true ],
|
||||
# 2 => [ 'The Invincible', false ],
|
||||
# };
|
||||
|
||||
=item B<< $st->kvh >>
|
||||
|
||||
Return a hashref where the first result column is used as key and the remaining
|
||||
columns are stored as hashref.
|
||||
|
||||
my $data = $conn->q('SELECT id, title, read FROM books')->kvh;
|
||||
# $data = {
|
||||
# 1 => { title => 'Revelation Space', read => true },
|
||||
# 2 => { title => 'The Invincible', read => false },
|
||||
# };
|
||||
|
||||
=back
|
||||
|
||||
The only time you actually need to assign a statement object to a variable is
|
||||
when you want to inspect C<params> or C<columns>, in all other cases you can
|
||||
chain the methods for more concise code. For example:
|
||||
when you want to inspect C<param_types> or C<columns>, in all other cases you
|
||||
can chain the methods for more concise code. For example:
|
||||
|
||||
my @cols = $conn->q('SELECT a, b FROM table')->cache(0)->text->rowa;
|
||||
my $data = $conn->q('SELECT a, b FROM table')->cache(0)->text->alla;
|
||||
|
||||
|
||||
=head2 Transactions
|
||||
|
|
@ -319,7 +381,8 @@ connection or any already existing subtransactions.
|
|||
=item B<< $txn->txn >>
|
||||
|
||||
Create a subtransaction within the current transaction. A subtransaction works
|
||||
exactly the same as a top-level transaction.
|
||||
exactly the same as a top-level transaction, except any changes remain
|
||||
invisible to other sessions until the top-level transaction has been committed.
|
||||
|
||||
=item B<< $txn->status >>
|
||||
|
||||
|
|
@ -371,6 +434,10 @@ Just don't try to use transaction objects and manual transaction commands at
|
|||
the same time, that won't end well.
|
||||
|
||||
|
||||
=head2 Formats and Types
|
||||
|
||||
I<TODO>
|
||||
|
||||
=head2 Errors
|
||||
|
||||
I<TODO>
|
||||
|
|
@ -391,7 +458,7 @@ default, but if this may not be the case in your situation, setting
|
|||
`client_encoding=utf8` as part of the connection string or manually switching
|
||||
to it after C<connect()> is always safe:
|
||||
|
||||
my $conn = FU::PG->connect('');
|
||||
my $conn = FU::Pg->connect('');
|
||||
$conn->exec('SET client_encoding=utf8');
|
||||
|
||||
=item * Only works with blocking (synchronous) calls, not very suitable for use
|
||||
|
|
@ -400,7 +467,30 @@ low-latency connection with the Postgres server.
|
|||
|
||||
=back
|
||||
|
||||
Missing features (for now): I<pretty much everything>.
|
||||
Missing features:
|
||||
|
||||
=over
|
||||
|
||||
=item COPY support
|
||||
|
||||
I hope to implement this someday.
|
||||
|
||||
=item LISTEN support
|
||||
|
||||
Would be nice to have, most likely doable without going full async.
|
||||
|
||||
=item Asynchronous calls
|
||||
|
||||
Probably won't happen. Perl's async story is slightly awkward in general, and
|
||||
fully supporting async operation might require a fundamental redesign of how
|
||||
this module works. It certainly won't I<simplify> the implementation.
|
||||
|
||||
=item Pipelining
|
||||
|
||||
I have some ideas for an API, but doubt I'll ever implement it. Suffers from
|
||||
the same awkwardness and complexity as asynchronous calls.
|
||||
|
||||
=back
|
||||
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
|
@ -414,13 +504,12 @@ than this module, but type conversions may leave things to be desired.
|
|||
|
||||
=item L<Pg::PQ>
|
||||
|
||||
A thin wrapper around libpq. Lacks many higher-level conveniences and does not
|
||||
support binary transfers (at the time of writing, but then again there's little
|
||||
benefit in dealing with the binary format in pure perl anyway).
|
||||
Thin wrapper around libpq. Lacks many higher-level conveniences and doesn't do
|
||||
any type conversions for you.
|
||||
|
||||
=item L<DBIx::Simple>
|
||||
|
||||
A popular DBI wrapper with some API conveniences. I may have taken some
|
||||
Popular DBI wrapper with some API conveniences. I may have taken some
|
||||
inspiration from it in the design of this module's API.
|
||||
|
||||
=back
|
||||
Loading…
Add table
Add a link
Reference in a new issue