diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index f40914583c..151ca3cb42 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -4,7 +4,7 @@
Phil
Thompson
-1998-05-04
+1998-07-07
Frontend/Backend Protocol
@@ -54,8 +54,10 @@ invalid database name).
Subsequent communications are query and result packets exchanged between the
-frontend and the backend. The postmaster takes no further part in the
-communication.
+frontend and the backend. The postmaster takes no further part in ordinary
+query/result communication. (However, the postmaster is involved when the
+frontend wishes to cancel a query currently being executed by its backend.
+Further details about that appear below.)
When the frontend wishes to disconnect it sends an appropriate packet and
@@ -182,6 +184,20 @@ The possible messages from the backend during this phase are:
+
+ BackendKeyData
+
+
+
+ This message is issued after successful backend startup.
+ It provides secret-key data that the frontend must save
+ if it wants to be able to issue cancel requests later.
+ The frontend should not respond to this message, but should
+ continue listening for a ReadyForQuery message.
+
+
+
+
ReadyForQuery
@@ -218,6 +234,14 @@ The possible messages from the backend during this phase are:
+
+The ReadyForQuery message is the same one that the backend will issue after
+each query cycle. Depending on the coding needs of the frontend, it is
+reasonable to consider ReadyForQuery as starting a query cycle (and then
+BackendKeyData indicates successful conclusion of the startup phase),
+or to consider ReadyForQuery as ending the startup phase and each subsequent
+query cycle.
+
Query
@@ -453,7 +477,7 @@ NotificationResponse messages at any time; see below.
If a frontend issues a listen(l) command, then the backend will send a
NotificationResponse message (not to be confused with NoticeResponse!)
-whenever a notify(l) command is executed for the same relation name.
+whenever a notify(l) command is executed for the same notification name.
Notification responses are permitted at any point in the protocol (after
@@ -470,8 +494,8 @@ NotificationResponse messages even when it is not engaged in a query.
- A notify(l) command has been executed for a relation for
- which a previous listen(l) command was executed. Notifications
+ A notify(l) command has been executed for a name for which
+ a previous listen(l) command was executed. Notifications
may be sent at any time.
@@ -479,29 +503,77 @@ NotificationResponse messages even when it is not engaged in a query.
+
+It may be worth pointing out that the names used in listen and notify
+commands need not have anything to do with names of relations (tables)
+in the SQL database. Notification names are simply arbitrarily chosen
+condition names.
+
Cancelling Requests in Progress
During the processing of a query, the frontend may request cancellation of the
-query by sending a single byte of OOB (out-of-band) data. The contents of the
-data byte should be zero (although the backend does not currently check this).
-If the cancellation is effective, it results in the current command being
-terminated with an error message. Note that the backend makes no specific
-reply to the cancel request itself. If the cancel request is ineffective
-(say, because it arrived after processing was complete) then it will have
-no visible effect at all. Thus, the frontend must continue with its normal
-processing of query cycle responses after issuing a cancel request.
+query by sending an appropriate request to the postmaster. The cancel request
+is not sent directly to the backend for reasons of implementation efficiency:
+we don't want to have the backend constantly checking for new input from
+the frontend during query processing. Cancel requests should be relatively
+infrequent, so we make them slightly cumbersome in order to avoid a penalty
+in the normal case.
+
+
+To issue a cancel request, the frontend opens a new connection to the
+postmaster and sends a CancelRequest message, rather than the StartupPacket
+message that would ordinarily be sent across a new connection. The postmaster
+will process this request and then close the connection. For security
+reasons, no direct reply is made to the cancel request message.
+
+
+A CancelRequest message will be ignored unless it contains the same key data
+(PID and secret key) passed to the frontend during connection startup. If the
+request matches the PID and secret key for a currently executing backend, the
+postmaster signals the backend to abort processing of the current query.
+
+
+The cancellation signal may or may not have any effect --- for example, if it
+arrives after the backend has finished processing the query, then it will have
+no effect. If the cancellation is effective, it results in the current
+command being terminated early with an error message.
+
+
+The upshot of all this is that for reasons of both security and efficiency,
+the frontend has no direct way to tell whether a cancel request has succeeded.
+It must continue to wait for the backend to respond to the query. Issuing a
+cancel simply improves the odds that the current query will finish soon,
+and improves the odds that it will fail with an error message instead of
+succeeding.
+
+
+Since the cancel request is sent to the postmaster and not across the
+regular frontend/backend communication link, it is possible for the cancel
+request to be issued by any process, not just the frontend whose query is
+to be canceled. This may have some benefits of flexibility in building
+multiple-process applications. It also introduces a security risk, in that
+unauthorized persons might try to cancel queries. The security risk is
+addressed by requiring a dynamically generated secret key to be supplied
+in cancel requests.
Termination
-The frontend sends a Terminate message and immediately closes the connection.
-On receipt of the message, the backend immediately closes the connection and
-terminates.
+The normal, graceful termination procedure is that the frontend sends a
+Terminate message and immediately closes the connection. On receipt of the
+message, the backend immediately closes the connection and terminates.
+
+
+An ungraceful termination may occur due to software failure (i.e., core dump)
+at either end. If either frontend or backend sees an unexpected closure of
+the connection, it should clean up and terminate. The frontend has the option
+of launching a new backend by recontacting the postmaster, if it doesn't want
+to terminate itself.
@@ -824,6 +896,52 @@ AuthenticationEncryptedPassword (B)
+
+
+
+
+
+BackendKeyData (B)
+
+
+
+
+
+
+
+ Byte1('K')
+
+
+
+ Identifies the message as cancellation key data.
+ The frontend must save these values if it wishes to be
+ able to issue CancelRequest messages later.
+
+
+
+
+
+ Int32
+
+
+
+ The process ID of this backend.
+
+
+
+
+
+ Int32
+
+
+
+ The secret key of this backend.
+
+
+
+
+
+
@@ -892,6 +1010,63 @@ BinaryRow (B)
+
+
+
+
+
+CancelRequest (F)
+
+
+
+
+
+
+
+ Int32(16)
+
+
+
+ The size of the packet in bytes.
+
+
+
+
+
+ Int32(80877102)
+
+
+
+ The cancel request code. The value is chosen to contain
+ "1234" in the most significant 16 bits, and "5678" in the
+ least 16 significant bits. (To avoid confusion, this code
+ must not be the same as any protocol version number.)
+
+
+
+
+
+ Int32
+
+
+
+ The process ID of the target backend.
+
+
+
+
+
+ Int32
+
+
+
+ The secret key for the target backend.
+
+
+
+
+
+
@@ -1092,31 +1267,6 @@ EncryptedPasswordPacket (F)
-
-
-
-
-
-ReadyForQuery (B)
-
-
-
-
-
-
-
- Byte1('Z')
-
-
-
- Identifies the message type. ReadyForQuery is sent
- whenever the backend is ready for a new query cycle.
-
-
-
-
-
-
@@ -1449,6 +1599,31 @@ Query (F)
+
+
+
+
+
+ReadyForQuery (B)
+
+
+
+
+
+
+
+ Byte1('Z')
+
+
+
+ Identifies the message type. ReadyForQuery is sent
+ whenever the backend is ready for a new query cycle.
+
+
+
+
+
+
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index b4b354cfc5..b80cbb8f34 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.34 1998/06/27 04:53:29 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/async.c,v 1.35 1998/07/09 03:28:44 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -21,11 +21,11 @@
* 2.a If the process is the same as the backend process that issued
* notification (we are notifying something that we are listening),
* signal the corresponding frontend over the comm channel.
- * 2.b For all other listening processes, we send kill(2) to wake up
+ * 2.b For all other listening processes, we send kill(SIGUSR2) to wake up
* the listening backend.
- * 3. Upon receiving a kill(2) signal from another backend process notifying
- * that one of the relation that we are listening is being notified,
- * we can be in either of two following states:
+ * 3. Upon receiving a kill(SIGUSR2) signal from another backend process
+ * notifying that one of the relation that we are listening is being
+ * notified, we can be in either of two following states:
* 3.a We are sleeping, wake up and signal our frontend.
* 3.b We are in middle of another transaction, wait until the end of
* of the current transaction and signal our frontend.
@@ -46,7 +46,7 @@
* (which takes place after commit) to all listeners on this relation.
*
* 3. Async. notification results in all backends listening on relation
- * to be woken up, by a process signal kill(2), with name of relation
+ * to be woken up, by a process signal kill(SIGUSR2), with name of relation
* passed in shared memory.
*
* 4. Each backend notifies its respective frontend over the comm
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 4aee9b9197..c1cc08f4c7 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.28 1998/06/13 04:27:14 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/auth.c,v 1.29 1998/07/09 03:28:45 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -40,13 +40,13 @@
#include
-static void sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ());
-static void handle_done_auth(Port *port);
-static void handle_krb4_auth(Port *port);
-static void handle_krb5_auth(Port *port);
-static void handle_password_auth(Port *port);
-static void readPasswordPacket(char *arg, PacketLen len, char *pkt);
-static void pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt);
+static void sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler);
+static int handle_done_auth(void *arg, PacketLen len, void *pkt);
+static int handle_krb4_auth(void *arg, PacketLen len, void *pkt);
+static int handle_krb5_auth(void *arg, PacketLen len, void *pkt);
+static int handle_password_auth(void *arg, PacketLen len, void *pkt);
+static int readPasswordPacket(void *arg, PacketLen len, void *pkt);
+static int pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt);
static int checkPassword(Port *port, char *user, char *password);
static int old_be_recvauth(Port *port);
static int map_old_to_new(Port *port, UserAuth old, int status);
@@ -327,8 +327,8 @@ pg_krb5_recvauth(Port *port)
* Handle a v0 password packet.
*/
-static void
-pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
+static int
+pg_passwordv0_recvauth(void *arg, PacketLen len, void *pkt)
{
Port *port;
PasswordPacketV0 *pp;
@@ -393,6 +393,8 @@ pg_passwordv0_recvauth(char *arg, PacketLen len, char *pkt)
if (map_old_to_new(port, uaPassword, status) != STATUS_OK)
auth_failed(port);
}
+
+ return (STATUS_OK); /* don't close the connection yet */
}
@@ -433,7 +435,7 @@ be_recvauth(Port *port)
else
{
AuthRequest areq;
- void (*auth_handler) ();
+ PacketDoneProc auth_handler;
/* Keep the compiler quiet. */
@@ -499,7 +501,7 @@ be_recvauth(Port *port)
*/
static void
-sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
+sendAuthRequest(Port *port, AuthRequest areq, PacketDoneProc handler)
{
char *dp,
*sp;
@@ -527,7 +529,7 @@ sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
i += 2;
}
- PacketSendSetup(&port->pktInfo, i, handler, (char *) port);
+ PacketSendSetup(&port->pktInfo, i, handler, (void *) port);
}
@@ -535,8 +537,8 @@ sendAuthRequest(Port *port, AuthRequest areq, void (*handler) ())
* Called when we have told the front end that it is authorised.
*/
-static void
-handle_done_auth(Port *port)
+static int
+handle_done_auth(void *arg, PacketLen len, void *pkt)
{
/*
@@ -544,7 +546,7 @@ handle_done_auth(Port *port)
* start.
*/
- return;
+ return STATUS_OK;
}
@@ -553,13 +555,17 @@ handle_done_auth(Port *port)
* authentication.
*/
-static void
-handle_krb4_auth(Port *port)
+static int
+handle_krb4_auth(void *arg, PacketLen len, void *pkt)
{
+ Port *port = (Port *) arg;
+
if (pg_krb4_recvauth(port) != STATUS_OK)
auth_failed(port);
else
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+ return STATUS_OK;
}
@@ -568,13 +574,17 @@ handle_krb4_auth(Port *port)
* authentication.
*/
-static void
-handle_krb5_auth(Port *port)
+static int
+handle_krb5_auth(void *arg, PacketLen len, void *pkt)
{
+ Port *port = (Port *) arg;
+
if (pg_krb5_recvauth(port) != STATUS_OK)
auth_failed(port);
else
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+ return STATUS_OK;
}
@@ -583,12 +593,16 @@ handle_krb5_auth(Port *port)
* authentication.
*/
-static void
-handle_password_auth(Port *port)
+static int
+handle_password_auth(void *arg, PacketLen len, void *pkt)
{
+ Port *port = (Port *) arg;
+
/* Set up the read of the password packet. */
- PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (char *) port);
+ PacketReceiveSetup(&port->pktInfo, readPasswordPacket, (void *) port);
+
+ return STATUS_OK;
}
@@ -596,13 +610,11 @@ handle_password_auth(Port *port)
* Called when we have received the password packet.
*/
-static void
-readPasswordPacket(char *arg, PacketLen len, char *pkt)
+static int
+readPasswordPacket(void *arg, PacketLen len, void *pkt)
{
char password[sizeof(PasswordPacket) + 1];
- Port *port;
-
- port = (Port *) arg;
+ Port *port = (Port *) arg;
/* Silently truncate a password that is too big. */
@@ -615,6 +627,8 @@ readPasswordPacket(char *arg, PacketLen len, char *pkt)
auth_failed(port);
else
sendAuthRequest(port, AUTH_REQ_OK, handle_done_auth);
+
+ return (STATUS_OK); /* don't close the connection yet */
}
@@ -662,7 +676,7 @@ old_be_recvauth(Port *port)
case STARTUP_PASSWORD_MSG:
PacketReceiveSetup(&port->pktInfo, pg_passwordv0_recvauth,
- (char *) port);
+ (void *) port);
return STATUS_OK;
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index a70bbc22e9..4c5b85b248 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.47 1998/06/27 04:53:30 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/pqcomm.c,v 1.48 1998/07/09 03:28:46 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -30,7 +30,6 @@
* pq_getinserv - initialize address from host and service name
* pq_connect - create remote input / output connection
* pq_accept - accept remote input / output connection
- * pq_async_notify - receive notification from backend.
*
* NOTES
* These functions are used by both frontend applications and
@@ -79,7 +78,6 @@
FILE *Pfout,
*Pfin;
FILE *Pfdebug; /* debugging libpq */
-int PQAsyncNotifyWaiting; /* for async. notification */
/* --------------------------------
* pq_init - open portal file descriptors
@@ -160,9 +158,7 @@ pq_close()
fclose(Pfout);
Pfout = NULL;
}
- PQAsyncNotifyWaiting = 0;
PQnotifies_init();
- pq_unregoob();
}
/* --------------------------------
@@ -418,29 +414,6 @@ pq_putint(int i, int b)
}
}
-/* ---
- * pq_sendoob - send a string over the out-of-band channel
- * pq_recvoob - receive a string over the oob channel
- * NB: Fortunately, the out-of-band channel doesn't conflict with
- * buffered I/O because it is separate from regular com. channel.
- * ---
- */
-int
-pq_sendoob(char *msg, int len)
-{
- int fd = fileno(Pfout);
-
- return send(fd, msg, len, MSG_OOB);
-}
-
-int
-pq_recvoob(char *msgPtr, int len)
-{
- int fd = fileno(Pfout);
-
- return recv(fd, msgPtr, len, MSG_OOB);
-}
-
/* --------------------------------
* pq_getinaddr - initialize address from host and port number
* --------------------------------
@@ -507,55 +480,6 @@ pq_getinserv(struct sockaddr_in * sin, char *host, char *serv)
return (pq_getinaddr(sin, host, ntohs(ss->s_port)));
}
-/*
- * register an out-of-band listener proc--at most one allowed.
- * This is used for receiving async. notification from the backend.
- */
-void
-pq_regoob(void (*fptr) ())
-{
- int fd = fileno(Pfout);
-
-#if defined(hpux)
- ioctl(fd, FIOSSAIOOWN, MyProcPid);
-#elif defined(sco)
- ioctl(fd, SIOCSPGRP, MyProcPid);
-#else
- fcntl(fd, F_SETOWN, MyProcPid);
-#endif /* hpux */
- pqsignal(SIGURG, fptr);
-}
-
-void
-pq_unregoob()
-{
- pqsignal(SIGURG, SIG_DFL);
-}
-
-
-void
-pq_async_notify()
-{
- char msg[20];
-
- /* int len = sizeof(msg); */
- int len = 20;
-
- if (pq_recvoob(msg, len) >= 0)
- {
- /* debugging */
- printf("received notification: %s\n", msg);
- PQAsyncNotifyWaiting = 1;
- /* PQappendNotify(msg+1); */
- }
- else
- {
- extern int errno;
-
- printf("SIGURG but no data: len = %d, err=%d\n", len, errno);
- }
-}
-
/*
* Streams -- wrapper around Unix socket system calls
*
@@ -620,7 +544,7 @@ StreamServerPort(char *hostName, short portName, int *fdP)
pqdebug("%s", PQerrormsg);
return (STATUS_ERROR);
}
- bzero(&saddr, sizeof(saddr));
+ MemSet((char *) &saddr, 0, sizeof(saddr));
saddr.sa.sa_family = family;
if (family == AF_UNIX)
{
diff --git a/src/backend/libpq/pqpacket.c b/src/backend/libpq/pqpacket.c
index 97caae952a..631af78ce2 100644
--- a/src/backend/libpq/pqpacket.c
+++ b/src/backend/libpq/pqpacket.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.15 1998/02/26 04:31:56 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/libpq/Attic/pqpacket.c,v 1.16 1998/07/09 03:28:46 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -33,7 +33,7 @@
* Set up a packet read for the postmaster event loop.
*/
-void PacketReceiveSetup(Packet *pkt, void (*iodone) (), char *arg)
+void PacketReceiveSetup(Packet *pkt, PacketDoneProc iodone, void *arg)
{
pkt->nrtodo = sizeof(pkt->len);
pkt->ptr = (char *) &pkt->len;
@@ -94,8 +94,8 @@ PacketReceiveFragment(Packet *pkt, int sock)
if (pkt->iodone == NULL)
return STATUS_ERROR;
- (*pkt->iodone) (pkt->arg, pkt->len - sizeof(pkt->len),
- (char *) &pkt->pkt);
+ return (*pkt->iodone) (pkt->arg, pkt->len - sizeof(pkt->len),
+ (void *) &pkt->pkt);
}
return STATUS_OK;
@@ -107,7 +107,7 @@ PacketReceiveFragment(Packet *pkt, int sock)
if (errno == EINTR)
return STATUS_OK;
- fprintf(stderr, "read() system call failed\n");
+ perror("PacketReceiveFragment: read() failed");
return STATUS_ERROR;
}
@@ -117,8 +117,9 @@ PacketReceiveFragment(Packet *pkt, int sock)
* Set up a packet write for the postmaster event loop.
*/
-void PacketSendSetup(Packet *pkt, int nbytes, void (*iodone) (), char *arg)
+void PacketSendSetup(Packet *pkt, int nbytes, PacketDoneProc iodone, void *arg)
{
+ pkt->len = (PacketLen) nbytes;
pkt->nrtodo = nbytes;
pkt->ptr = (char *) &pkt->pkt;
pkt->iodone = iodone;
@@ -153,7 +154,8 @@ PacketSendFragment(Packet *pkt, int sock)
if (pkt->iodone == NULL)
return STATUS_ERROR;
- (*pkt->iodone) (pkt->arg);
+ return (*pkt->iodone) (pkt->arg, pkt->len,
+ (void *) &pkt->pkt);
}
return STATUS_OK;
@@ -165,7 +167,7 @@ PacketSendFragment(Packet *pkt, int sock)
if (errno == EINTR)
return STATUS_OK;
- fprintf(stderr, "write() system call failed\n");
+ perror("PacketSendFragment: write() failed");
return STATUS_ERROR;
}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 60d998814d..7f33dfedba 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.92 1998/06/27 14:06:40 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.93 1998/07/09 03:28:47 scrappy Exp $
*
* NOTES
*
@@ -206,7 +206,6 @@ static int orgsigmask = sigblock(0);
*/
static unsigned int random_seed = 0;
-long MyCancelKey = 0;
extern char *optarg;
extern int optind,
@@ -228,7 +227,8 @@ static void ExitPostmaster(int status);
static void usage(const char *);
static int ServerLoop(void);
static int BackendStartup(Port *port);
-static void readStartupPacket(char *arg, PacketLen len, char *pkt);
+static int readStartupPacket(void *arg, PacketLen len, void *pkt);
+static int processCancelRequest(Port *port, PacketLen len, void *pkt);
static int initMasks(fd_set *rmask, fd_set *wmask);
static long PostmasterRandom(void);
static void RandomSalt(char *salt);
@@ -518,6 +518,10 @@ PostmasterMain(int argc, char *argv[])
if (silentflag)
pmdaemonize();
+ /*
+ * Set up signal handlers for the postmaster process.
+ */
+
pqsignal(SIGINT, pmdie);
pqsignal(SIGCHLD, reaper);
pqsignal(SIGTTIN, SIG_IGN);
@@ -657,14 +661,14 @@ ServerLoop(void)
(port = ConnCreate(ServerSock_UNIX)) != NULL)
PacketReceiveSetup(&port->pktInfo,
readStartupPacket,
- (char *) port);
+ (void *) port);
if (ServerSock_INET != INVALID_SOCK &&
FD_ISSET(ServerSock_INET, &rmask) &&
(port = ConnCreate(ServerSock_INET)) != NULL)
PacketReceiveSetup(&port->pktInfo,
readStartupPacket,
- (char *) port);
+ (void *) port);
/* Build up new masks for select(). */
@@ -790,8 +794,8 @@ initMasks(fd_set *rmask, fd_set *wmask)
* Called when the startup packet has been read.
*/
-static void
-readStartupPacket(char *arg, PacketLen len, char *pkt)
+static int
+readStartupPacket(void *arg, PacketLen len, void *pkt)
{
Port *port;
StartupPacket *si;
@@ -799,6 +803,28 @@ readStartupPacket(char *arg, PacketLen len, char *pkt)
port = (Port *) arg;
si = (StartupPacket *) pkt;
+ /* The first field is either a protocol version number or
+ * a special request code.
+ */
+
+ port->proto = ntohl(si->protoVersion);
+
+ if (port->proto == CANCEL_REQUEST_CODE)
+ return processCancelRequest(port, len, pkt);
+
+ /* Could add additional special packet types here */
+
+ /* Check we can handle the protocol the frontend is using. */
+
+ if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
+ PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
+ (PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
+ PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
+ {
+ PacketSendError(&port->pktInfo, "Unsupported frontend protocol.");
+ return STATUS_OK; /* don't close the connection yet */
+ }
+
/*
* Get the parameters from the startup packet as C strings. The
* packet destination was cleared first so a short packet has zeros
@@ -815,31 +841,74 @@ readStartupPacket(char *arg, PacketLen len, char *pkt)
if (port->database[0] == '\0')
StrNCpy(port->database, si->user, sizeof(port->database) - 1);
- /* Check we can handle the protocol the frontend is using. */
-
- port->proto = ntohl(si->protoVersion);
-
- if (PG_PROTOCOL_MAJOR(port->proto) < PG_PROTOCOL_MAJOR(PG_PROTOCOL_EARLIEST) ||
- PG_PROTOCOL_MAJOR(port->proto) > PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) ||
- (PG_PROTOCOL_MAJOR(port->proto) == PG_PROTOCOL_MAJOR(PG_PROTOCOL_LATEST) &&
- PG_PROTOCOL_MINOR(port->proto) > PG_PROTOCOL_MINOR(PG_PROTOCOL_LATEST)))
- {
- PacketSendError(&port->pktInfo, "Unsupported frontend protocol.");
- return;
- }
-
/* Check a user name was given. */
if (port->user[0] == '\0')
{
PacketSendError(&port->pktInfo,
"No Postgres username specified in startup packet.");
- return;
+ return STATUS_OK; /* don't close the connection yet */
}
/* Start the authentication itself. */
be_recvauth(port);
+
+ return STATUS_OK; /* don't close the connection yet */
+}
+
+
+/*
+ * The client has sent a cancel request packet, not a normal
+ * start-a-new-backend packet. Perform the necessary processing.
+ * Note that in any case, we return STATUS_ERROR to close the
+ * connection immediately. Nothing is sent back to the client.
+ */
+
+static int
+processCancelRequest(Port *port, PacketLen len, void *pkt)
+{
+ CancelRequestPacket *canc = (CancelRequestPacket *) pkt;
+ int backendPID;
+ long cancelAuthCode;
+ Dlelem *curr;
+ Backend *bp;
+
+ backendPID = (int) ntohl(canc->backendPID);
+ cancelAuthCode = (long) ntohl(canc->cancelAuthCode);
+
+ /* See if we have a matching backend */
+
+ for (curr = DLGetHead(BackendList); curr; curr = DLGetSucc(curr))
+ {
+ bp = (Backend *) DLE_VAL(curr);
+ if (bp->pid == backendPID)
+ {
+ if (bp->cancel_key == cancelAuthCode)
+ {
+ /* Found a match; signal that backend to cancel current op */
+ if (DebugLvl)
+ fprintf(stderr, "%s: processCancelRequest: sending SIGINT to process %d\n",
+ progname, bp->pid);
+ kill(bp->pid, SIGINT);
+ }
+ else
+ {
+ /* Right PID, wrong key: no way, Jose */
+ if (DebugLvl)
+ fprintf(stderr, "%s: processCancelRequest: bad key in cancel request for process %d\n",
+ progname, bp->pid);
+ }
+ return STATUS_ERROR;
+ }
+ }
+
+ /* No matching backend */
+ if (DebugLvl)
+ fprintf(stderr, "%s: processCancelRequest: bad PID in cancel request for process %d\n",
+ progname, backendPID);
+
+ return STATUS_ERROR;
}
@@ -1221,6 +1290,8 @@ DoBackend(Port *port)
char dbbuf[ARGV_SIZE + 1];
int ac = 0;
int i;
+ struct timeval now;
+ struct timezone tz;
/*
* Let's clean up ourselves as the postmaster child
@@ -1254,7 +1325,16 @@ DoBackend(Port *port)
if (NetServer)
StreamClose(ServerSock_INET);
StreamClose(ServerSock_UNIX);
-
+
+ /*
+ * Don't want backend to be able to see the postmaster random number
+ * generator state. We have to clobber the static random_seed *and*
+ * start a new random sequence in the random() library function.
+ */
+ random_seed = 0;
+ gettimeofday(&now, &tz);
+ srandom(now.tv_usec);
+
/* Now, on to standard postgres stuff */
MyProcPid = getpid();
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 97ac571d2c..0a7408a7b9 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.78 1998/06/27 04:53:43 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.79 1998/07/09 03:28:48 scrappy Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
@@ -724,7 +724,7 @@ pg_exec_query_dest(char *query_string, /* string to execute */
/* --------------------------------
* signal handler routines used in PostgresMain()
*
- * handle_warn() is used to catch kill(getpid(),1) which
+ * handle_warn() is used to catch kill(getpid(), SIGHUP) which
* occurs when elog(ERROR) is called.
*
* quickdie() occurs when signalled by the postmaster.
@@ -777,7 +777,7 @@ FloatExceptionHandler(SIGNAL_ARGS)
}
-/* signal handler for query cancel */
+/* signal handler for query cancel signal from postmaster */
static void
QueryCancelHandler(SIGNAL_ARGS)
{
@@ -787,12 +787,9 @@ QueryCancelHandler(SIGNAL_ARGS)
void
CancelQuery(void)
{
- char dummy;
-
- /* throw it away */
- while (pq_recvoob(&dummy, 1) > 0)
- ;
- /* QueryCancel reset in longjump after elog() call */
+ /* QueryCancel flag will be reset in main loop, which we reach by
+ * longjmp from elog().
+ */
elog(ERROR, "Query was cancelled.");
}
@@ -1261,7 +1258,6 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
}
pq_init(Portfd);
whereToSendOutput = Remote;
- pq_regoob(QueryCancelHandler); /* we do it here so the backend it connected */
}
else
whereToSendOutput = Debug;
@@ -1287,6 +1283,24 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
}
#endif
+ /* ----------------
+ * Set up handler for cancel-request signal, and
+ * send this backend's cancellation info to the frontend.
+ * This should not be done until we are sure startup is successful.
+ * ----------------
+ */
+
+ pqsignal(SIGINT, QueryCancelHandler);
+
+ if (whereToSendOutput == Remote &&
+ PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2)
+ {
+ pq_putnchar("K", 1);
+ pq_putint((int32) MyProcPid, sizeof(int32));
+ pq_putint((int32) MyCancelKey, sizeof(int32));
+ /* Need not flush since ReadyForQuery will do it. */
+ }
+
/* ----------------
* if an exception is encountered, processing resumes here
* so we abort the current transaction and start a new one.
@@ -1294,7 +1308,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
* so that the slaves signal the master to abort the transaction
* rather than calling AbortCurrentTransaction() themselves.
*
- * Note: elog(ERROR) causes a kill(getpid(),1) to occur sending
+ * Note: elog(ERROR) causes a kill(getpid(), SIGHUP) to occur sending
* us back here.
* ----------------
*/
@@ -1325,7 +1339,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
if (!IsUnderPostmaster)
{
puts("\nPOSTGRES backend interactive interface");
- puts("$Revision: 1.78 $ $Date: 1998/06/27 04:53:43 $");
+ puts("$Revision: 1.79 $ $Date: 1998/07/09 03:28:48 $");
}
/* ----------------
@@ -1431,7 +1445,7 @@ PostgresMain(int argc, char *argv[], int real_argc, char *real_argv[])
break;
default:
- elog(ERROR, "unknown frontend message was recieved");
+ elog(ERROR, "unknown frontend message was received");
}
/* ----------------
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 2deec81de0..585471d37f 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.23 1998/05/29 17:00:18 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/init/globals.c,v 1.24 1998/07/09 03:28:51 scrappy Exp $
*
* NOTES
* Globals used all over the place should be declared here and not
@@ -44,6 +44,8 @@ bool QueryCancel = false;
int MyProcPid;
+long MyCancelKey;
+
char *DataDir;
/*
diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c
index 1ed9e4561f..65692bd9a1 100644
--- a/src/bin/psql/psql.c
+++ b/src/bin/psql/psql.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.146 1998/06/16 07:29:38 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.147 1998/07/09 03:28:53 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -291,15 +291,27 @@ PSQLexec(PsqlSettings *pset, char *query)
* If interactive, 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 only because the cancel is sent as an OOB message.
- * If it were inline data, then we'd risk inserting it into the
- * middle of a normal data message by doing this.
- * (It's probably not too cool to write on stderr, for that matter...
- * but for debugging purposes we'll risk that.)
+ * is safe only because PQrequestCancel is carefully written to
+ * make it so. We have to be very careful what else we do in the
+ * signal handler.
+ * Writing on stderr is potentially dangerous, if the signal interrupted
+ * some stdio operation on stderr. On Unix we can avoid trouble by using
+ * write() instead; on Windows that's probably not workable, but we can
+ * at least avoid trusting printf by using the more primitive fputs.
*/
static PGconn * cancelConn = NULL; /* connection to try cancel on */
+static void
+safe_write_stderr (const char * s)
+{
+#ifdef WIN32
+ fputs(s, stderr);
+#else
+ write(fileno(stderr), s, strlen(s));
+#endif
+}
+
static void
handle_sigint (SIGNAL_ARGS)
{
@@ -307,11 +319,13 @@ handle_sigint (SIGNAL_ARGS)
exit(1); /* accept signal if no connection */
/* Try to send cancel request */
if (PQrequestCancel(cancelConn))
- fprintf(stderr, "\nCANCEL request sent\n");
+ {
+ safe_write_stderr("\nCANCEL request sent\n");
+ }
else
{
- fprintf(stderr, "\nCannot send cancel request:\n%s\n",
- PQerrorMessage(cancelConn));
+ safe_write_stderr("\nCannot send cancel request:\n");
+ safe_write_stderr(PQerrorMessage(cancelConn));
}
}
diff --git a/src/include/commands/dbcommands.h b/src/include/commands/dbcommands.h
index 7717fd9d15..06a291070e 100644
--- a/src/include/commands/dbcommands.h
+++ b/src/include/commands/dbcommands.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: dbcommands.h,v 1.1 1997/11/24 05:32:51 momjian Exp $
+ * $Id: dbcommands.h,v 1.2 1998/07/09 03:28:56 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,8 +17,7 @@
* Originally from tmp/daemon.h. The functions declared in daemon.h does not
* exist; hence removed. -- AY 7/29/94
*/
-#define SIGKILLDAEMON1 SIGINT
-#define SIGKILLDAEMON2 SIGTERM
+#define SIGKILLDAEMON1 SIGTERM
extern void createdb(char *dbname, char *dbpath);
extern void destroydb(char *dbname);
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 7058eec246..5d0c6d7ac6 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -7,7 +7,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq-be.h,v 1.10 1998/02/26 04:41:49 momjian Exp $
+ * $Id: libpq-be.h,v 1.11 1998/07/09 03:29:00 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -68,16 +68,20 @@ typedef enum
WritingPacket
} PacketState;
+typedef int (*PacketDoneProc) (void * arg, PacketLen pktlen, void * pktdata);
+
typedef struct Packet
{
PacketState state; /* What's in progress. */
PacketLen len; /* Actual length */
int nrtodo; /* Bytes still to transfer */
char *ptr; /* Buffer pointer */
- void (*iodone) (); /* I/O complete callback */
- char *arg; /* Argument to callback */
+ PacketDoneProc iodone; /* I/O complete callback */
+ void *arg; /* Argument to callback */
- /* A union of all the different packets. */
+ /* We declare the data buffer as a union of the allowed packet types,
+ * mainly to ensure that enough space is allocated for the largest one.
+ */
union
{
@@ -89,6 +93,7 @@ typedef struct Packet
/* These are incoming and have a packet length prepended. */
StartupPacket si;
+ CancelRequestPacket canc;
PasswordPacketV0 pwv0;
PasswordPacket pw;
} pkt;
@@ -126,16 +131,15 @@ typedef struct Port
extern FILE *Pfout,
*Pfin;
-extern int PQAsyncNotifyWaiting;
extern ProtocolVersion FrontendProtocol;
/*
* prototypes for functions in pqpacket.c
*/
-void PacketReceiveSetup(Packet *pkt, void (*iodone) (), char *arg);
+void PacketReceiveSetup(Packet *pkt, PacketDoneProc iodone, void *arg);
int PacketReceiveFragment(Packet *pkt, int sock);
-void PacketSendSetup(Packet *pkt, int nbytes, void (*iodone) (), char *arg);
+void PacketSendSetup(Packet *pkt, int nbytes, PacketDoneProc iodone, void *arg);
int PacketSendFragment(Packet *pkt, int sock);
void PacketSendError(Packet *pkt, char *errormsg);
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 9a347989c4..804bdf0cc4 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: libpq.h,v 1.16 1998/06/16 07:29:41 momjian Exp $
+ * $Id: libpq.h,v 1.17 1998/07/09 03:29:01 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -126,8 +126,7 @@ extern size_t portals_array_size;
*/
typedef struct PQNotifyList
{
- char relname[NAMEDATALEN]; /* name of relation containing
- * data */
+ char relname[NAMEDATALEN]; /* listen/notify name */
int be_pid; /* process id of backend */
int valid; /* has this already been handled by user. */
/* SLNode Node; */
@@ -268,8 +267,6 @@ extern int pq_getint(int b);
extern void pq_putstr(char *s);
extern void pq_putnchar(char *s, int n);
extern void pq_putint(int i, int b);
-extern int pq_sendoob(char *msg, int len);
-extern int pq_recvoob(char *msgPtr, int len);
extern int pq_getinaddr(struct sockaddr_in * sin, char *host, int port);
extern int pq_getinserv(struct sockaddr_in * sin, char *host, char *serv);
@@ -281,10 +278,7 @@ extern int
pq_connect(char *dbname, char *user, char *args, char *hostName,
char *debugTty, char *execFile, short portName);
extern int StreamOpen(char *hostName, short portName, Port *port);
-extern void pq_regoob(void (*fptr) ());
-extern void pq_unregoob(void);
-extern void pq_async_notify(void);
-extern void StreamDoUnlink();
+extern void StreamDoUnlink(void);
extern int StreamServerPort(char *hostName, short portName, int *fdP);
extern int StreamConnection(int server_fd, Port *port);
extern void StreamClose(int sock);
diff --git a/src/include/libpq/pqcomm.h b/src/include/libpq/pqcomm.h
index 867e91c060..98d4ad0350 100644
--- a/src/include/libpq/pqcomm.h
+++ b/src/include/libpq/pqcomm.h
@@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: pqcomm.h,v 1.25 1998/05/06 23:50:32 momjian Exp $
+ * $Id: pqcomm.h,v 1.26 1998/07/09 03:29:01 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
@@ -122,6 +122,25 @@ typedef uint32 AuthRequest;
typedef ProtocolVersion MsgType;
+/* A client can also send a cancel-current-operation request to the postmaster.
+ * This is uglier than sending it directly to the client's backend, but it
+ * avoids depending on out-of-band communication facilities.
+ */
+
+/* The cancel request code must not match any protocol version number
+ * we're ever likely to use. This random choice should do.
+ */
+#define CANCEL_REQUEST_CODE PG_PROTOCOL(1234,5678)
+
+typedef struct CancelRequestPacket
+{
+ /* Note that each field is stored in network byte order! */
+ MsgType cancelRequestCode; /* code to identify a cancel request */
+ uint32 backendPID; /* PID of client's backend */
+ uint32 cancelAuthCode; /* secret key to authorize cancel */
+} CancelRequestPacket;
+
+
/* in pqcompriv.c */
int pqGetShort(int *, FILE *);
int pqGetLong(int *, FILE *);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 6f4d6f4028..a6f2243299 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -11,7 +11,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
- * $Id: miscadmin.h,v 1.26 1998/06/09 17:13:06 momjian Exp $
+ * $Id: miscadmin.h,v 1.27 1998/07/09 03:28:55 scrappy Exp $
*
* NOTES
* some of the information in this file will be moved to
@@ -42,6 +42,8 @@ extern char *DataDir;
extern int MyProcPid;
+extern long MyCancelKey;
+
extern char OutputFileName[];
/*
diff --git a/src/include/port/sco.h b/src/include/port/sco.h
index 5bc74f7279..30db00386f 100644
--- a/src/include/port/sco.h
+++ b/src/include/port/sco.h
@@ -1,6 +1,5 @@
#include /* For _POSIX_PATH_MAX */
#define MAXPATHLEN _POSIX_PATH_MAX
-#define SIGURG SIGUSR1
#define NOFILE NOFILES_MIN
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 672948a287..47aa2b9bca 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -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
diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c
index 49bd6d07e5..6a68af49d3 100644
--- a/src/interfaces/libpq/fe-exec.c
+++ b/src/interfaces/libpq/fe-exec.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.
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index 1bfd0f5a3e..a4fad81a37 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -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);
diff --git a/src/man/listen.l b/src/man/listen.l
index d18d3fdd7f..49801408f7 100644
--- a/src/man/listen.l
+++ b/src/man/listen.l
@@ -1,6 +1,6 @@
.\" This is -*-nroff-*-
.\" XXX standard disclaimer belongs here....
-.\" $Header: /cvsroot/pgsql/src/man/Attic/listen.l,v 1.6 1998/06/24 13:21:27 momjian Exp $
+.\" $Header: /cvsroot/pgsql/src/man/Attic/listen.l,v 1.7 1998/07/09 03:29:09 scrappy Exp $
.TH "LISTEN" SQL 03/12/94 PostgreSQL PostgreSQL
.SH NAME
listen - listen for notification on a relation
@@ -21,9 +21,7 @@ is cleared.
.PP
This event notification is performed through the Libpq protocol
and frontend application interface. The application program
-must explicitly poll a Libpq global variable,
-.IR PQAsyncNotifyWaiting ,
-and call the routine
+must call the routine
.IR PQnotifies
in order to find out the name of the class to which a given
notification corresponds. If this code is not included in
diff --git a/src/man/notify.l b/src/man/notify.l
index 50f44b442e..26b3de6f6f 100644
--- a/src/man/notify.l
+++ b/src/man/notify.l
@@ -1,6 +1,6 @@
.\" This is -*-nroff-*-
.\" XXX standard disclaimer belongs here....
-.\" $Header: /cvsroot/pgsql/src/man/Attic/notify.l,v 1.4 1998/06/24 13:21:27 momjian Exp $
+.\" $Header: /cvsroot/pgsql/src/man/Attic/notify.l,v 1.5 1998/07/09 03:29:11 scrappy Exp $
.TH "NOTIFY" SQL 11/05/95 PostgreSQL PostgreSQL
.SH NAME
notify - signal all frontends and backends listening on a class
@@ -32,11 +32,14 @@ does is indicate that some backend wishes its peers to examine
.IR class_name
in some application-specific way.
.PP
+In fact,
+.IR class_name
+need not be the name of an SQL class at all. It is best thought of
+as a condition name that the application programmer selects.
+.PP
This event notification is performed through the Libpq protocol
and frontend application interface. The application program
-must explicitly poll a Libpq global variable,
-.IR PQAsyncNotifyWaiting ,
-and call the routine
+must call the routine
.IR PQnotifies
in order to find out the name of the class to which a given
notification corresponds. If this code is not included in