mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
From: Tom Lane <tgl@sss.pgh.pa.us>
Making PQrequestCancel safe to call in a signal handler turned out to be
much easier than I feared. So here are the diffs.
Some notes:
* I modified the postmaster's packet "iodone" callback interface to allow
the callback routine to return a continue-or-drop-connection return
code; this was necessary to allow the connection to be closed after
receiving a Cancel, rather than proceeding to launch a new backend...
Being a neatnik, I also made the iodone proc have a typechecked
parameter list.
* I deleted all code I could find that had to do with OOB.
* I made some edits to ensure that all signals mentioned in the code
are referred to symbolically not by numbers ("SIGUSR2" not "2").
I think Bruce may have already done at least some of the same edits;
I hope that merging these patches is not too painful.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.72 1998/07/07 18:00:09 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.73 1998/07/09 03:29:07 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -499,13 +499,11 @@ connectDB(PGconn *conn)
|
||||
{
|
||||
PGresult *res;
|
||||
struct hostent *hp;
|
||||
|
||||
StartupPacket sp;
|
||||
AuthRequest areq;
|
||||
int laddrlen = sizeof(SockAddr);
|
||||
int portno,
|
||||
family,
|
||||
len;
|
||||
family;
|
||||
char beresp;
|
||||
int on = 1;
|
||||
|
||||
@@ -561,11 +559,11 @@ connectDB(PGconn *conn)
|
||||
(char *) hp->h_addr,
|
||||
hp->h_length);
|
||||
conn->raddr.in.sin_port = htons((unsigned short) (portno));
|
||||
len = sizeof(struct sockaddr_in);
|
||||
conn->raddr_len = sizeof(struct sockaddr_in);
|
||||
}
|
||||
#ifndef WIN32
|
||||
else
|
||||
len = UNIXSOCK_PATH(conn->raddr.un, portno);
|
||||
conn->raddr_len = UNIXSOCK_PATH(conn->raddr.un, portno);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -577,7 +575,7 @@ connectDB(PGconn *conn)
|
||||
errno, strerror(errno));
|
||||
goto connect_errReturn;
|
||||
}
|
||||
if (connect(conn->sock, &conn->raddr.sa, len) < 0)
|
||||
if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
|
||||
{
|
||||
(void) sprintf(conn->errorMessage,
|
||||
"connectDB() failed: Is the postmaster running and accepting%s connections at '%s' on port '%s'?\n",
|
||||
@@ -724,7 +722,7 @@ connectDB(PGconn *conn)
|
||||
* A ReadyForQuery message indicates that startup is successful,
|
||||
* but we might also get an Error message indicating failure.
|
||||
* (Notice messages indicating nonfatal warnings are also allowed
|
||||
* by the protocol.)
|
||||
* by the protocol, as is a BackendKeyData message.)
|
||||
* Easiest way to handle this is to let PQgetResult() read the messages.
|
||||
* We just have to fake it out about the state of the connection.
|
||||
*/
|
||||
@@ -994,6 +992,99 @@ PQreset(PGconn *conn)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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 errorMessage is set).
|
||||
* Note: successful dispatch is no guarantee that there will be any effect at
|
||||
* the backend. The application must read the operation result as usual.
|
||||
*
|
||||
* 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.
|
||||
* malloc/free are often non-reentrant, and anything that might call them is
|
||||
* just as dangerous. We avoid sprintf here for that reason. Building up
|
||||
* error messages with strcpy/strcat is tedious but should be quite safe.
|
||||
*/
|
||||
|
||||
int
|
||||
PQrequestCancel(PGconn *conn)
|
||||
{
|
||||
int tmpsock = -1;
|
||||
struct {
|
||||
uint32 packetlen;
|
||||
CancelRequestPacket cp;
|
||||
} crp;
|
||||
|
||||
/* Check we have an open connection */
|
||||
if (!conn)
|
||||
return FALSE;
|
||||
|
||||
if (conn->sock < 0)
|
||||
{
|
||||
strcpy(conn->errorMessage,
|
||||
"PQrequestCancel() -- connection is not open\n");
|
||||
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.sa.sa_family, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
strcpy(conn->errorMessage, "PQrequestCancel() -- socket() failed: ");
|
||||
goto cancel_errReturn;
|
||||
}
|
||||
if (connect(tmpsock, &conn->raddr.sa, conn->raddr_len) < 0)
|
||||
{
|
||||
strcpy(conn->errorMessage, "PQrequestCancel() -- connect() failed: ");
|
||||
goto cancel_errReturn;
|
||||
}
|
||||
/*
|
||||
* We needn't set nonblocking I/O or NODELAY options here.
|
||||
*/
|
||||
|
||||
/* Create and send the cancel request packet. */
|
||||
|
||||
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);
|
||||
|
||||
if (send(tmpsock, (char*) &crp, sizeof(crp), 0) != (int) sizeof(crp))
|
||||
{
|
||||
strcpy(conn->errorMessage, "PQrequestCancel() -- send() failed: ");
|
||||
goto cancel_errReturn;
|
||||
}
|
||||
|
||||
/* Sent it, done */
|
||||
#ifdef WIN32
|
||||
closesocket(tmpsock);
|
||||
#else
|
||||
close(tmpsock);
|
||||
#endif
|
||||
|
||||
return TRUE;
|
||||
|
||||
cancel_errReturn:
|
||||
strcat(conn->errorMessage, strerror(errno));
|
||||
strcat(conn->errorMessage, "\n");
|
||||
if (tmpsock >= 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
closesocket(tmpsock);
|
||||
#else
|
||||
close(tmpsock);
|
||||
#endif
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PacketSend() -- send a single-packet message.
|
||||
* this is like PacketSend(), defined in backend/libpq/pqpacket.c
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.55 1998/07/03 04:24:13 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.56 1998/07/09 03:29:08 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -361,6 +361,16 @@ parseInput(PGconn *conn)
|
||||
PGRES_EMPTY_QUERY);
|
||||
conn->asyncStatus = PGASYNC_READY;
|
||||
break;
|
||||
case 'K': /* secret key data from the backend */
|
||||
/* This is expected only during backend startup,
|
||||
* but it's just as easy to handle it as part of the
|
||||
* main loop. Save the data and continue processing.
|
||||
*/
|
||||
if (pqGetInt(&(conn->be_pid), 4, conn))
|
||||
return;
|
||||
if (pqGetInt(&(conn->be_key), 4, conn))
|
||||
return;
|
||||
break;
|
||||
case 'N': /* notices from the backend */
|
||||
if (getNotice(conn))
|
||||
return;
|
||||
@@ -761,44 +771,6 @@ PQexec(PGconn *conn, const char *query)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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 errorMessage is set).
|
||||
* Note: successful dispatch is no guarantee that there will be any effect at
|
||||
* the backend. The application must read the operation result as usual.
|
||||
*/
|
||||
|
||||
int
|
||||
PQrequestCancel(PGconn *conn)
|
||||
{
|
||||
char msg[1];
|
||||
|
||||
if (!conn)
|
||||
return FALSE;
|
||||
|
||||
if (conn->sock < 0)
|
||||
{
|
||||
sprintf(conn->errorMessage,
|
||||
"PQrequestCancel() -- connection is not open\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
msg[0] = '\0';
|
||||
|
||||
if (send(conn->sock, msg, 1, MSG_OOB) < 0)
|
||||
{
|
||||
sprintf(conn->errorMessage,
|
||||
"PQrequestCancel() -- couldn't send OOB data: errno=%d\n%s\n",
|
||||
errno, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Attempt to read a Notice response message.
|
||||
* This is possible in several places, so we break it out as a subroutine.
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: libpq-fe.h,v 1.30 1998/06/16 07:29:49 momjian Exp $
|
||||
* $Id: libpq-fe.h,v 1.31 1998/07/09 03:29:09 scrappy Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -174,8 +174,11 @@ extern "C"
|
||||
int sock; /* Unix FD for socket, -1 if not connected */
|
||||
SockAddr laddr; /* Local address */
|
||||
SockAddr raddr; /* Remote address */
|
||||
int raddr_len; /* Length of remote address */
|
||||
|
||||
/* Miscellaneous stuff */
|
||||
int be_pid; /* PID of backend --- needed for cancels */
|
||||
int be_key; /* key of backend --- needed for cancels */
|
||||
char salt[2]; /* password salt received from backend */
|
||||
PGlobjfuncs *lobjfuncs; /* private state for large-object access fns */
|
||||
|
||||
@@ -273,6 +276,8 @@ extern "C"
|
||||
#define PQsetdb(M_PGHOST,M_PGPORT,M_PGOPT,M_PGTTY,M_DBNAME) PQsetdbLogin(M_PGHOST, M_PGPORT, M_PGOPT, M_PGTTY, M_DBNAME, NULL, NULL)
|
||||
/* close the current connection and free the PGconn data structure */
|
||||
extern void PQfinish(PGconn *conn);
|
||||
/* issue a cancel request */
|
||||
extern int PQrequestCancel(PGconn *conn);
|
||||
|
||||
/*
|
||||
* close the current connection and restablish a new one with the same
|
||||
@@ -305,7 +310,6 @@ extern "C"
|
||||
/* Routines for managing an asychronous query */
|
||||
extern int PQisBusy(PGconn *conn);
|
||||
extern void PQconsumeInput(PGconn *conn);
|
||||
extern int PQrequestCancel(PGconn *conn);
|
||||
/* Routines for copy in/out */
|
||||
extern int PQgetline(PGconn *conn, char *string, int length);
|
||||
extern void PQputline(PGconn *conn, const char *string);
|
||||
|
||||
Reference in New Issue
Block a user