Index: bin/psql/common.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/common.c,v
retrieving revision 1.92
diff -c -r1.92 common.c
*** bin/psql/common.c	10 Oct 2004 23:37:40 -0000	1.92
--- bin/psql/common.c	30 Oct 2004 19:45:45 -0000
***************
*** 223,245 ****
   *
   * Before we start a query, we enable a SIGINT signal catcher that sends a
   * cancel request to the backend. Note that sending the cancel directly from
!  * the signal handler is safe because PQrequestCancel() is written to make it
   * so. We use write() to print to stdout because it's better to use simple
   * facilities in a signal handler.
   */
! static PGconn *volatile cancelConn = NULL;

  volatile bool cancel_pressed = false;


- #ifndef WIN32

  #define write_stderr(String) write(fileno(stderr), String, strlen(String))

  void
  handle_sigint(SIGNAL_ARGS)
  {
  	int			save_errno = errno;

  	/* Don't muck around if prompting for a password. */
  	if (prompt_state)
--- 223,254 ----
   *
   * Before we start a query, we enable a SIGINT signal catcher that sends a
   * cancel request to the backend. Note that sending the cancel directly from
!  * the signal handler is safe because PQcancel() is written to make it
   * so. We use write() to print to stdout because it's better to use simple
   * facilities in a signal handler.
+  *
+  * On win32, the signal cancelling happens on a separate thread, because
+  * that's how SetConsoleCtrlHandler works. The PQcancel function is safe
+  * or this, unlike PQrequestCancel. However, a CRITICAL_SECTION is required
+  * to protect the PGcancel structure during all usage.
   */
! static PGcancel *cancelConn = NULL;
! #ifdef WIN32
! static CRITICAL_SECTION cancelConnLock;
! #endif

  volatile bool cancel_pressed = false;



  #define write_stderr(String) write(fileno(stderr), String, strlen(String))

+ #ifndef WIN32
  void
  handle_sigint(SIGNAL_ARGS)
  {
  	int			save_errno = errno;
+ 	char        errbuf[256];

  	/* Don't muck around if prompting for a password. */
  	if (prompt_state)
***************
*** 250,265 ****

  	cancel_pressed = true;

! 	if (PQrequestCancel(cancelConn))
  		write_stderr("Cancel request sent\n");
  	else
  	{
  		write_stderr("Could not send cancel request: ");
! 		write_stderr(PQerrorMessage(cancelConn));
  	}
  	errno = save_errno;			/* just in case the write changed it */
  }
! #endif   /* not WIN32 */



--- 259,315 ----

  	cancel_pressed = true;

! 	if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
  		write_stderr("Cancel request sent\n");
  	else
  	{
  		write_stderr("Could not send cancel request: ");
! 		write_stderr(errbuf);
  	}
  	errno = save_errno;			/* just in case the write changed it */
  }
! #else /* WIN32 */
!
! static BOOL WINAPI consoleHandler(DWORD dwCtrlType)
! {
! 	char        errbuf[256];
!
! 	if (dwCtrlType == CTRL_C_EVENT ||
! 		dwCtrlType == CTRL_BREAK_EVENT)
! 	{
! 		if (prompt_state)
! 			return TRUE;
!
! 		/* Perform query cancel */
! 		EnterCriticalSection(&cancelConnLock);
! 		if (cancelConn != NULL)
! 		{
! 			cancel_pressed = true;
!
! 			if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
! 				write_stderr("Cancel request sent\n");
! 			else
! 			{
! 				write_stderr("Could not send cancel request: ");
! 				write_stderr(errbuf);
! 			}
! 		}
! 		LeaveCriticalSection(&cancelConnLock);
!
! 		return TRUE;
! 	}
! 	else
! 		/* Return FALSE for any signals not being handled */
! 		return FALSE;
! }
!
! void
! setup_cancel_handler(void)
! {
! 	InitializeCriticalSection(&cancelConnLock);
! 	SetConsoleCtrlHandler(consoleHandler, TRUE);
! }
! #endif



***************
*** 327,346 ****
  static void
  SetCancelConn(void)
  {
! 	cancelConn = pset.db;
  }


  /*
   * ResetCancelConn
   *
!  * Set cancelConn to NULL.	I don't know what this means exactly, but it saves
!  * having to export the variable.
   */
  void
  ResetCancelConn(void)
  {
  	cancelConn = NULL;
  }


--- 377,414 ----
  static void
  SetCancelConn(void)
  {
! #ifdef WIN32
! 	EnterCriticalSection(&cancelConnLock);
! #endif
! 	/* Free the old one if we have one */
! 	if (cancelConn != NULL)
! 		PQfreeCancel(cancelConn);
!
! 	cancelConn = PQgetCancel(pset.db);
! #ifdef WIN32
! 	LeaveCriticalSection(&cancelConnLock);
! #endif
  }


  /*
   * ResetCancelConn
   *
!  * Free the current cancel connection, if any, and set to NULL.
   */
  void
  ResetCancelConn(void)
  {
+ #ifdef WIN32
+ 	EnterCriticalSection(&cancelConnLock);
+ #endif
+ 	if (cancelConn)
+ 		PQfreeCancel(cancelConn);
+
  	cancelConn = NULL;
+ #ifdef WIN32
+ 	LeaveCriticalSection(&cancelConnLock);
+ #endif
  }


Index: bin/psql/common.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/common.h,v
retrieving revision 1.39
diff -c -r1.39 common.h
*** bin/psql/common.h	29 Aug 2004 04:13:02 -0000	1.39
--- bin/psql/common.h	30 Oct 2004 12:34:40 -0000
***************
*** 48,54 ****

  #ifndef WIN32
  extern void handle_sigint(SIGNAL_ARGS);
! #endif   /* not WIN32 */

  extern PGresult *PSQLexec(const char *query, bool start_xact);

--- 48,56 ----

  #ifndef WIN32
  extern void handle_sigint(SIGNAL_ARGS);
! #else /* WIN32 */
! extern void setup_cancel_handler(void);
! #endif

  extern PGresult *PSQLexec(const char *query, bool start_xact);

Index: bin/psql/mainloop.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/bin/psql/mainloop.c,v
retrieving revision 1.64
diff -c -r1.64 mainloop.c
*** bin/psql/mainloop.c	29 Aug 2004 05:06:54 -0000	1.64
--- bin/psql/mainloop.c	30 Oct 2004 12:31:57 -0000
***************
*** 121,127 ****
  		 * ready
  		 */
  		pqsignal(SIGINT, handle_sigint);		/* control-C => cancel */
! #endif   /* not WIN32 */

  		fflush(stdout);

--- 121,129 ----
  		 * ready
  		 */
  		pqsignal(SIGINT, handle_sigint);		/* control-C => cancel */
! #else /* WIN32 */
! 		setup_cancel_handler(); /* in common.c */
! #endif

  		fflush(stdout);

Index: interfaces/libpq/exports.txt
===================================================================
RCS file: /projects/cvsroot/pgsql/src/interfaces/libpq/exports.txt,v
retrieving revision 1.2
diff -c -r1.2 exports.txt
*** interfaces/libpq/exports.txt	18 Oct 2004 22:00:42 -0000	1.2
--- interfaces/libpq/exports.txt	30 Oct 2004 19:39:18 -0000
***************
*** 119,121 ****
--- 119,124 ----
  pqsignal                  117
  PQprepare                 118
  PQsendPrepare             119
+ PQgetCancel               120
+ PQfreeCancel              121
+ PQcancel                  122
Index: interfaces/libpq/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
*** interfaces/libpq/fe-connect.c	29 Oct 2004 19:30:02 -0000	1.288
--- interfaces/libpq/fe-connect.c	30 Oct 2004 19:43:00 -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,2243 ----
  	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;
+
+ 	return cancel;
+ }
+
+ /* PQfreeCancel: free a cancel structure */
+ void
+ PQfreeCancel(PGcancel *cancel)
+ {
+ 	if (!cancel)
+ 		return;
+
+ 	free(cancel);
+ }

  /*
!  * 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 ****
--- 2247,2254 ----
   * 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;
  	}

--- 2258,2303 ----
   * 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))
--- 2309,2316 ----

  	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;
  	}

--- 2318,2325 ----
  		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);
--- 2351,2361 ----
  	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 ****
--- 2369,2417 ----
  }


+ int
+ PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
+ {
+ 	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,
+ 						errbuf, errbufsize);
+
+ 	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);
+ 	if (!r)
+ 		conn->errorMessage.len = strlen(conn->errorMessage.data);
+
+ 	return r;
+ }
+
+
  /*
   * pqPacketSend() -- convenience routine to send a message to server.
   *
Index: interfaces/libpq/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
*** interfaces/libpq/libpq-fe.h	18 Oct 2004 22:00:42 -0000	1.112
--- interfaces/libpq/libpq-fe.h	30 Oct 2004 19:39:10 -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,255 ----
  /* 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);
+
  /* issue a cancel request */
+ extern int PQcancel(PGcancel *cancel, char *errbuf, int errbufsize);
+
+ /* backwards compatible version, not as thread safe */
  extern int	PQrequestCancel(PGconn *conn);

  /* Accessor functions for PGconn objects */
Index: interfaces/libpq/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
*** interfaces/libpq/libpq-int.h	18 Oct 2004 22:00:42 -0000	1.95
--- interfaces/libpq/libpq-int.h	30 Oct 2004 19:38:26 -0000
***************
*** 346,351 ****
--- 346,363 ----
  	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 */
+ };
+
+
  /* String descriptions of the ExecStatusTypes.
   * direct use of this array is deprecated; call PQresStatus() instead.
   */
