* 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-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.118 2005/08/23 20:48:47 momjian Exp $
+ * src/interfaces/libpq/fe-misc.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
-#include <errno.h>
#include <signal.h>
#include <time.h>
-#ifndef WIN32_CLIENT_ONLY
-#include <netinet/in.h>
-#include <arpa/inet.h>
-#endif
-
#ifdef WIN32
#include "win32.h"
#else
#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"
-
+#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);
-static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
- time_t end_time);
+static int pqSocketCheck(PGconn *conn, int forRead, int forWrite,
+ 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
*result = conn->inBuffer[conn->inCursor++];
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("From backend> %c\n"), *result);
+ fprintf(conn->Pfdebug, "From backend> %c\n", *result);
return 0;
}
return EOF;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("To backend> %c\n"), c);
+ fprintf(conn->Pfdebug, "To backend> %c\n", c);
return 0;
}
/*
- * 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;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("From backend> \"%s\"\n"),
+ fprintf(conn->Pfdebug, "From backend> \"%s\"\n",
buf->data);
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, libpq_gettext("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, libpq_gettext("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, libpq_gettext("To backend> %.*s\n"), (int) len, s);
+ {
+ fprintf(conn->Pfdebug, "To backend> ");
+ fputnbytes(conn->Pfdebug, s, len);
+ fprintf(conn->Pfdebug, "\n");
+ }
return 0;
}
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:
pqInternalNotice(&conn->noticeHooks,
- "integer of size %lu not supported by pqGetInt",
+ "integer of size %lu not supported by pqGetInt",
(unsigned long) bytes);
return EOF;
}
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("From backend (#%lu)> %d\n"), (unsigned long) bytes, *result);
+ fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result);
return 0;
}
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:
pqInternalNotice(&conn->noticeHooks,
- "integer of size %lu not supported by pqPutInt",
+ "integer of size %lu not supported by pqPutInt",
(unsigned long) bytes);
return EOF;
}
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("To backend (%lu#)> %d\n"), (unsigned long) bytes, value);
+ fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value);
return 0;
}
* Returns 0 on success, EOF if failed to enlarge buffer
*/
int
-pqCheckOutBufferSpace(int bytes_needed, PGconn *conn)
+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
{
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)
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)
* Returns 0 on success, EOF if failed to enlarge buffer
*/
int
-pqCheckInBufferSpace(int bytes_needed, PGconn *conn)
+pqCheckInBufferSpace(size_t bytes_needed, PGconn *conn)
{
int newsize = conn->inBufSize;
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.
+ * 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 (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->inBuffer, newsize);
if (newbuf)
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->inBuffer, newsize);
if (newbuf)
/* length word, if needed, will be filled in by pqPutMsgEnd */
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("To backend> Msg %c\n"),
+ fprintf(conn->Pfdebug, "To backend> Msg %c\n",
msg_type ? msg_type : ' ');
return 0;
pqPutMsgEnd(PGconn *conn)
{
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, libpq_gettext("To backend> Msg complete, length %u\n"),
+ fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
conn->outMsgEnd - conn->outCount);
/* Fill in length word if needed */
{
uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
- msgLen = htonl(msgLen);
+ msgLen = pg_hton32(msgLen);
memcpy(conn->outBuffer + conn->outMsgStart, &msgLen, 4);
}
{
int someread = 0;
int nread;
- char sebuf[256];
- 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)
{
- if (pqCheckInBufferSpace(conn->inEnd + 8192, conn))
+ if (pqCheckInBufferSpace(conn->inEnd + (size_t) 8192, conn))
{
/*
- * We don't insist that the enlarge worked, but we need some
- * room
+ * 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, sebuf, sizeof(sebuf)));
+ /* 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. So we
- * must play dumb and assume there is more data, relying on the SSL
- * layer to detect true EOF.
+ * 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)
+ if (conn->ssl_in_use)
return 0;
#endif
/* 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, sebuf, sizeof(sebuf)));
+ /* 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);
- closesocket(conn->sock);
- 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 */
while (len > 0)
{
int sent;
- char sebuf[256];
+#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/EWOULDBLOCK/EINTR 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, sebuf, sizeof(sebuf)));
- /* 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
/*
* We didn't send it all, wait till we can send more.
*
- * If the connection is in non-blocking mode we don't wait, but
- * return 1 to indicate that data is still pending.
+ * 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.
*/
- if (pqIsnonblocking(conn))
+ if (pqReadData(conn) < 0)
{
- result = 1;
+ result = -1; /* error message already set up */
break;
}
- /*
- * 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.
- */
- if (pqReadData(conn) < 0)
+ if (pqIsnonblocking(conn))
{
- result = -1; /* error message already set up */
+ result = 1;
break;
}
- if (pqWait(TRUE, TRUE, conn))
+
+ 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)
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;
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;
if (result < 0)
{
- char sebuf[256];
+ char sebuf[PG_STRERROR_R_BUFLEN];
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("select() failed: %s\n"),
- SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
}
return result;
}
return poll(&input_fd, 1, timeout_ms);
-
#else /* !HAVE_POLL */
fd_set input_mask;
FD_ZERO(&except_mask);
if (forRead)
FD_SET(sock, &input_mask);
+
if (forWrite)
FD_SET(sock, &output_mask);
FD_SET(sock, &except_mask);
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 unsigned char *s, int encoding)
+PQmblen(const char *s, int encoding)
{
- return (pg_encoding_mblen(encoding, s));
+ return pg_encoding_mblen(encoding, s);
}
/*
- * returns the display length of the word beginning s, using the
+ * returns the display length of the character beginning at s, using the
* specified encoding.
*/
int
-PQdsplen(const unsigned char *s, int encoding)
+PQdsplen(const char *s, int encoding)
{
- return (pg_encoding_dsplen(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 bool already_bound = false;
if (!already_bound)
{
- /* dgettext() preserves errno, but bindtextdomain() doesn't */
- int save_errno = errno;
+ /* bindtextdomain() does not preserve errno */
+#ifdef WIN32
+ int save_errno = GetLastError();
+#else
+ int save_errno = errno;
+#endif
const char *ldir;
already_bound = true;
ldir = getenv("PGLOCALEDIR");
if (!ldir)
ldir = LOCALEDIR;
- bindtextdomain("libpq", ldir);
+ bindtextdomain(PG_TEXTDOMAIN("libpq"), ldir);
#ifdef WIN32
SetLastError(save_errno);
#else
errno = save_errno;
#endif
}
+}
+
+char *
+libpq_gettext(const char *msgid)
+{
+ libpq_binddomain();
+ return dgettext(PG_TEXTDOMAIN("libpq"), msgid);
+}
- return dgettext("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 */