Author: turnstep
Date: Thu Feb 10 20:58:52 2011
New Revision: 14706
Modified:
DBD-Pg/trunk/Pg.xs
DBD-Pg/trunk/dbdimp.c
DBD-Pg/trunk/dbdimp.h
DBD-Pg/trunk/t/03smethod.t
Log:
Add cancel() feature.
Patch by Eric Simon
Bug 63516
Modified: DBD-Pg/trunk/Pg.xs
==============================================================================
--- DBD-Pg/trunk/Pg.xs (original)
+++ DBD-Pg/trunk/Pg.xs Thu Feb 10 20:58:52 2011
@@ -796,6 +796,13 @@
D_imp_sth(sth);
ST(0) = pg_db_cancel_sth(sth, imp_sth) ? &PL_sv_yes : &PL_sv_no;
+void
+cancel(sth)
+ SV *sth
+ CODE:
+ D_imp_sth(sth);
+ ST(0) = dbd_st_cancel(sth, imp_sth) ? &PL_sv_yes : &PL_sv_no;
+
#if PGLIBVERSION >= 80000
void
Modified: DBD-Pg/trunk/dbdimp.c
==============================================================================
--- DBD-Pg/trunk/dbdimp.c (original)
+++ DBD-Pg/trunk/dbdimp.c Thu Feb 10 20:58:52 2011
@@ -4923,6 +4923,39 @@
} /* end of handle_old_async */
+/* ================================================================== */
+/* Attempt to cancel a synchronous query
+ Returns true if the cancel succeeded, and false if it did not */
+int dbd_st_cancel(SV *sth, imp_sth_t *imp_sth)
+{
+ dTHX;
+ D_imp_dbh_from_sth;
+ PGcancel *cancel;
+ char errbuf[256];
+
+ if (TSTART) TRC(DBILOGFP, "%sBegin dbd_st_cancel\n", THEADER);
+
+ /* Get the cancel structure */
+ TRACE_PQGETCANCEL;
+ cancel = PQgetCancel(imp_dbh->conn);
+
+ /* This almost always works. If not, free our structure and complain
loudly */
+ TRACE_PQGETCANCEL;
+ if (!PQcancel(cancel, errbuf, sizeof(errbuf))) {
+ TRACE_PQFREECANCEL;
+ PQfreeCancel(cancel);
+ if (TRACEWARN) TRC(DBILOGFP, "%sPQcancel failed: %s\n",
THEADER, errbuf);
+ pg_error(aTHX_ sth, PGRES_FATAL_ERROR, "PQcancel failed");
+ if (TEND) TRC(DBILOGFP, "%sEnd dbd_st_cancel (error: cancel
failed)\n", THEADER);
+ return DBDPG_FALSE;
+ }
+ TRACE_PQFREECANCEL;
+ PQfreeCancel(cancel);
+
+ if (TEND) TRC(DBILOGFP, "%sEnd dbd_st_cancel\n", THEADER);
+ return DBDPG_TRUE;
+
+} /* end of dbd_st_cancel */
/*
Some information to keep you sane:
Modified: DBD-Pg/trunk/dbdimp.h
==============================================================================
--- DBD-Pg/trunk/dbdimp.h (original)
+++ DBD-Pg/trunk/dbdimp.h Thu Feb 10 20:58:52 2011
@@ -166,9 +166,12 @@
#define dbd_st_rows pg_st_rows
int dbd_st_rows (SV * sth, imp_sth_t * imp_sth);
-#define dbd_st_finish pg_st_finidh
+#define dbd_st_finish pg_st_finish
int dbd_st_finish (SV * sth, imp_sth_t * imp_sth);
+#define dbd_st_cancel pg_st_cancel
+int dbd_st_cancel (SV * sth, imp_sth_t * imp_sth);
+
#define dbd_st_destroy pg_st_destroy
void dbd_st_destroy (SV * sth, imp_sth_t * imp_sth);
Modified: DBD-Pg/trunk/t/03smethod.t
==============================================================================
--- DBD-Pg/trunk/t/03smethod.t (original)
+++ DBD-Pg/trunk/t/03smethod.t Thu Feb 10 20:58:52 2011
@@ -9,6 +9,7 @@
use 5.006;
use strict;
use warnings;
+use POSIX qw(:signal_h);
use Test::More;
use DBI ':sql_types';
use lib 't','.';
@@ -20,7 +21,7 @@
if (! $dbh) {
plan skip_all => 'Connection to database failed, cannot continue
testing';
}
-plan tests => 96;
+plan tests => 97;
isnt ($dbh, undef, 'Connect to database for statement handle method testing');
@@ -652,6 +653,36 @@
$sth->fetchall_arrayref();
is ($sth->{pg_current_row}, 0, $t);
+#
+# Test of the statement handle method "cancel"
+#
+
+$dbh->do('INSERT INTO dbd_pg_test (id) VALUES (?)',undef,1);
+$dbh->commit;
+$dbh->do('SELECT * FROM dbd_pg_test WHERE id = ? FOR UPDATE',undef,1);
+
+my $dbh2 = $dbh->clone;
+$dbh2->do('SET search_path TO ' . $dbh->selectrow_array('SHOW search_path'));
+
+my $oldaction;
+eval {
+ # This statement will block indefinitely because of the 'FOR UPDATE'
clause,
+ # so we set up an alarm to cancel it after 2 seconds.
+ my $sth = $dbh2->prepare('SELECT * FROM dbd_pg_test WHERE id = ? FOR
UPDATE');
+ $sth->{RaiseError} = 1;
+
+ my $action = POSIX::SigAction->new(sub
{$sth->cancel},POSIX::SigSet->new(SIGALRM));
+ $oldaction = POSIX::SigAction->new;
+ POSIX::sigaction(SIGALRM,$action,$oldaction);
+
+ alarm(2); # seconds before alarm
+ $sth->execute(1);
+ alarm(0); # cancel alarm (if execute didn't block)
+};
+POSIX::sigaction(SIGALRM,$oldaction); # restore original signal handler
+like ($@,qr/canceling statement due to user request/,'cancel');
+$dbh2->disconnect();
+
cleanup_database($dbh,'test');
$dbh->rollback();
$dbh->disconnect();