diff options
Diffstat (limited to 'src/interfaces/libpq/fe-misc.c')
-rw-r--r-- | src/interfaces/libpq/fe-misc.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index e5ef8d44bd4..ea4c9d2ee0c 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -824,6 +824,13 @@ definitelyFailed: * * Return 0 on success, -1 on failure and 1 when not all data could be sent * because the socket would block and the connection is non-blocking. + * + * Upon write failure, conn->write_failed is set and the error message is + * saved in conn->write_err_msg, but we clear the output buffer and return + * zero anyway; this is because callers should soldier on until it's possible + * to read from the server and check for an error message. write_err_msg + * should be reported only when we are unable to obtain a server error first. + * (Thus, a -1 result is returned only for an internal *read* failure.) */ static int pqSendSome(PGconn *conn, int len) @@ -832,13 +839,32 @@ pqSendSome(PGconn *conn, int len) int remaining = conn->outCount; int result = 0; + /* + * If we already had a write failure, we will never again try to send data + * on that connection. Even if the kernel would let us, we've probably + * lost message boundary sync with the server. conn->write_failed + * therefore persists until the connection is reset, and we just discard + * all data presented to be written. + */ + if (conn->write_failed) + { + /* conn->write_err_msg should be set up already */ + conn->outCount = 0; + return 0; + } + if (conn->sock == PGINVALID_SOCKET) { printfPQExpBuffer(&conn->errorMessage, libpq_gettext("connection not open\n")); + conn->write_failed = true; + /* Transfer error message to conn->write_err_msg, if possible */ + /* (strdup failure is OK, we'll cope later) */ + conn->write_err_msg = strdup(conn->errorMessage.data); + resetPQExpBuffer(&conn->errorMessage); /* Discard queued data; no chance it'll ever be sent */ conn->outCount = 0; - return -1; + return 0; } /* while there's still data to send */ @@ -876,17 +902,24 @@ pqSendSome(PGconn *conn, int len) default: /* pqsecure_write set the error message for us */ + conn->write_failed = true; /* - * 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. But abandon - * attempt to send data. + * Transfer error message to conn->write_err_msg, if + * possible (strdup failure is OK, we'll cope later). + * + * Note: this assumes that pqsecure_write and its children + * will overwrite not append to conn->errorMessage. If + * that's ever changed, we could remember the length of + * conn->errorMessage at entry to this routine, and then + * save and delete just what was appended. */ + conn->write_err_msg = strdup(conn->errorMessage.data); + resetPQExpBuffer(&conn->errorMessage); + + /* Discard queued data; no chance it'll ever be sent */ conn->outCount = 0; - return -1; + return 0; } } else @@ -921,6 +954,9 @@ pqSendSome(PGconn *conn, int len) * can do, and works pretty well in practice. (The documentation * used to say that you only need to wait for write-ready, so * there are still plenty of applications like that out there.) + * + * Note that errors here don't result in write_failed becoming + * set. */ if (pqReadData(conn) < 0) { @@ -956,6 +992,7 @@ pqSendSome(PGconn *conn, int len) * * Return 0 on success, -1 on failure and 1 when not all data could be sent * because the socket would block and the connection is non-blocking. + * (See pqSendSome comments about how failure should be handled.) */ int pqFlush(PGconn *conn) |