Here's my present idea how a synchronous cancel operation should really
look like.

Rationale: The result of the query which was supposed to be cancelled
can really be anything as it might have been complete by the time the
cancel request was received. The pg_db_result function goes to great
lengths to handle all kinds of results a query can possibly
have. Instead of partially duplicating this, the cancel code should just
invoke it to handle the query result, with pg_db_result changed such
that it no longer treats cancellation as an error but just returns "0
rows" instead.

NB: This is incomplete. In particular, modifications to the t/08async.t
test script are required for the tests to pass.

diff --git a/dbdimp.c b/dbdimp.c
index 1c023db..f7a7225 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -5333,6 +5333,11 @@ long pg_db_result (SV *h, imp_dbh_t *imp_dbh)
             pg_error(aTHX_ h, status, PQerrorMessage(imp_dbh->conn));
             break;
         case PGRES_FATAL_ERROR:
+            if (0 == strncmp(imp_dbh->sqlstate, "57014", 5)) {
+                rows = 0;
+                break;
+            }
+
         default:
             rows = -2;
             TRACE_PQERRORMESSAGE;
@@ -5431,9 +5436,6 @@ int pg_db_cancel(SV *h, imp_dbh_t *imp_dbh)
 {
     dTHX;
     PGcancel *cancel;
-    char errbuf[256];
-    PGresult *result;
-    ExecStatusType status;
 
     if (TSTART_slow) TRC(DBILOGFP, "%sBegin pg_db_cancel (async status: %d)\n",
                     THEADER_slow, imp_dbh->async_status);
@@ -5468,41 +5470,10 @@ int pg_db_cancel(SV *h, imp_dbh_t *imp_dbh)
     TRACE_PQFREECANCEL;
     PQfreeCancel(cancel);
 
-    /* Whatever else happens, we should no longer be inside of an async query 
*/
-    imp_dbh->async_status = -1;
-    if (NULL != imp_dbh->async_sth)
-        imp_dbh->async_sth->async_status = -1;
+    pg_db_result(h, imp_dbh);
 
-    /* Read in the result - assume only one */
-    TRACE_PQGETRESULT;
-    result = PQgetResult(imp_dbh->conn);
-    status = _sqlstate(aTHX_ imp_dbh, result);
-    if (!result) {
-        pg_error(aTHX_ h, PGRES_FATAL_ERROR, "Failed to get a result after 
PQcancel");
-        if (TEND_slow) TRC(DBILOGFP, "%sEnd pg_db_cancel (error: no 
result)\n", THEADER_slow);
-        return DBDPG_FALSE;
-    }
-
-    TRACE_PQCLEAR;
-    PQclear(result);
-
-    /* If we actually cancelled a running query, just return true - the caller 
must rollback if needed */
-    if (0 == strncmp(imp_dbh->sqlstate, "57014", 5)) {
-        if (TEND_slow) TRC(DBILOGFP, "%sEnd pg_db_cancel\n", THEADER_slow);
-        return DBDPG_TRUE;
-    }
-
-    /* If we got any other error, make sure we report it */
-    if (0 != strncmp(imp_dbh->sqlstate, "00000", 5)) {
-        if (TRACEWARN_slow) TRC(DBILOGFP,
-                           "%sQuery was not cancelled: was already 
finished\n", THEADER_slow);
-        TRACE_PQERRORMESSAGE;
-        pg_error(aTHX_ h, status, PQerrorMessage(imp_dbh->conn));
-        if (TEND_slow) TRC(DBILOGFP, "%sEnd pg_db_cancel (error)\n", 
THEADER_slow);
-    }
-    else if (TEND_slow) TRC(DBILOGFP, "%sEnd pg_db_cancel\n", THEADER_slow);
-    return DBDPG_FALSE;
-                    
+    if (TEND_slow) TRC(DBILOGFP, "%sEnd pg_db_cancel\n", THEADER_slow);
+    return 0 == strncmp(imp_dbh->sqlstate, "57014", 5);
 } /* end of pg_db_cancel */

Reply via email to