pg: Rework txn implementation + statement config API
I liked the Perl implementation of transactions, but managing state between Perl and C is a bit cumbersome, so I've moved the whole thing into C. Also added a few statement configuration methods that currently don't do anything yet.
This commit is contained in:
parent
8f94dd0921
commit
166744dd51
7 changed files with 335 additions and 186 deletions
121
FU/PG.pm
121
FU/PG.pm
|
|
@ -6,61 +6,8 @@ _load_libpq();
|
|||
|
||||
package FU::PG::conn {
|
||||
sub lib_version { FU::PG::lib_version() }
|
||||
|
||||
sub txn($c) {
|
||||
$c->exec('BEGIN');
|
||||
$c->_set_cookie(++$FU::PG::txn::COUNTER);
|
||||
bless [$c, $FU::PG::txn::COUNTER, undef], 'FU::PG::txn';
|
||||
}
|
||||
};
|
||||
|
||||
package FU::PG::txn {
|
||||
use Carp 'confess';
|
||||
|
||||
my $COUNTER = 0;
|
||||
|
||||
# Arrayref:
|
||||
# 0: $conn
|
||||
# 1: $cookie, a snapshot of $COUNTER that identifies this transaction, used
|
||||
# to match commands against transactions. Set to undef when this
|
||||
# transaction is 'done' but the object is still alive.
|
||||
# 2: $parent, undef if this is a top-level transaction.
|
||||
|
||||
sub commit($t) {
|
||||
confess "Unable to commit transaction that has already finished" if !$t->[1];
|
||||
$t->exec($t->[2] ? "RELEASE SAVEPOINT fupg_$t->[1]" : 'COMMIT');
|
||||
$t->[1] = undef;
|
||||
}
|
||||
|
||||
sub rollback($t) {
|
||||
confess "Unable to rollback transaction that has already finished" if !$t->[1];
|
||||
$t->exec($t->[2] ? "ROLLBACK TO SAVEPOINT fupg_$t->[1]" : 'ROLLBACK');
|
||||
$t->[1] = undef;
|
||||
}
|
||||
|
||||
sub txn($t) {
|
||||
confess "Unable to create sub-transaction when current transaction has already finished" if !$t->[1];
|
||||
$COUNTER++;
|
||||
$t->exec("SAVEPOINT fupg_$COUNTER");
|
||||
$t->[0]->_set_cookie($COUNTER);
|
||||
bless [$t->[0], $COUNTER, $t], 'FU::PG::txn';
|
||||
}
|
||||
|
||||
sub status($t) {
|
||||
my $cs = $t->[0]->status;
|
||||
return $cs if $cs eq 'bad' || ($t->[1] && $t->[0]->_get_cookie != $t->[1]);
|
||||
return $cs eq 'txn_error' ? 'error' : $t->[1] ? 'idle' : 'done';
|
||||
}
|
||||
|
||||
sub DESTROY($t) {
|
||||
# Can't really throw an error in DESTROY. If a rollback command fails,
|
||||
# we're sufficiently screwed that the only sensible recourse is to
|
||||
# disconnect and let any further operations throw an error.
|
||||
eval { $t->rollback; 1 } || $t->[0]->disconnect if $t->[1];
|
||||
$t->[0]->_set_cookie($t->[2] ? $t->[2][1] : 0);
|
||||
}
|
||||
}
|
||||
|
||||
package FU::PG::error {
|
||||
use overload '""' => sub($e, @) { $e->{full_message} };
|
||||
}
|
||||
|
|
@ -163,6 +110,16 @@ Connection is dead or otherwise unusable.
|
|||
|
||||
=back
|
||||
|
||||
=item B<< $conn->cache($enable) >>
|
||||
|
||||
=item B<< $conn->text_params($enable) >>
|
||||
|
||||
=item B<< $conn->text_results($enable) >>
|
||||
|
||||
=item B<< $conn->text($enable) >>
|
||||
|
||||
Set the default settings for new statements created with B<< $conn->q() >>.
|
||||
|
||||
=item B<< $conn->disconnect >>
|
||||
|
||||
Close the connection. Any active transactions are rolled back and any further
|
||||
|
|
@ -198,8 +155,30 @@ used.
|
|||
|
||||
=back
|
||||
|
||||
Statement objects returned by C<< $conn->q() >> can be inspected with the
|
||||
following two methods:
|
||||
Statement objects returned by C<< $conn->q() >> support the following
|
||||
configuration parameters:
|
||||
|
||||
=over
|
||||
|
||||
=item B<< $st->cache($enable) >>
|
||||
|
||||
Enable or disable caching of the prepared statement for this particular query.
|
||||
|
||||
=item B<< $st->text_params($enable) >>
|
||||
|
||||
Enable or disable sending bind parameters in the text format.
|
||||
|
||||
=item B<< $st->text_results($enable) >>
|
||||
|
||||
Enable or disable receiving query results in the text format.
|
||||
|
||||
=item B<< $st->text($enable) >>
|
||||
|
||||
Shorthand for setting C<text_params> and C<text_results> at the same time.
|
||||
|
||||
=back
|
||||
|
||||
Statement objects can be inspected with the following two methods:
|
||||
|
||||
=over
|
||||
|
||||
|
|
@ -264,6 +243,13 @@ multiple columns with the same name.
|
|||
|
||||
=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:
|
||||
|
||||
my @cols = $conn->q('SELECT a, b FROM table')->cache(0)->text->rowa;
|
||||
|
||||
|
||||
=head2 Transactions
|
||||
|
||||
This module provides a convenient and safe API for I<scoped transactions> and
|
||||
|
|
@ -298,12 +284,16 @@ Transaction methods:
|
|||
|
||||
=over
|
||||
|
||||
=item B<< $txn->exec(..) >> and B<< $txn->q(..) >>
|
||||
=item B<< $txn->exec(..) >>
|
||||
|
||||
=item B<< $txn->q(..) >>
|
||||
|
||||
Run a query inside the transaction. These work the same as the respective
|
||||
methods on the parent C<$conn> object.
|
||||
|
||||
=item B<< $txn->commit >> and B<< $txn->rollback >>
|
||||
=item B<< $txn->commit >>
|
||||
|
||||
=item B<< $txn->rollback >>
|
||||
|
||||
Commit or abort the transaction. Any attempts to run queries on this
|
||||
transaction object after this call will throw an error.
|
||||
|
|
@ -311,6 +301,21 @@ transaction object after this call will throw an error.
|
|||
Calling C<rollback> is optional, the transaction is automatically rolled back
|
||||
when the object goes out of scope.
|
||||
|
||||
=item B<< $txn->cache($enable) >>
|
||||
|
||||
=item B<< $txn->text_params($enable) >>
|
||||
|
||||
=item B<< $txn->text_results($enable) >>
|
||||
|
||||
=item B<< $txn->text($enable) >>
|
||||
|
||||
Set the default settings for new statements created with B<< $txn->q() >>.
|
||||
|
||||
These settings are inherited from the main connection when the transaction is
|
||||
created. Subtransactions inherit these settings from their parent transaction.
|
||||
Changing these settings within a transaction does not affect the main
|
||||
connection or any already existing subtransactions.
|
||||
|
||||
=item B<< $txn->txn >>
|
||||
|
||||
Create a subtransaction within the current transaction. A subtransaction works
|
||||
|
|
@ -386,7 +391,7 @@ your database encoding is UTF-8. Non-UTF-8 databases are still supported with
|
|||
the text format by setting `client_encoding=utf8` as part of the connection
|
||||
string or by manually switching to it after C<connect()>:
|
||||
|
||||
my $conn = FU::PG->connect("");
|
||||
my $conn = FU::PG->connect("")->text;
|
||||
$conn->exec('SET client_encoding=utf8');
|
||||
|
||||
(But you're missing out on most features this module has to offer if you're
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue