[svn:DBD-Pg] r14706 - in DBD-Pg/trunk: . t

[email protected]
Newsgroups gmane.comp.db.postgresql.dbdpg.cvs
Message-ID <[email protected]>
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();
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.