Index: exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.2
diff -c -r1.2 exports.txt
*** exports.txt	18 Oct 2004 22:00:42 -0000	1.2
--- exports.txt	30 Oct 2004 12:23:53 -0000
***************
*** 119,121 ****
--- 119,125 ----
  pqsignal                  117
  PQprepare                 118
  PQsendPrepare             119
+ PQgetCancel               120
+ PQfreeCancel              121
+ PQgetCancelError          122
+ PQcancel                  123
Index: fe-connect.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v
retrieving revision 1.288
diff -c -r1.288 fe-connect.c
*** fe-connect.c	29 Oct 2004 19:30:02 -0000	1.288
--- fe-connect.c	30 Oct 2004 12:25:44 -0000
***************
*** 2189,2197 ****
  	return PGRES_POLLING_FAILED;
  }


  /*
!  * PQrequestCancel: attempt to request cancellation of the current operation.
   *
   * The return value is TRUE if the cancel request was successfully
   * dispatched, FALSE if not (in which case conn->errorMessage is set).
--- 2189,2257 ----
  	return PGRES_POLLING_FAILED;
  }

+ /*
+  * PQcancelGet: get a PGcancel structure corresponding to a connection.
+  *
+  * A copy is needed to be able to cancel a running query from a different
+  * thread. If the same structure is used all structure members would have
+  * to be individually locked (if the entire structure was locked, it would
+  * be impossible to cancel a synchronous query becuase the structure would
+  * have to stay locked for the duration of the query).
+  */
+ PGcancel *
+ PQgetCancel(PGconn *conn)
+ {
+ 	PGcancel *cancel;
+
+ 	if (!conn)
+ 		return NULL;
+
+ 	if (conn->sock < 0)
+ 		return NULL;
+
+ 	cancel = malloc(sizeof(PGcancel));
+ 	if (cancel == NULL)
+ 		return NULL;
+
+ 	memcpy(&cancel->raddr, &conn->raddr, sizeof(SockAddr));
+ 	cancel->be_pid = conn->be_pid;
+ 	cancel->be_key = conn->be_key;
+
+ 	initPQExpBuffer(&cancel->errorMessage);
+
+ 	return cancel;
+ }
+
+ /* PQfreeCancel: free a cancel structure */
+ void
+ PQfreeCancel(PGcancel *cancel)
+ {
+ 	if (!cancel)
+ 		return;
+
+ 	termPQExpBuffer(&cancel->errorMessage);
+
+ 	free(cancel);
+ }
+
+ /* PQgetCancelError: get the current error message for a cancel structure */
+ char *
+ PQgetCancelError(PGcancel *cancel)
+ {
+ 	if (!cancel)
+ 		return libpq_gettext("cancel pointer is NULL\n");
+
+ 	return cancel->errorMessage.data;
+ }

  /*
!  * PQcancel and PQrequestCancel: attempt to request cancellation of the
!  * current operation.
!  *
!  * PQrequestCancel is the backwards compatible version. This version is
!  * safe to call from signal handlers, but it is NOT safe to call from
!  * a different thread. PQcancel is safe both from a different thread and
!  * from a signal handler.
   *
   * The return value is TRUE if the cancel request was successfully
   * dispatched, FALSE if not (in which case conn->errorMessage is set).
***************
*** 2201,2206 ****
--- 2261,2268 ----
   * XXX it was a bad idea to have the error message returned in
   * conn->errorMessage, since it could overwrite a message already there.
   * Would be better to return it in a char array passed by the caller.
+  * This has been fixed in the PQcancel function, and only applies to
+  * PQrequestCancel.
   *
   * CAUTION: we want this routine to be safely callable from a signal handler
   * (for example, an application might want to call it in a SIGINT handler).
***************
*** 2210,2268 ****
   * error messages with strcpy/strcat is tedious but should be quite safe.
   * We also save/restore errno in case the signal handler support doesn't.
   *
!  * NOTE: this routine must not generate any error message longer than
!  * INITIAL_EXPBUFFER_SIZE (currently 256), since we dare not try to
   * expand conn->errorMessage!
   */

