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();

Reply via email to