From f0ae1e8d10d7a38029cd0009063fa5131dce7937 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 28 May 1999 01:54:53 +0000 Subject: [PATCH] When closure of the backend connection is detected during pqFlush, do the right thing: look for a NOTICE message from the backend before we close our side of the socket. 6.4 libpq did not reliably print the backend's hara-kiri message, 'The Postmaster has informed me ...', because it only did the right thing if connection closure was detected during a read attempt instead of a write attempt. --- src/interfaces/libpq/fe-exec.c | 71 +++++++++++++++++++++++++++++++++++------- src/interfaces/libpq/fe-misc.c | 19 +++++------ 2 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 475a51bc4a..446a70c821 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.80 1999/05/25 16:15:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.81 1999/05/28 01:54:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,6 +47,7 @@ const char *const pgresStatus[] = { static int addTuple(PGresult *res, PGresAttValue *tup); static void parseInput(PGconn *conn); +static void handleSendFailure(PGconn *conn); static int getRowDescriptions(PGconn *conn); static int getAnotherTuple(PGconn *conn, int binary); static int getNotify(PGconn *conn); @@ -433,18 +434,53 @@ PQsendQuery(PGconn *conn, const char *query) /* send the query to the backend; */ /* the frontend-backend protocol uses 'Q' to designate queries */ - if (pqPutnchar("Q", 1, conn)) - return 0; - if (pqPuts(query, conn)) - return 0; - if (pqFlush(conn)) + if (pqPutnchar("Q", 1, conn) || + pqPuts(query, conn) || + pqFlush(conn)) + { + handleSendFailure(conn); return 0; + } /* OK, it's launched! */ conn->asyncStatus = PGASYNC_BUSY; return 1; } +/* + * handleSendFailure: try to clean up after failure to send command. + * + * Primarily, what we want to accomplish here is to process an async + * NOTICE message that the backend might have sent just before it died. + * + * NOTE: this routine should only be called in PGASYNC_IDLE state. + */ + +static void +handleSendFailure(PGconn *conn) +{ + /* Preserve the error message emitted by the failing output routine */ + char * svErrMsg = strdup(conn->errorMessage); + + /* + * Accept any available input data, ignoring errors. Note that if + * pqReadData decides the backend has closed the channel, it will + * close our side of the socket --- that's just what we want here. + */ + while (pqReadData(conn) > 0) + /* loop until no more data readable */ ; + + /* + * Parse any available input messages. Since we are in PGASYNC_IDLE + * state, only NOTICE and NOTIFY messages will be eaten. + */ + parseInput(conn); + + /* Restore error message generated by output routine, if any. */ + if (*svErrMsg != '\0') + strcpy(conn->errorMessage, svErrMsg); + free(svErrMsg); +} /* * Consume any available input from the backend @@ -1399,31 +1435,44 @@ PQfn(PGconn *conn, /* clear the error string */ conn->errorMessage[0] = '\0'; - if (pqPuts("F ", conn)) /* function */ - return NULL; - if (pqPutInt(fnid, 4, conn))/* function id */ - return NULL; - if (pqPutInt(nargs, 4, conn)) /* # of args */ + if (pqPuts("F ", conn) || /* function */ + pqPutInt(fnid, 4, conn) || /* function id */ + pqPutInt(nargs, 4, conn)) /* # of args */ + { + handleSendFailure(conn); return NULL; + } for (i = 0; i < nargs; ++i) { /* len.int4 + contents */ if (pqPutInt(args[i].len, 4, conn)) + { + handleSendFailure(conn); return NULL; + } if (args[i].isint) { if (pqPutInt(args[i].u.integer, 4, conn)) + { + handleSendFailure(conn); return NULL; + } } else { if (pqPutnchar((char *) args[i].u.ptr, args[i].len, conn)) + { + handleSendFailure(conn); return NULL; + } } } if (pqFlush(conn)) + { + handleSendFailure(conn); return NULL; + } for (;;) { diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index fd33052f7c..a16b89e5c3 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -24,7 +24,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.24 1999/05/25 16:15:13 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.25 1999/05/28 01:54:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -471,7 +471,6 @@ pqFlush(PGconn *conn) /* Prevent being SIGPIPEd if backend has closed the connection. */ #ifndef WIN32 pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN); - #endif int sent = send(conn->sock, ptr, len, 0); @@ -498,6 +497,7 @@ pqFlush(PGconn *conn) case EWOULDBLOCK: break; #endif + case EPIPE: #ifdef ECONNRESET case ECONNRESET: @@ -506,14 +506,15 @@ pqFlush(PGconn *conn) "pqFlush() -- backend closed the channel unexpectedly.\n" "\tThis probably means the backend terminated abnormally" " before or while processing the request.\n"); - conn->status = CONNECTION_BAD; /* No more connection */ -#ifdef WIN32 - closesocket(conn->sock); -#else - close(conn->sock); -#endif - conn->sock = -1; + /* + * We used to close the socket here, but that's a bad + * idea since there might be unread data waiting + * (typically, a NOTICE message from the backend telling + * us it's committing hara-kiri...). Leave the socket + * open until pqReadData finds no more data can be read. + */ return EOF; + default: sprintf(conn->errorMessage, "pqFlush() -- couldn't send data: errno=%d\n%s\n", -- 2.11.0