! int
! PQrequestCancel(PGconn *conn)
  {
  	int			save_errno = SOCK_ERRNO;
  	int			tmpsock = -1;
  	char		sebuf[256];
  	struct
  	{
  		uint32		packetlen;
  		CancelRequestPacket cp;
  	}			crp;

- 	/* Check we have an open connection */
- 	if (!conn)
- 		return FALSE;
-
- 	if (conn->sock < 0)
- 	{
- 		strcpy(conn->errorMessage.data,
- 			   "PQrequestCancel() -- connection is not open\n");
- 		conn->errorMessage.len = strlen(conn->errorMessage.data);
- #ifdef WIN32
- 		WSASetLastError(save_errno);
- #else
- 		errno = save_errno;
- #endif
- 		return FALSE;
- 	}
-
  	/*
! 	 * We need to open a temporary connection to the postmaster. Use the
! 	 * information saved by connectDB to do this with only kernel calls.
  	 */
! 	if ((tmpsock = socket(conn->raddr.addr.ss_family, SOCK_STREAM, 0)) < 0)
  	{
! 		strcpy(conn->errorMessage.data,
! 			   "PQrequestCancel() -- socket() failed: ");
  		goto cancel_errReturn;
  	}
  retry3:
! 	if (connect(tmpsock, (struct sockaddr *) & conn->raddr.addr,
! 				conn->raddr.salen) < 0)
  	{
  		if (SOCK_ERRNO == EINTR)
  			/* Interrupted system call - we'll just try again */
  			goto retry3;
! 		strcpy(conn->errorMessage.data,
! 			   "PQrequestCancel() -- connect() failed: ");
  		goto cancel_errReturn;
  	}

--- 2272,2317 ----
   * error messages with strcpy/strcat is tedious but should be quite safe.
   * We also save/restore errno in case the signal handler support doesn't.
   *
!  * NOTE: error messages will be cut off at the current size of the
!  * error message buffer, since we dare not try to
   * expand conn->errorMessage!
   */

! /* internal_cancel() is an internal helper function to make code-sharing
!  * between the two versions of the cancel function possible.
!  */
! static int
! internal_cancel(SockAddr *raddr, int be_pid, int be_key, char *errbuf, int errbufsize)
  {
  	int			save_errno = SOCK_ERRNO;
  	int			tmpsock = -1;
  	char		sebuf[256];
+ 	int         maxlen;
  	struct
  	{
  		uint32		packetlen;
  		CancelRequestPacket cp;
  	}			crp;

  	/*
! 	 * We need to open a temporary connection to the postmaster. Do
! 	 * this with only kernel calls.
  	 */
! 	if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) < 0)
  	{
! 		strncpy(errbuf,
! 				"PQrequestCancel() -- socket() failed: ", errbufsize);
  		goto cancel_errReturn;
  	}
  retry3:
! 	if (connect(tmpsock, (struct sockaddr *) & raddr->addr,
! 				raddr->salen) < 0)
  	{
  		if (SOCK_ERRNO == EINTR)
  			/* Interrupted system call - we'll just try again */
  			goto retry3;
! 		strncpy(errbuf,
! 				"PQrequestCancel() -- connect() failed: ", errbufsize);
  		goto cancel_errReturn;
  	}

***************
*** 2274,2281 ****

  	crp.packetlen = htonl((uint32) sizeof(crp));
  	crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
! 	crp.cp.backendPID = htonl(conn->be_pid);
! 	crp.cp.cancelAuthCode = htonl(conn->be_key);

  retry4:
  	if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
--- 2323,2330 ----

  	crp.packetlen = htonl((uint32) sizeof(crp));
  	crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE);
! 	crp.cp.backendPID = htonl(be_pid);
! 	crp.cp.cancelAuthCode = htonl(be_key);

  retry4:
  	if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp))
***************
*** 2283,2290 ****
  		if (SOCK_ERRNO == EINTR)
  			/* Interrupted system call - we'll just try again */
  			goto retry4;
! 		strcpy(conn->errorMessage.data,
! 			   "PQrequestCancel() -- send() failed: ");
  		goto cancel_errReturn;
  	}

--- 2332,2339 ----
  		if (SOCK_ERRNO == EINTR)
  			/* Interrupted system call - we'll just try again */
  			goto retry4;
! 		strncpy(errbuf,
! 				"PQrequestCancel() -- send() failed: ", errbufsize);
  		goto cancel_errReturn;
  	}

***************
*** 2316,2324 ****
  	return TRUE;

  cancel_errReturn:
