pg: Add transaction & subtransaction support
Was expecting the implementation of this to get overly complicated and brittle, but using a counter-based cookie and doing parts of it in Perl made it pretty easy actually. Pretty happy with how this turned out so far. TODO: documentation -.-
This commit is contained in:
parent
9d5905e3b4
commit
171afc0268
5 changed files with 276 additions and 10 deletions
50
FU/PG.pm
50
FU/PG.pm
|
|
@ -6,8 +6,58 @@ _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 'croak';
|
||||
|
||||
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) {
|
||||
croak "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) {
|
||||
croak "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) {
|
||||
croak "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) {
|
||||
$t->rollback if $t->[1];
|
||||
$t->[0]->_set_cookie($t->[2] ? $t->[2][1] : 0);
|
||||
}
|
||||
}
|
||||
|
||||
package FU::PG::error {
|
||||
use overload '""' => sub($e, @) { $e->{full_message} };
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue