* miscellaneous useful functions
*
* The communication routines here are analogous to the ones in
- * backend/libpq/pqcomm.c and backend/libpq/pqcomprim.c, but operate
+ * backend/libpq/pqcomm.c and backend/libpq/pqformat.c, but operate
* in the considerably different environment of the frontend libpq.
* In particular, we work with a bare nonblock-mode socket, rather than
* a stdio stream, so that we can avoid unwanted blocking of the application.
* routines.
*
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.89 2003/04/19 00:02:30 tgl Exp $
+ * src/interfaces/libpq/fe-misc.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
-#include <errno.h>
#include <signal.h>
#include <time.h>
-#include <netinet/in.h>
-#include <arpa/inet.h>
#ifdef WIN32
#include "win32.h"
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
-#ifdef HAVE_SYS_POLL_H
-#include <sys/poll.h>
-#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include "libpq-fe.h"
#include "libpq-int.h"
-#include "pqsignal.h"
#include "mb/pg_wchar.h"
-
-#define DONOTICE(conn,message) \
- ((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
+#include "pg_config_paths.h"
+#include "port/pg_bswap.h"
static int pqPutMsgBytes(const void *buf, size_t len, PGconn *conn);
static int pqSendSome(PGconn *conn, int len);
time_t end_time);
static int pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
+/*
+ * PQlibVersion: return the libpq version number
+ */
+int
+PQlibVersion(void)
+{
+ return PG_VERSION_NUM;
+}
+
+/*
+ * fputnbytes: print exactly N bytes to a file
+ *
+ * We avoid using %.*s here because it can misbehave if the data
+ * is not valid in what libc thinks is the prevailing encoding.
+ */
+static void
+fputnbytes(FILE *f, const char *str, size_t n)
+{
+ while (n-- > 0)
+ fputc(*str++, f);
+}
+
/*
* pqGetc: get 1 character from the connection
/*
- * pqGets:
+ * pqGets[_append]:
* get a null-terminated string from the connection,
* and store it in an expansible PQExpBuffer.
* If we run out of memory, all of the string is still read,
* but the excess characters are silently discarded.
*/
-int
-pqGets(PQExpBuffer buf, PGconn *conn)
+static int
+pqGets_internal(PQExpBuffer buf, PGconn *conn, bool resetbuffer)
{
/* Copy conn data to locals for faster search loop */
char *inBuffer = conn->inBuffer;
slen = inCursor - conn->inCursor;
- resetPQExpBuffer(buf);
+ if (resetbuffer)
+ resetPQExpBuffer(buf);
+
appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);
conn->inCursor = ++inCursor;
return 0;
}
+int
+pqGets(PQExpBuffer buf, PGconn *conn)
+{
+ return pqGets_internal(buf, conn, true);
+}
+
+int
+pqGets_append(PQExpBuffer buf, PGconn *conn)
+{
+ return pqGets_internal(buf, conn, false);
+}
+
/*
* pqPuts: write a null-terminated string to the current message
return EOF;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> '%s'\n", s);
+ fprintf(conn->Pfdebug, "To backend> \"%s\"\n", s);
return 0;
}
int
pqGetnchar(char *s, size_t len, PGconn *conn)
{
- if (len < 0 || len > (size_t) (conn->inEnd - conn->inCursor))
+ if (len > (size_t) (conn->inEnd - conn->inCursor))
return EOF;
memcpy(s, conn->inBuffer + conn->inCursor, len);
conn->inCursor += len;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (%lu)> %.*s\n", (unsigned long) len, (int) len, s);
+ {
+ fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
+ fputnbytes(conn->Pfdebug, s, len);
+ fprintf(conn->Pfdebug, "\n");
+ }
+
+ return 0;
+}
+
+/*
+ * pqSkipnchar:
+ * skip over len bytes in input buffer.
+ *
+ * Note: this is primarily useful for its debug output, which should
+ * be exactly the same as for pqGetnchar. We assume the data in question
+ * will actually be used, but just isn't getting copied anywhere as yet.
+ */
+int
+pqSkipnchar(size_t len, PGconn *conn)
+{
+ if (len > (size_t) (conn->inEnd - conn->inCursor))
+ return EOF;
+
+ if (conn->Pfdebug)
+ {
+ fprintf(conn->Pfdebug, "From backend (%lu)> ", (unsigned long) len);
+ fputnbytes(conn->Pfdebug, conn->inBuffer + conn->inCursor, len);
+ fprintf(conn->Pfdebug, "\n");
+ }
+
+ conn->inCursor += len;
return 0;
}
return EOF;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %.*s\n", (int) len, s);
+ {
+ fprintf(conn->Pfdebug, "To backend> ");
+ fputnbytes(conn->Pfdebug, s, len);
+ fprintf(conn->Pfdebug, "\n");
+ }
return 0;
}
/*
- * pgGetInt
+ * pqGetInt
* read a 2 or 4 byte integer and convert from network byte order
* to local byte order
*/
{
uint16 tmp2;
uint32 tmp4;
- char noticeBuf[64];
switch (bytes)
{
return EOF;
memcpy(&tmp2, conn->inBuffer + conn->inCursor, 2);
conn->inCursor += 2;
- *result = (int) ntohs(tmp2);
+ *result = (int) pg_ntoh16(tmp2);
break;
case 4:
if (conn->inCursor + 4 > conn->inEnd)
return EOF;
memcpy(&tmp4, conn->inBuffer + conn->inCursor, 4);
conn->inCursor += 4;
- *result = (int) ntohl(tmp4);
+ *result = (int) pg_ntoh32(tmp4);
break;
default:
- snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("integer of size %lu not supported by pqGetInt\n"),
- (unsigned long) bytes);
- DONOTICE(conn, noticeBuf);
+ pqInternalNotice(&conn->noticeHooks,
+ "integer of size %lu not supported by pqGetInt",
+ (unsigned long) bytes);
return EOF;
}
}
/*
- * pgPutInt
+ * pqPutInt
* write an integer of 2 or 4 bytes, converting from host byte order
* to network byte order.
*/
{
uint16 tmp2;
uint32 tmp4;
- char noticeBuf[64];
switch (bytes)
{
case 2:
- tmp2 = htons((uint16) value);
+ tmp2 = pg_hton16((uint16) value);
if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
return EOF;
break;
case 4:
- tmp4 = htonl((uint32) value);
+ tmp4 = pg_hton32((uint32) value);
if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
return EOF;
break;
default:
- snprintf(noticeBuf, sizeof(noticeBuf),
- libpq_gettext("integer of size %lu not supported by pqPutInt\n"),
- (unsigned long) bytes);
- DONOTICE(conn, noticeBuf);
+ pqInternalNotice(&conn->noticeHooks,
+ "integer of size %lu not supported by pqPutInt",
+ (unsigned long) bytes);
return EOF;
}
/*
* Make sure conn's output buffer can hold bytes_needed bytes (caller must
- * include existing outCount into the value!)
+ * include already-stored data into the value!)
*
- * Returns 0 on success, EOF on error
+ * Returns 0 on success, EOF if failed to enlarge buffer
*/
-static int
-checkOutBufferSpace(int bytes_needed, PGconn *conn)
+int
+pqCheckOutBufferSpace(size_t bytes_needed, PGconn *conn)
{
int newsize = conn->outBufSize;
char *newbuf;
- if (bytes_needed <= newsize)
+ /* Quick exit if we have enough space */
+ if (bytes_needed <= (size_t) newsize)
return 0;
+
/*
- * If we need to enlarge the buffer, we first try to double it in size;
- * if that doesn't work, enlarge in multiples of 8K. This avoids
- * thrashing the malloc pool by repeated small enlargements.
+ * If we need to enlarge the buffer, we first try to double it in size; if
+ * that doesn't work, enlarge in multiples of 8K. This avoids thrashing
+ * the malloc pool by repeated small enlargements.
*
* Note: tests for newsize > 0 are to catch integer overflow.
*/
- do {
+ do
+ {
newsize *= 2;
- } while (bytes_needed > newsize && newsize > 0);
+ } while (newsize > 0 && bytes_needed > (size_t) newsize);
- if (bytes_needed <= newsize)
+ if (newsize > 0 && bytes_needed <= (size_t) newsize)
{
newbuf = realloc(conn->outBuffer, newsize);
if (newbuf)
}
newsize = conn->outBufSize;
- do {
+ do
+ {
newsize += 8192;
- } while (bytes_needed > newsize && newsize > 0);
+ } while (newsize > 0 && bytes_needed > (size_t) newsize);
- if (bytes_needed <= newsize)
+ if (newsize > 0 && bytes_needed <= (size_t) newsize)
{
newbuf = realloc(conn->outBuffer, newsize);
if (newbuf)
return EOF;
}
+/*
+ * Make sure conn's input buffer can hold bytes_needed bytes (caller must
+ * include already-stored data into the value!)
+ *
+ * Returns 0 on success, EOF if failed to enlarge buffer
+ */
+int
+pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
+{
+ int newsize = conn->inBufSize;
+ char *newbuf;
+
+ /* Quick exit if we have enough space */
+ if (bytes_needed <= (size_t) newsize)
+ return 0;
+
+ /*
+ * Before concluding that we need to enlarge the buffer, left-justify
+ * whatever is in it and recheck. The caller's value of bytes_needed
+ * includes any data to the left of inStart, but we can delete that in
+ * preference to enlarging the buffer. It's slightly ugly to have this
+ * function do this, but it's better than making callers worry about it.
+ */
+ bytes_needed -= conn->inStart;
+
+ if (conn->inStart < conn->inEnd)
+ {
+ if (conn->inStart > 0)
+ {
+ memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
+ conn->inEnd - conn->inStart);
+ conn->inEnd -= conn->inStart;
+ conn->inCursor -= conn->inStart;
+ conn->inStart = 0;
+ }
+ }
+ else
+ {
+ /* buffer is logically empty, reset it */
+ conn->inStart = conn->inCursor = conn->inEnd = 0;
+ }
+
+ /* Recheck whether we have enough space */
+ if (bytes_needed <= (size_t) newsize)
+ return 0;
+
+ /*
+ * If we need to enlarge the buffer, we first try to double it in size; if
+ * that doesn't work, enlarge in multiples of 8K. This avoids thrashing
+ * the malloc pool by repeated small enlargements.
+ *
+ * Note: tests for newsize > 0 are to catch integer overflow.
+ */
+ do
+ {
+ newsize *= 2;
+ } while (newsize > 0 && bytes_needed > (size_t) newsize);
+
+ if (newsize > 0 && bytes_needed <= (size_t) newsize)
+ {
+ newbuf = realloc(conn->inBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->inBuffer = newbuf;
+ conn->inBufSize = newsize;
+ return 0;
+ }
+ }
+
+ newsize = conn->inBufSize;
+ do
+ {
+ newsize += 8192;
+ } while (newsize > 0 && bytes_needed > (size_t) newsize);
+
+ if (newsize > 0 && bytes_needed <= (size_t) newsize)
+ {
+ newbuf = realloc(conn->inBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->inBuffer = newbuf;
+ conn->inBufSize = newsize;
+ return 0;
+ }
+ }
+
+ /* realloc failed. Probably out of memory */
+ printfPQExpBuffer(&conn->errorMessage,
+ "cannot allocate memory for input buffer\n");
+ return EOF;
+}
+
/*
* pqPutMsgStart: begin construction of a message to the server
*
* msg_type is the message type byte, or 0 for a message without type byte
* (only startup messages have no type byte)
*
+ * force_len forces the message to have a length word; otherwise, we add
+ * a length word if protocol 3.
+ *
* Returns 0 on success, EOF on error
*
* The idea here is that we construct the message in conn->outBuffer,
* beginning just past any data already in outBuffer (ie, at
* outBuffer+outCount). We enlarge the buffer as needed to hold the message.
- * When the message is complete, we fill in the length word and then advance
- * outCount past the message, making it eligible to send. The state
- * variable conn->outMsgStart points to the incomplete message's length word
- * (it is either outCount or outCount+1 depending on whether there is a
- * type byte). The state variable conn->outMsgEnd is the end of the data
- * collected so far.
+ * When the message is complete, we fill in the length word (if needed) and
+ * then advance outCount past the message, making it eligible to send.
+ *
+ * The state variable conn->outMsgStart points to the incomplete message's
+ * length word: it is either outCount or outCount+1 depending on whether
+ * there is a type byte. If we are sending a message without length word
+ * (pre protocol 3.0 only), then outMsgStart is -1. The state variable
+ * conn->outMsgEnd is the end of the data collected so far.
*/
int
-pqPutMsgStart(char msg_type, PGconn *conn)
+pqPutMsgStart(char msg_type, bool force_len, PGconn *conn)
{
int lenPos;
+ int endPos;
- /* where the message length word will go */
+ /* allow room for message type byte */
if (msg_type)
- lenPos = conn->outCount + 1;
+ endPos = conn->outCount + 1;
else
- lenPos = conn->outCount;
- /* make sure there is room for it */
- if (checkOutBufferSpace(lenPos + 4, conn))
+ endPos = conn->outCount;
+
+ /* do we want a length word? */
+ if (force_len || PG_PROTOCOL_MAJOR(conn->pversion) >= 3)
+ {
+ lenPos = endPos;
+ /* allow room for message length */
+ endPos += 4;
+ }
+ else
+ lenPos = -1;
+
+ /* make sure there is room for message header */
+ if (pqCheckOutBufferSpace(endPos, conn))
return EOF;
/* okay, save the message type byte if any */
if (msg_type)
conn->outBuffer[conn->outCount] = msg_type;
/* set up the message pointers */
conn->outMsgStart = lenPos;
- conn->outMsgEnd = lenPos + 4;
- /* length word will be filled in by pqPutMsgEnd */
+ conn->outMsgEnd = endPos;
+ /* length word, if needed, will be filled in by pqPutMsgEnd */
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg %c\n",
pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
{
/* make sure there is room for it */
- if (checkOutBufferSpace(conn->outMsgEnd + len, conn))
+ if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn))
return EOF;
/* okay, save the data */
memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
int
pqPutMsgEnd(PGconn *conn)
{
- uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
-
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
- msgLen);
+ conn->outMsgEnd - conn->outCount);
+
+ /* Fill in length word if needed */
+ if (conn->outMsgStart >= 0)
+ {
+ uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+ msgLen = pg_hton32(msgLen);
+ memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
+ }
- msgLen = htonl(msgLen);
- memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
+ /* Make message eligible to send */
conn->outCount = conn->outMsgEnd;
if (conn->outCount >= 8192)
{
- int toSend = conn->outCount - (conn->outCount % 8192);
+ int toSend = conn->outCount - (conn->outCount % 8192);
if (pqSendSome(conn, toSend) < 0)
return EOF;
int someread = 0;
int nread;
- if (conn->sock < 0)
+ if (conn->sock == PGINVALID_SOCKET)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("connection not open\n"));
}
/*
- * If the buffer is fairly full, enlarge it. We need to be able to
- * enlarge the buffer in case a single message exceeds the initial
- * buffer size. We enlarge before filling the buffer entirely so as
- * to avoid asking the kernel for a partial packet. The magic constant
- * here should be large enough for a TCP packet or Unix pipe
- * bufferload. 8K is the usual pipe buffer size, so...
+ * If the buffer is fairly full, enlarge it. We need to be able to enlarge
+ * the buffer in case a single message exceeds the initial buffer size. We
+ * enlarge before filling the buffer entirely so as to avoid asking the
+ * kernel for a partial packet. The magic constant here should be large
+ * enough for a TCP packet or Unix pipe bufferload. 8K is the usual pipe
+ * buffer size, so...
*/
if (conn->inBufSize - conn->inEnd < 8192)
{
- int newSize = conn->inBufSize * 2;
- char *newBuf = (char *) realloc(conn->inBuffer, newSize);
-
- if (newBuf)
+ if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn))
{
- conn->inBuffer = newBuf;
- conn->inBufSize = newSize;
+ /*
+ * We don't insist that the enlarge worked, but we need some room
+ */
+ if (conn->inBufSize - conn->inEnd < 100)
+ return -1; /* errorMessage already set */
}
}
if (SOCK_ERRNO == ECONNRESET)
goto definitelyFailed;
#endif
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not receive data from server: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO));
+ /* pqsecure_read set the error message for us */
return -1;
}
if (nread > 0)
conn->inEnd += nread;
/*
- * Hack to deal with the fact that some kernels will only give us
- * back 1 packet per recv() call, even if we asked for more and
- * there is more available. If it looks like we are reading a
- * long message, loop back to recv() again immediately, until we
- * run out of data or buffer space. Without this, the
- * block-and-restart behavior of libpq's higher levels leads to
- * O(N^2) performance on long messages.
+ * Hack to deal with the fact that some kernels will only give us back
+ * 1 packet per recv() call, even if we asked for more and there is
+ * more available. If it looks like we are reading a long message,
+ * loop back to recv() again immediately, until we run out of data or
+ * buffer space. Without this, the block-and-restart behavior of
+ * libpq's higher levels leads to O(N^2) performance on long messages.
*
* Since we left-justified the data above, conn->inEnd gives the
- * amount of data already read in the current message. We
- * consider the message "long" once we have acquired 32k ...
+ * amount of data already read in the current message. We consider
+ * the message "long" once we have acquired 32k ...
*/
if (conn->inEnd > 32768 &&
(conn->inBufSize - conn->inEnd) >= 8192)
return 1; /* got a zero read after successful tries */
/*
- * A return value of 0 could mean just that no data is now available,
- * or it could mean EOF --- that is, the server has closed the
- * connection. Since we have the socket in nonblock mode, the only way
- * to tell the difference is to see if select() is saying that the
- * file is ready. Grumble. Fortunately, we don't expect this path to
- * be taken much, since in normal practice we should not be trying to
- * read data unless the file selected for reading already.
+ * A return value of 0 could mean just that no data is now available, or
+ * it could mean EOF --- that is, the server has closed the connection.
+ * Since we have the socket in nonblock mode, the only way to tell the
+ * difference is to see if select() is saying that the file is ready.
+ * Grumble. Fortunately, we don't expect this path to be taken much,
+ * since in normal practice we should not be trying to read data unless
+ * the file selected for reading already.
+ *
+ * In SSL mode it's even worse: SSL_read() could say WANT_READ and then
+ * data could arrive before we make the pqReadReady() test, but the second
+ * SSL_read() could still say WANT_READ because the data received was not
+ * a complete SSL record. So we must play dumb and assume there is more
+ * data, relying on the SSL layer to detect true EOF.
*/
+
+#ifdef USE_SSL
+ if (conn->ssl_in_use)
+ return 0;
+#endif
+
switch (pqReadReady(conn))
{
case 0:
/* ready for read */
break;
default:
- goto definitelyFailed;
+ /* we override pqReadReady's message with something more useful */
+ goto definitelyEOF;
}
/*
if (SOCK_ERRNO == ECONNRESET)
goto definitelyFailed;
#endif
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not receive data from server: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO));
+ /* pqsecure_read set the error message for us */
return -1;
}
if (nread > 0)
}
/*
- * OK, we are getting a zero read even though select() says ready.
- * This means the connection has been closed. Cope.
+ * OK, we are getting a zero read even though select() says ready. This
+ * means the connection has been closed. Cope.
*/
-definitelyFailed:
+definitelyEOF:
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
- conn->status = CONNECTION_BAD; /* No more connection to backend */
- pqsecure_close(conn);
-#ifdef WIN32
- closesocket(conn->sock);
-#else
- close(conn->sock);
-#endif
- conn->sock = -1;
+ "server closed the connection unexpectedly\n"
+ "\tThis probably means the server terminated abnormally\n"
+ "\tbefore or while processing the request.\n"));
+ /* Come here if lower-level code already set a suitable errorMessage */
+definitelyFailed:
+ /* Do *not* drop any already-read data; caller still wants it */
+ pqDropConnection(conn, false);
+ conn->status = CONNECTION_BAD; /* No more connection to backend */
return -1;
}
*
* 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)
int remaining = conn->outCount;
int result = 0;
- if (conn->sock < 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"));
- return -1;
+ 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 0;
}
/* while there's still data to send */
{
int sent;
+#ifndef WIN32
sent = pqsecure_write(conn, ptr, len);
+#else
+
+ /*
+ * Windows can fail on large sends, per KB article Q201213. The
+ * failure-point appears to be different in different versions of
+ * Windows, but 64k should always be safe.
+ */
+ sent = pqsecure_write(conn, ptr, Min(len, 65536));
+#endif
if (sent < 0)
{
- /*
- * Anything except EAGAIN or EWOULDBLOCK is trouble. If it's
- * EPIPE or ECONNRESET, assume we've lost the backend
- * connection permanently.
- */
+ /* Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble */
switch (SOCK_ERRNO)
{
#ifdef EAGAIN
case EINTR:
continue;
- case EPIPE:
-#ifdef ECONNRESET
- case ECONNRESET:
-#endif
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext(
- "server closed the connection unexpectedly\n"
- "\tThis probably means the server terminated abnormally\n"
- "\tbefore or while processing the request.\n"));
+ 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->outCount = 0;
- return -1;
+ conn->write_err_msg = strdup(conn->errorMessage.data);
+ resetPQExpBuffer(&conn->errorMessage);
- default:
- printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("could not send data to server: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO));
- /* We don't assume it's a fatal error... */
+ /* Discard queued data; no chance it'll ever be sent */
conn->outCount = 0;
- return -1;
+ return 0;
}
}
else
if (len > 0)
{
- /* We didn't send it all, wait till we can send more */
-
/*
- * if the socket is in non-blocking mode we may need to abort
- * here and return 1 to indicate that data is still pending.
+ * We didn't send it all, wait till we can send more.
+ *
+ * There are scenarios in which we can't send data because the
+ * communications channel is full, but we cannot expect the server
+ * to clear the channel eventually because it's blocked trying to
+ * send data to us. (This can happen when we are sending a large
+ * amount of COPY data, and the server has generated lots of
+ * NOTICE responses.) To avoid a deadlock situation, we must be
+ * prepared to accept and buffer incoming data before we try
+ * again. Furthermore, it is possible that such incoming data
+ * might not arrive until after we've gone to sleep. Therefore,
+ * we wait for either read ready or write ready.
+ *
+ * In non-blocking mode, we don't wait here directly, but return 1
+ * to indicate that data is still pending. The caller should wait
+ * for both read and write ready conditions, and call
+ * PQconsumeInput() on read ready, but just in case it doesn't, we
+ * call pqReadData() ourselves before returning. That's not
+ * enough if the data has not arrived yet, but it's the best we
+ * 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.
*/
-#ifdef USE_SSL
- /* can't do anything for our SSL users yet */
- if (conn->ssl == NULL)
+ if (pqReadData(conn) < 0)
{
-#endif
- if (pqIsnonblocking(conn))
- {
- result = 1;
- break;
- }
-#ifdef USE_SSL
+ result = -1; /* error message already set up */
+ break;
}
-#endif
- if (pqWait(FALSE, TRUE, conn))
+ if (pqIsnonblocking(conn))
+ {
+ result = 1;
+ break;
+ }
+
+ if (pqWait(true, true, conn))
{
result = -1;
break;
*
* 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)
/*
* pqWaitTimed: wait, but not past finish_time.
*
- * If finish_time is exceeded then we return failure (EOF). This is like
- * the response for a kernel exception because we don't want the caller
- * to try to read/write in that case.
- *
* finish_time = ((time_t) -1) disables the wait limit.
+ *
+ * Returns -1 on failure, 0 if the socket is readable/writable, 1 if it timed out.
*/
int
pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
{
- int result;
+ int result;
result = pqSocketCheck(conn, forRead, forWrite, finish_time);
if (result < 0)
- return EOF; /* errorMessage is already set */
+ return -1; /* errorMessage is already set */
if (result == 0)
{
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("timeout expired\n"));
- return EOF;
+ return 1;
}
return 0;
static int
pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
{
- int result;
+ int result;
if (!conn)
return -1;
- if (conn->sock < 0)
+ if (conn->sock == PGINVALID_SOCKET)
{
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("socket not open\n"));
+ libpq_gettext("invalid socket\n"));
return -1;
}
#ifdef USE_SSL
/* Check for SSL library buffering read bytes */
- if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
+ if (forRead && conn->ssl_in_use && pgtls_read_pending(conn))
{
/* short-circuit the select */
return 1;
/* We will retry as long as we get EINTR */
do
- {
result = pqSocketPoll(conn->sock, forRead, forWrite, end_time);
- }
while (result < 0 && SOCK_ERRNO == EINTR);
if (result < 0)
{
+ char sebuf[PG_STRERROR_R_BUFLEN];
+
printfPQExpBuffer(&conn->errorMessage,
- libpq_gettext("select() failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO));
+ libpq_gettext("select() failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
}
return result;
/* We use poll(2) if available, otherwise select(2) */
#ifdef HAVE_POLL
struct pollfd input_fd;
- int timeout_ms;
+ int timeout_ms;
if (!forRead && !forWrite)
return 0;
- input_fd.fd = sock;
- input_fd.events = POLLERR;
+ input_fd.fd = sock;
+ input_fd.events = POLLERR;
input_fd.revents = 0;
if (forRead)
/* Compute appropriate timeout interval */
if (end_time == ((time_t) -1))
- {
timeout_ms = -1;
- }
else
{
- time_t now = time(NULL);
+ time_t now = time(NULL);
if (end_time > now)
timeout_ms = (end_time - now) * 1000;
}
return poll(&input_fd, 1, timeout_ms);
+#else /* !HAVE_POLL */
-#else /* !HAVE_POLL */
-
- fd_set input_mask;
- fd_set output_mask;
- fd_set except_mask;
- struct timeval timeout;
+ fd_set input_mask;
+ fd_set output_mask;
+ fd_set except_mask;
+ struct timeval timeout;
struct timeval *ptr_timeout;
if (!forRead && !forWrite)
FD_ZERO(&except_mask);
if (forRead)
FD_SET(sock, &input_mask);
+
if (forWrite)
FD_SET(sock, &output_mask);
FD_SET(sock, &except_mask);
/* Compute appropriate timeout interval */
if (end_time == ((time_t) -1))
- {
ptr_timeout = NULL;
- }
else
{
- time_t now = time(NULL);
+ time_t now = time(NULL);
if (end_time > now)
timeout.tv_sec = end_time - now;
return select(sock + 1, &input_mask, &output_mask,
&except_mask, ptr_timeout);
-#endif /* HAVE_POLL */
+#endif /* HAVE_POLL */
}
*/
/*
- * returns the byte length of the word beginning s, using the
+ * returns the byte length of the character beginning at s, using the
+ * specified encoding.
+ */
+int
+PQmblen(const char *s, int encoding)
+{
+ return pg_encoding_mblen(encoding, s);
+}
+
+/*
+ * returns the display length of the character beginning at s, using the
* specified encoding.
*/
int
-PQmblen(const unsigned char *s, int encoding)
+PQdsplen(const char *s, int encoding)
{
- return (pg_encoding_mblen(encoding, s));
+ return pg_encoding_dsplen(encoding, s);
}
/*
str = getenv("PGCLIENTENCODING");
if (str && *str != '\0')
+ {
encoding = pg_char_to_encoding(str);
- return (encoding);
+ if (encoding < 0)
+ encoding = PG_SQL_ASCII;
+ }
+ return encoding;
}
#ifdef ENABLE_NLS
-char *
-libpq_gettext(const char *msgid)
+static void
+libpq_binddomain()
{
- static int already_bound = 0;
+ static bool already_bound = false;
if (!already_bound)
{
- already_bound = 1;
- bindtextdomain("libpq", LOCALEDIR);
+ /* bindtextdomain() does not preserve errno */
+#ifdef WIN32
+ int save_errno = GetLastError();
+#else
+ int save_errno = errno;
+#endif
+ const char *ldir;
+
+ already_bound = true;
+ /* No relocatable lookup here because the binary could be anywhere */
+ ldir = getenv("PGLOCALEDIR");
+ if (!ldir)
+ ldir = LOCALEDIR;
+ bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir);
+#ifdef WIN32
+ SetLastError(save_errno);
+#else
+ errno = save_errno;
+#endif
}
+}
- return dgettext("libpq", msgid);
+char *
+libpq_gettext(const char *msgid)
+{
+ libpq_binddomain();
+ return dgettext(PG_TEXTDOMAIN("libpq"), msgid);
+}
+
+char *
+libpq_ngettext(const char *msgid, const char *msgid_plural, unsigned long n)
+{
+ libpq_binddomain();
+ return dngettext(PG_TEXTDOMAIN("libpq"), msgid, msgid_plural, n);
}
-#endif /* ENABLE_NLS */
+#endif /* ENABLE_NLS */