! 	strcat(conn->errorMessage.data, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
! 	strcat(conn->errorMessage.data, "\n");
! 	conn->errorMessage.len = strlen(conn->errorMessage.data);
  	if (tmpsock >= 0)
  	{
  		closesocket(tmpsock);
--- 2365,2375 ----
  	return TRUE;

  cancel_errReturn:
! 	/* Make sure we don't overflow the error buffer. Leave space for
! 	 * the \n at the end, and for the terminating zero. */
! 	maxlen = errbufsize - strlen(errbuf) - 2;
! 	strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)), maxlen);
! 	strcat(errbuf, "\n");
  	if (tmpsock >= 0)
  	{
  		closesocket(tmpsock);
***************
*** 2332,2337 ****
--- 2383,2429 ----
  }
  
  
+ int
+ PQcancel(PGcancel *cancel)
+ {
+ 	int r;
+ 
+ 	if (!cancel)
+ 		return FALSE;
+ 
+ 	/* internal_cancel will save/restore errno for us */
+ 	r = internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key,
+ 						cancel->errorMessage.data, cancel->errorMessage.maxlen);
+ 
+ 	return r;
+ }
+ 
+ int
+ PQrequestCancel(PGconn *conn)
+ {
+ 	int r;
+
+ 	/* Check we have an open connection */
+ 	if (!conn)
+ 		return FALSE;
+
+ 	if (!conn->sock)
+ 	{
+ 		strcpy(conn->errorMessage.data,
+ 			   "PQrequestCancel() -- connection is not open\n");
+ 		conn->errorMessage.len = strlen(conn->errorMessage.data);
+
+ 		return FALSE;
+ 	}
+
+ 	/* internal_cancel will save/restore errno for us */
+ 	r = internal_cancel(&conn->raddr, conn->be_pid, conn->be_key,
+ 						conn->errorMessage.data, conn->errorMessage.maxlen);
+
+ 	return r;
+ }
+
+
  /*
   * pqPacketSend() -- convenience routine to send a message to server.
   *
Index: libpq-fe.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-fe.h,v
retrieving revision 1.112
diff -c -r1.112 libpq-fe.h
*** libpq-fe.h	18 Oct 2004 22:00:42 -0000	1.112
--- libpq-fe.h	30 Oct 2004 11:46:42 -0000
***************
*** 116,121 ****
--- 116,127 ----
   */
  typedef struct pg_result PGresult;

+ /* PGcancel encapsulates the information needed to cancel a running
+  * query on an existing connection.
+  * The contents of this struct are not supposed to be known to applications.
+  */
+ typedef struct pg_cancel PGcancel;
+
  /* PGnotify represents the occurrence of a NOTIFY message.
   * Ideally this would be an opaque typedef, but it's so simple that it's
   * unlikely to change.
***************
*** 234,240 ****
--- 240,258 ----
  /* Synchronous (blocking) */
  extern void PQreset(PGconn *conn);

+ /* request a cancel structure */
+ extern PGcancel *PQgetCancel(PGconn *conn);
+
+ /* free a cancel structure */
+ extern void PQfreeCancel(PGcancel *cancel);
+
+ /* get the error message from a failed cancel request */
+ extern char *PQgetCancelError(PGcancel *cancel);
+
  /* issue a cancel request */
+ extern int PQcancel(PGcancel *cancel);
+
+ /* backwards compatible version, not as thread safe */
  extern int	PQrequestCancel(PGconn *conn);

  /* Accessor functions for PGconn objects */
Index: libpq-int.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/libpq-int.h,v
retrieving revision 1.95
diff -c -r1.95 libpq-int.h
*** libpq-int.h	18 Oct 2004 22:00:42 -0000	1.95
--- libpq-int.h	30 Oct 2004 11:47:11 -0000
***************
*** 346,351 ****
--- 346,366 ----
  	PQExpBufferData workBuffer; /* expansible string */
  };

+ /* PGcancel stores all data necessary to cancel a connection. A copy of this
+  * data is required to safely cancel a connection running on a different
+  * thread.
+  */
+ struct pg_cancel
+ {
+ 	SockAddr	raddr;			/* Remote address */
+ 	int			be_pid;			/* PID of backend --- needed for cancels */
+ 	int			be_key;			/* key of backend --- needed for cancels */
+
+ 	/* Buffer for current error message */
+ 	PQExpBufferData errorMessage;		/* expansible string */
+ };
+
+
  /* String descriptions of the ExecStatusTypes.
   * direct use of this array is deprecated; call PQresStatus() instead.
   */
