pg: ->disconnect() and docs
This commit is contained in:
parent
171afc0268
commit
96aee880ce
3 changed files with 152 additions and 3 deletions
4
FU.xs
4
FU.xs
|
|
@ -117,6 +117,10 @@ void q(fupg_conn *c, SV *sv, ...)
|
||||||
FUPG_CONN_COOKIE;
|
FUPG_CONN_COOKIE;
|
||||||
ST(0) = fupg_q(aTHX_ c, SvPVutf8_nolen(sv), ax, items);
|
ST(0) = fupg_q(aTHX_ c, SvPVutf8_nolen(sv), ax, items);
|
||||||
|
|
||||||
|
void disconnect(fupg_conn *c)
|
||||||
|
CODE:
|
||||||
|
fupg_disconnect(c);
|
||||||
|
|
||||||
void DESTROY(fupg_conn *c)
|
void DESTROY(fupg_conn *c)
|
||||||
CODE:
|
CODE:
|
||||||
fupg_destroy(c);
|
fupg_destroy(c);
|
||||||
|
|
|
||||||
144
FU/PG.pm
144
FU/PG.pm
|
|
@ -53,7 +53,10 @@ package FU::PG::txn {
|
||||||
}
|
}
|
||||||
|
|
||||||
sub DESTROY($t) {
|
sub DESTROY($t) {
|
||||||
$t->rollback if $t->[1];
|
# 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);
|
$t->[0]->_set_cookie($t->[2] ? $t->[2][1] : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -127,6 +130,43 @@ C<$major * 10000 + $minor>. For example, returns 170002 for PostgreSQL 17.2.
|
||||||
Returns the libpq version in the same format as the C<server_version> method.
|
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 >>
|
||||||
|
|
||||||
|
Returns a string indicating the status of the connection. Note that this method
|
||||||
|
does not verify that the connection is still alive, the status is updated after
|
||||||
|
each command. Possible return values:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item idle
|
||||||
|
|
||||||
|
Awaiting commands, not in a transaction.
|
||||||
|
|
||||||
|
=item txn_idle
|
||||||
|
|
||||||
|
Awaiting commands, inside a transaction.
|
||||||
|
|
||||||
|
=item txn_done
|
||||||
|
|
||||||
|
Idle, but a transaction object still exists. The connection is unusable until
|
||||||
|
that object goes out of scope.
|
||||||
|
|
||||||
|
=item txn_error
|
||||||
|
|
||||||
|
Inside a transaction that is in an error state. The transaction must be rolled
|
||||||
|
back in order to recover to a usable state. This happens automatically when the
|
||||||
|
transaction object goes out of scope.
|
||||||
|
|
||||||
|
=item bad
|
||||||
|
|
||||||
|
Connection is dead or otherwise unusable.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=item B<< $conn->disconnect >>
|
||||||
|
|
||||||
|
Close the connection. Any active transactions are rolled back and any further
|
||||||
|
attempts to use C<$conn> throw an error.
|
||||||
|
|
||||||
=back
|
=back
|
||||||
|
|
||||||
|
|
@ -226,7 +266,107 @@ multiple columns with the same name.
|
||||||
|
|
||||||
=head2 Transactions
|
=head2 Transactions
|
||||||
|
|
||||||
I<TODO>
|
This module provides a convenient and safe API for I<scoped transactions> and
|
||||||
|
I<subtransactions>. A new transaction can be started with C<< $conn->txn >>,
|
||||||
|
which returns an object that can be used to run commands inside the transaction
|
||||||
|
and control its fate. When the object goes out of scope, the transaction is
|
||||||
|
automatically rolled back if no explicit C<< $txn->commit >> has been
|
||||||
|
performed. Any attempts to run queries on the parent C<< $conn >> object will
|
||||||
|
fail while a transaction object is alive.
|
||||||
|
|
||||||
|
{
|
||||||
|
# start a new transaction
|
||||||
|
my $txn = $conn->txn;
|
||||||
|
|
||||||
|
# run queries
|
||||||
|
$txn->q('DELETE FROM books WHERE id = $1', 1)->exec;
|
||||||
|
|
||||||
|
# run commands in a subtransaction
|
||||||
|
{
|
||||||
|
my $subtxn = $txn->txn;
|
||||||
|
# ...
|
||||||
|
}
|
||||||
|
|
||||||
|
# commit
|
||||||
|
$txn->commit;
|
||||||
|
|
||||||
|
# If $txn->commit has not been called, the transaction will be rolled back
|
||||||
|
# automatically when it goes out of scope.
|
||||||
|
}
|
||||||
|
|
||||||
|
Transaction methods:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item B<< $txn->exec(..) >> and 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 >>
|
||||||
|
|
||||||
|
Commit or abort the transaction. Any attempts to run queries on this
|
||||||
|
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->txn >>
|
||||||
|
|
||||||
|
Create a subtransaction within the current transaction. A subtransaction works
|
||||||
|
exactly the same as a top-level transaction.
|
||||||
|
|
||||||
|
=item B<< $txn->status >>
|
||||||
|
|
||||||
|
Like C<< $conn->status >>, but with the following status codes:
|
||||||
|
|
||||||
|
=over
|
||||||
|
|
||||||
|
=item idle
|
||||||
|
|
||||||
|
Current transaction is active and awaiting commands.
|
||||||
|
|
||||||
|
=item done
|
||||||
|
|
||||||
|
Current transaction has either been committed or rolled back, further commands
|
||||||
|
will throw an error.
|
||||||
|
|
||||||
|
=item error
|
||||||
|
|
||||||
|
Current transaction is in error state and must be rolled back.
|
||||||
|
|
||||||
|
=item txn_idle
|
||||||
|
|
||||||
|
A subtransaction is active and awaiting commands. The current transaction is
|
||||||
|
not usable until the subtransaction goes out of scope.
|
||||||
|
|
||||||
|
(This status code is also returned when the subtransaction is 'done', the
|
||||||
|
current implementation does not track subtransactions that closely)
|
||||||
|
|
||||||
|
=item txn_error
|
||||||
|
|
||||||
|
A subtransaction is in error state and awaiting to be rolled back.
|
||||||
|
|
||||||
|
=item bad
|
||||||
|
|
||||||
|
Connection is dead or otherwise unusable.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
Of course, if you prefer the old-fashioned manual approach to transaction
|
||||||
|
handling, that is still available:
|
||||||
|
|
||||||
|
$conn->exec('BEGIN');
|
||||||
|
# We're now inside a transaction
|
||||||
|
$conn->exec('COMMIT') or $conn->exec('ROLLBACK');
|
||||||
|
|
||||||
|
Just don't try to use transaction objects and manual transaction commands at
|
||||||
|
the same time, that won't end well.
|
||||||
|
|
||||||
|
=back
|
||||||
|
|
||||||
|
|
||||||
=head2 Errors
|
=head2 Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,11 @@ static const char *fupg_status(fupg_conn *c) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void fupg_disconnect(fupg_conn *c) {
|
||||||
|
PQfinish(c->conn);
|
||||||
|
c->conn = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static void fupg_destroy(fupg_conn *c) {
|
static void fupg_destroy(fupg_conn *c) {
|
||||||
PQfinish(c->conn);
|
PQfinish(c->conn);
|
||||||
safefree(c);
|
safefree(c);
|
||||||
|
|
@ -160,8 +165,8 @@ typedef struct {
|
||||||
SV **bind;
|
SV **bind;
|
||||||
int bindn;
|
int bindn;
|
||||||
/* Set during prepare */
|
/* Set during prepare */
|
||||||
char name[32];
|
|
||||||
int prepared;
|
int prepared;
|
||||||
|
char name[32];
|
||||||
PGresult *describe;
|
PGresult *describe;
|
||||||
/* Set during execute */
|
/* Set during execute */
|
||||||
int paramn;
|
int paramn;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue