summaryrefslogtreecommitdiff
path: root/src/interfaces/libpq/fe-connect.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/interfaces/libpq/fe-connect.c')
-rw-r--r--src/interfaces/libpq/fe-connect.c185
1 files changed, 135 insertions, 50 deletions
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 3da9caecc6d..f44e1f606db 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.288 2004/10/29 19:30:02 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.289 2004/10/30 23:11:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2189,19 +2189,55 @@ PQresetPoll(PGconn *conn)
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)
+ free(cancel);
+}
+
/*
- * PQrequestCancel: attempt to request cancellation of the current operation.
+ * PQcancel and 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).
+ * dispatched, FALSE if not (in which case an error message is available).
* Note: successful dispatch is no guarantee that there will be any effect at
* the backend. The application must read the operation result as usual.
*
- * 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.
- *
* 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).
* This means we cannot use any C library routine that might be non-reentrant.
@@ -2210,59 +2246,40 @@ PQresetPoll(PGconn *conn)
* 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!
+ * internal_cancel() is an internal helper function to make code-sharing
+ * between the two versions of the cancel function possible.
*/
-
-int
-PQrequestCancel(PGconn *conn)
+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;
- /* 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.
+ * We need to open a temporary connection to the postmaster. Do
+ * this with only kernel calls.
*/
- if ((tmpsock = socket(conn->raddr.addr.ss_family, SOCK_STREAM, 0)) < 0)
+ if ((tmpsock = socket(raddr->addr.ss_family, SOCK_STREAM, 0)) < 0)
{
- strcpy(conn->errorMessage.data,
- "PQrequestCancel() -- socket() failed: ");
+ StrNCpy(errbuf, "PQcancel() -- socket() failed: ", errbufsize);
goto cancel_errReturn;
}
retry3:
- if (connect(tmpsock, (struct sockaddr *) & conn->raddr.addr,
- conn->raddr.salen) < 0)
+ if (connect(tmpsock, (struct sockaddr *) & raddr->addr,
+ raddr->salen) < 0)
{
if (SOCK_ERRNO == EINTR)
/* Interrupted system call - we'll just try again */
goto retry3;
- strcpy(conn->errorMessage.data,
- "PQrequestCancel() -- connect() failed: ");
+ StrNCpy(errbuf, "PQcancel() -- connect() failed: ", errbufsize);
goto cancel_errReturn;
}
@@ -2274,8 +2291,8 @@ retry3:
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);
+ 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,8 +2300,7 @@ retry4:
if (SOCK_ERRNO == EINTR)
/* Interrupted system call - we'll just try again */
goto retry4;
- strcpy(conn->errorMessage.data,
- "PQrequestCancel() -- send() failed: ");
+ StrNCpy(errbuf, "PQcancel() -- send() failed: ", errbufsize);
goto cancel_errReturn;
}
@@ -2316,21 +2332,90 @@ retry5:
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)
+ /*
+ * 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;
+ if (maxlen >= 0)
{
+ strncat(errbuf, SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)),
+ maxlen);
+ strcat(errbuf, "\n");
+ }
+ if (tmpsock >= 0)
closesocket(tmpsock);
#ifdef WIN32
- WSASetLastError(save_errno);
+ WSASetLastError(save_errno);
#else
- errno = save_errno;
+ errno = save_errno;
#endif
- }
+
return FALSE;
}
+/*
+ * PQcancel: request query cancel
+ *
+ * Returns TRUE if able to send the cancel request, FALSE if not.
+ *
+ * On failure, an error message is stored in *errbuf, which must be of size
+ * errbufsize (recommended size is 256 bytes). *errbuf is not changed on
+ * success return.
+ */
+int
+PQcancel(PGcancel *cancel, char *errbuf, int errbufsize)
+{
+ if (!cancel)
+ {
+ StrNCpy(errbuf, "PQcancel() -- no cancel object supplied", errbufsize);
+ return FALSE;
+ }
+
+ return internal_cancel(&cancel->raddr, cancel->be_pid, cancel->be_key,
+ errbuf, errbufsize);
+}
+
+/*
+ * PQrequestCancel: old, not thread-safe function for requesting query cancel
+ *
+ * Returns TRUE if able to send the cancel request, FALSE if not.
+ *
+ * On failure, the error message is saved in conn->errorMessage; this means
+ * that this can't be used when there might be other active operations on
+ * the connection object.
+ *
+ * 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!
+ */
+int
+PQrequestCancel(PGconn *conn)
+{
+ int r;
+
+ /* Check we have an open connection */
+ if (!conn)
+ return FALSE;
+
+ if (conn->sock < 0)
+ {
+ StrNCpy(conn->errorMessage.data,
+ "PQrequestCancel() -- connection is not open\n",
+ conn->errorMessage.maxlen);
+ conn->errorMessage.len = strlen(conn->errorMessage.data);
+
+ return FALSE;
+ }
+
+ 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.