On Fri Dec 10 12:51:12 2010, greg@turnstep.com wrote:
Show quoted text> > Any idea if this will make it into the 2.18 release?
>
> I've not had time to look at it close, but I don't see
> any reason why it would not.
Hi all,
This is just another bump to help get this patch committed, especially
if we're planning to go to another major revision soon.
I've attached a fresh patch since there have been a number of changes
over the two months since I submitted it.
--
Eric Simon
Index: dbdimp.c
===================================================================
--- dbdimp.c (revision 14700)
+++ dbdimp.c (working copy)
@@ -4953,7 +4953,40 @@
} /* 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:
typedef enum
Index: dbdimp.h
===================================================================
--- dbdimp.h (revision 14700)
+++ dbdimp.h (working copy)
@@ -169,9 +169,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);
Index: Pg.xs
===================================================================
--- Pg.xs (revision 14700)
+++ Pg.xs (working copy)
@@ -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
Index: t/03smethod.t
===================================================================
--- t/03smethod.t (revision 14700)
+++ t/03smethod.t (working copy)
@@ -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 becaue 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();