* will cause repeat printouts.
*
* We must speak the same transmitted data representations as the backend
- * routines. Note that this module supports *only* network byte order
- * for transmitted ints, whereas the backend modules (as of this writing)
- * still handle either network or little-endian byte order.
+ * routines.
*
- * Copyright (c) 1994, Regents of the University of California
*
+ * Portions Copyright (c) 1996-2004, 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.28 1999/07/19 06:25:40 momjian Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-misc.c,v 1.109 2004/08/29 04:13:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
-#include <sys/time.h>
+#include "postgres_fe.h"
+
#include <errno.h>
#include <signal.h>
#include <time.h>
-#include "postgres.h"
-#include "libpq-fe.h"
-#include "libpq-int.h"
-#include "pqsignal.h"
+#if !defined(_MSC_VER) && !defined(__BORLANDC__)
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
#ifdef WIN32
#include "win32.h"
#else
#include <unistd.h>
+#include <sys/time.h>
#endif
+#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)))
+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 pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time);
-/* --------------------------------------------------------------------- */
-/* pqGetc:
- get a character from the connection
- All these routines return 0 on success, EOF on error.
- Note that for the Get routines, EOF only means there is not enough
- data in the buffer, not that there is necessarily a hard error.
-*/
+/*
+ * pqGetc: get 1 character from the connection
+ *
+ * All these routines return 0 on success, EOF on error.
+ * Note that for the Get routines, EOF only means there is not enough
+ * data in the buffer, not that there is necessarily a hard error.
+ */
int
pqGetc(char *result, PGconn *conn)
{
}
-/* --------------------------------------------------------------------- */
-/* pqPutBytes: local routine to write N bytes to the connection,
- with buffering
+/*
+ * pqPutc: write 1 char to the current message
*/
-static int
-pqPutBytes(const char *s, int nbytes, PGconn *conn)
+int
+pqPutc(char c, PGconn *conn)
{
- int avail = conn->outBufSize - conn->outCount;
-
- while (nbytes > avail)
- {
- memcpy(conn->outBuffer + conn->outCount, s, avail);
- conn->outCount += avail;
- s += avail;
- nbytes -= avail;
- if (pqFlush(conn))
- return EOF;
- avail = conn->outBufSize;
- }
+ if (pqPutMsgBytes(&c, 1, conn))
+ return EOF;
- memcpy(conn->outBuffer + conn->outCount, s, nbytes);
- conn->outCount += nbytes;
+ if (conn->Pfdebug)
+ fprintf(conn->Pfdebug, "To backend> %c\n", c);
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pqGets:
- get a null-terminated string from the connection,
- and store it in a buffer of size maxlen bytes.
- If the incoming string is >= maxlen bytes, all of it is read,
- but the excess characters are silently discarded.
-*/
+
+/*
+ * pqGets:
+ * 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(char *s, int maxlen, PGconn *conn)
+pqGets(PQExpBuffer buf, PGconn *conn)
{
/* Copy conn data to locals for faster search loop */
char *inBuffer = conn->inBuffer;
return EOF;
slen = inCursor - conn->inCursor;
- if (slen < maxlen)
- strcpy(s, inBuffer + conn->inCursor);
- else
- {
- strncpy(s, inBuffer + conn->inCursor, maxlen - 1);
- s[maxlen - 1] = '\0';
- }
+
+ resetPQExpBuffer(buf);
+ appendBinaryPQExpBuffer(buf, inBuffer + conn->inCursor, slen);
conn->inCursor = ++inCursor;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend> \"%s\"\n", s);
+ fprintf(conn->Pfdebug, "From backend> \"%s\"\n",
+ buf->data);
return 0;
}
-/* --------------------------------------------------------------------- */
+
+/*
+ * pqPuts: write a null-terminated string to the current message
+ */
int
pqPuts(const char *s, PGconn *conn)
{
- if (pqPutBytes(s, strlen(s) + 1, conn))
+ if (pqPutMsgBytes(s, strlen(s) + 1, conn))
return EOF;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %s\n", s);
+ fprintf(conn->Pfdebug, "To backend> '%s'\n", s);
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pqGetnchar:
- get a string of exactly len bytes in buffer s, no null termination
-*/
+/*
+ * pqGetnchar:
+ * get a string of exactly len bytes in buffer s, no null termination
+ */
int
-pqGetnchar(char *s, int len, PGconn *conn)
+pqGetnchar(char *s, size_t len, PGconn *conn)
{
- if (len < 0 || len > conn->inEnd - conn->inCursor)
+ if (len < 0 || 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 (%d)> %.*s\n", len, len, s);
+ fprintf(conn->Pfdebug, "From backend (%lu)> %.*s\n", (unsigned long) len, (int) len, s);
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pqPutnchar:
- send a string of exactly len bytes, no null termination needed
-*/
+/*
+ * pqPutnchar:
+ * write exactly len bytes to the current message
+ */
int
-pqPutnchar(const char *s, int len, PGconn *conn)
+pqPutnchar(const char *s, size_t len, PGconn *conn)
{
- if (pqPutBytes(s, len, conn))
+ if (pqPutMsgBytes(s, len, conn))
return EOF;
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend> %.*s\n", len, s);
+ fprintf(conn->Pfdebug, "To backend> %.*s\n", (int) len, s);
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pgGetInt
- read a 2 or 4 byte integer and convert from network byte order
- to local byte order
-*/
+/*
+ * pqGetInt
+ * read a 2 or 4 byte integer and convert from network byte order
+ * to local byte order
+ */
int
-pqGetInt(int *result, int bytes, PGconn *conn)
+pqGetInt(int *result, size_t bytes, PGconn *conn)
{
uint16 tmp2;
uint32 tmp4;
*result = (int) ntohl(tmp4);
break;
default:
- sprintf(conn->errorMessage,
- "pqGetInt: int size %d not supported\n", bytes);
- DONOTICE(conn, conn->errorMessage);
+ pqInternalNotice(&conn->noticeHooks,
+ "integer of size %lu not supported by pqGetInt",
+ (unsigned long) bytes);
return EOF;
}
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "From backend (#%d)> %d\n", bytes, *result);
+ fprintf(conn->Pfdebug, "From backend (#%lu)> %d\n", (unsigned long) bytes, *result);
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pgPutInt
- send an integer of 2 or 4 bytes, converting from host byte order
- to network byte order.
-*/
+/*
+ * pqPutInt
+ * write an integer of 2 or 4 bytes, converting from host byte order
+ * to network byte order.
+ */
int
-pqPutInt(int value, int bytes, PGconn *conn)
+pqPutInt(int value, size_t bytes, PGconn *conn)
{
uint16 tmp2;
uint32 tmp4;
{
case 2:
tmp2 = htons((uint16) value);
- if (pqPutBytes((const char *) &tmp2, 2, conn))
+ if (pqPutMsgBytes((const char *) &tmp2, 2, conn))
return EOF;
break;
case 4:
tmp4 = htonl((uint32) value);
- if (pqPutBytes((const char *) &tmp4, 4, conn))
+ if (pqPutMsgBytes((const char *) &tmp4, 4, conn))
return EOF;
break;
default:
- sprintf(conn->errorMessage,
- "pqPutInt: int size %d not supported\n", bytes);
- DONOTICE(conn, conn->errorMessage);
+ pqInternalNotice(&conn->noticeHooks,
+ "integer of size %lu not supported by pqPutInt",
+ (unsigned long) bytes);
return EOF;
}
if (conn->Pfdebug)
- fprintf(conn->Pfdebug, "To backend (%d#)> %d\n", bytes, value);
+ fprintf(conn->Pfdebug, "To backend (%lu#)> %d\n", (unsigned long) bytes, value);
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pqReadReady: is select() saying the file is ready to read?
+/*
+ * Make sure conn's output 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
*/
-static int
-pqReadReady(PGconn *conn)
+int
+pqCheckOutBufferSpace(int bytes_needed, PGconn *conn)
{
- fd_set input_mask;
- struct timeval timeout;
+ int newsize = conn->outBufSize;
+ char *newbuf;
- if (conn->sock < 0)
+ if (bytes_needed <= newsize)
return 0;
- FD_ZERO(&input_mask);
- FD_SET(conn->sock, &input_mask);
- timeout.tv_sec = 0;
- timeout.tv_usec = 0;
- if (select(conn->sock + 1, &input_mask, (fd_set *) NULL, (fd_set *) NULL,
- &timeout) < 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
{
- sprintf(conn->errorMessage,
- "pqReadReady() -- select() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ newsize *= 2;
+ } while (bytes_needed > newsize && newsize > 0);
+
+ if (bytes_needed <= newsize)
+ {
+ newbuf = realloc(conn->outBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->outBuffer = newbuf;
+ conn->outBufSize = newsize;
+ return 0;
+ }
+ }
+
+ newsize = conn->outBufSize;
+ do
+ {
+ newsize += 8192;
+ } while (bytes_needed > newsize && newsize > 0);
+
+ if (bytes_needed <= newsize)
+ {
+ newbuf = realloc(conn->outBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->outBuffer = newbuf;
+ conn->outBufSize = newsize;
+ return 0;
+ }
+ }
+
+ /* realloc failed. Probably out of memory */
+ printfPQExpBuffer(&conn->errorMessage,
+ "cannot allocate memory for output buffer\n");
+ 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(int bytes_needed, PGconn *conn)
+{
+ int newsize = conn->inBufSize;
+ char *newbuf;
+
+ if (bytes_needed <= 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);
+
+ if (bytes_needed <= newsize)
+ {
+ newbuf = realloc(conn->inBuffer, newsize);
+ if (newbuf)
+ {
+ /* realloc succeeded */
+ conn->inBuffer = newbuf;
+ conn->inBufSize = newsize;
+ return 0;
+ }
}
- return FD_ISSET(conn->sock, &input_mask);
+
+ newsize = conn->inBufSize;
+ do
+ {
+ newsize += 8192;
+ } while (bytes_needed > newsize && newsize > 0);
+
+ if (bytes_needed <= 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 (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, bool force_len, PGconn *conn)
+{
+ int lenPos;
+ int endPos;
+
+ /* allow room for message type byte */
+ if (msg_type)
+ endPos = conn->outCount + 1;
+ else
+ 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 = endPos;
+ /* length word, if needed, will be filled in by pqPutMsgEnd */
+
+ if (conn->Pfdebug)
+ fprintf(conn->Pfdebug, "To backend> Msg %c\n",
+ msg_type ? msg_type : ' ');
+
+ return 0;
+}
+
+/*
+ * pqPutMsgBytes: add bytes to a partially-constructed message
+ *
+ * Returns 0 on success, EOF on error
+ */
+static int
+pqPutMsgBytes(const void *buf, size_t len, PGconn *conn)
+{
+ /* make sure there is room for it */
+ if (pqCheckOutBufferSpace(conn->outMsgEnd + len, conn))
+ return EOF;
+ /* okay, save the data */
+ memcpy(conn->outBuffer + conn->outMsgEnd, buf, len);
+ conn->outMsgEnd += len;
+ /* no Pfdebug call here, caller should do it */
+ return 0;
+}
+
+/*
+ * pqPutMsgEnd: finish constructing a message and possibly send it
+ *
+ * Returns 0 on success, EOF on error
+ *
+ * We don't actually send anything here unless we've accumulated at least
+ * 8K worth of data (the typical size of a pipe buffer on Unix systems).
+ * This avoids sending small partial packets. The caller must use pqFlush
+ * when it's important to flush all the data out to the server.
+ */
+int
+pqPutMsgEnd(PGconn *conn)
+{
+ if (conn->Pfdebug)
+ fprintf(conn->Pfdebug, "To backend> Msg complete, length %u\n",
+ conn->outMsgEnd - conn->outCount);
+
+ /* Fill in length word if needed */
+ if (conn->outMsgStart >= 0)
+ {
+ uint32 msgLen = conn->outMsgEnd - conn->outMsgStart;
+
+ 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);
+
+ if (pqSendSome(conn, toSend) < 0)
+ return EOF;
+ /* in nonblock mode, don't complain if unable to send it all */
+ }
+
+ return 0;
}
-/* --------------------------------------------------------------------- */
-/* pqReadData: read more data, if any is available
+/* ----------
+ * pqReadData: read more data, if any is available
* Possible return values:
* 1: successfully loaded at least one more byte
* 0: no data is presently available, but no error detected
* conn->errorMessage set
* NOTE: callers must not assume that pointers or indexes into conn->inBuffer
* remain valid across this call!
+ * ----------
*/
int
pqReadData(PGconn *conn)
{
+ int someread = 0;
int nread;
+ char sebuf[256];
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqReadData() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("connection not open\n"));
return -1;
}
/* Left-justify any data in the buffer to make room */
if (conn->inStart < conn->inEnd)
{
- memmove(conn->inBuffer, conn->inBuffer + conn->inStart,
- conn->inEnd - conn->inStart);
- conn->inEnd -= conn->inStart;
- conn->inCursor -= conn->inStart;
- conn->inStart = 0;
+ 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;
+ }
/*
* 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 at least one TCP packet.
+ * 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 < 2000)
+ if (conn->inBufSize - conn->inEnd < 8192)
{
- int newSize = conn->inBufSize * 2;
- char *newBuf = (char *) realloc(conn->inBuffer, newSize);
-
- if (newBuf)
+ if (pqCheckInBufferSpace(conn->inEnd + 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 */
}
}
/* OK, try to read some data */
-tryAgain:
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
+retry3:
+ nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd);
if (nread < 0)
{
- if (errno == EINTR)
- goto tryAgain;
+ if (SOCK_ERRNO == EINTR)
+ goto retry3;
/* Some systems return EAGAIN/EWOULDBLOCK for no data */
#ifdef EAGAIN
- if (errno == EAGAIN)
- return 0;
+ if (SOCK_ERRNO == EAGAIN)
+ return someread;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- if (errno == EWOULDBLOCK)
- return 0;
+ if (SOCK_ERRNO == EWOULDBLOCK)
+ return someread;
#endif
/* We might get ECONNRESET here if using TCP and backend died */
#ifdef ECONNRESET
- if (errno == ECONNRESET)
+ if (SOCK_ERRNO == ECONNRESET)
goto definitelyFailed;
#endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not receive data from server: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
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.
+ *
+ * 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 ...
+ */
+ if (conn->inEnd > 32768 &&
+ (conn->inBufSize - conn->inEnd) >= 8192)
+ {
+ someread = 1;
+ goto retry3;
+ }
return 1;
}
+ if (someread)
+ 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
* 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.
*/
- if (!pqReadReady(conn))
- return 0; /* definitely no data available */
+
+#ifdef USE_SSL
+ if (conn->ssl)
+ return 0;
+#endif
+
+ switch (pqReadReady(conn))
+ {
+ case 0:
+ /* definitely no data available */
+ return 0;
+ case 1:
+ /* ready for read */
+ break;
+ default:
+ goto definitelyFailed;
+ }
/*
* Still not sure that it's EOF, because some data could have just
* arrived.
*/
-tryAgain2:
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
+retry4:
+ nread = pqsecure_read(conn, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd);
if (nread < 0)
{
- if (errno == EINTR)
- goto tryAgain2;
+ if (SOCK_ERRNO == EINTR)
+ goto retry4;
/* Some systems return EAGAIN/EWOULDBLOCK for no data */
#ifdef EAGAIN
- if (errno == EAGAIN)
+ if (SOCK_ERRNO == EAGAIN)
return 0;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
- if (errno == EWOULDBLOCK)
+ if (SOCK_ERRNO == EWOULDBLOCK)
return 0;
#endif
/* We might get ECONNRESET here if using TCP and backend died */
#ifdef ECONNRESET
- if (errno == ECONNRESET)
+ if (SOCK_ERRNO == ECONNRESET)
goto definitelyFailed;
#endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("could not receive data from server: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
return -1;
}
if (nread > 0)
* This means the connection has been closed. Cope.
*/
definitelyFailed:
- sprintf(conn->errorMessage,
- "pqReadData() -- backend closed the channel unexpectedly.\n"
- "\tThis probably means the backend terminated abnormally\n"
- "\tbefore or while processing the request.\n");
+ 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 */
-#ifdef WIN32
+ pqsecure_close(conn);
closesocket(conn->sock);
-#else
- close(conn->sock);
-#endif
conn->sock = -1;
return -1;
}
-/* --------------------------------------------------------------------- */
-/* pqFlush: send any data waiting in the output buffer
+/*
+ * pqSendSome: send data waiting in the output buffer.
+ *
+ * len is how much to try to send (typically equal to outCount, but may
+ * be less).
+ *
+ * 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.
*/
-int
-pqFlush(PGconn *conn)
+static int
+pqSendSome(PGconn *conn, int len)
{
char *ptr = conn->outBuffer;
- int len = conn->outCount;
+ int remaining = conn->outCount;
+ int result = 0;
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqFlush() -- connection not open\n");
- return EOF;
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("connection not open\n"));
+ return -1;
}
+ /* while there's still data to send */
while (len > 0)
{
- /* Prevent being SIGPIPEd if backend has closed the connection. */
-#ifndef WIN32
- pqsigfunc oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif
+ int sent;
+ char sebuf[256];
- int sent = send(conn->sock, ptr, len, 0);
-
-#ifndef WIN32
- pqsignal(SIGPIPE, oldsighandler);
-#endif
+ sent = pqsecure_write(conn, ptr, len);
if (sent < 0)
{
-
/*
- * Anything except EAGAIN or EWOULDBLOCK is trouble. If it's
- * EPIPE or ECONNRESET, assume we've lost the backend
+ * Anything except EAGAIN/EWOULDBLOCK/EINTR is trouble. If
+ * it's EPIPE or ECONNRESET, assume we've lost the backend
* connection permanently.
*/
- switch (errno)
+ switch (SOCK_ERRNO)
{
#ifdef EAGAIN
case EAGAIN:
case EWOULDBLOCK:
break;
#endif
+ case EINTR:
+ continue;
case EPIPE:
#ifdef ECONNRESET
case ECONNRESET:
#endif
- sprintf(conn->errorMessage,
- "pqFlush() -- backend closed the channel unexpectedly.\n"
- "\tThis probably means the backend terminated abnormally"
- " before or while processing the request.\n");
+ 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"));
+
/*
* 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.
+ * (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.
*/
- return EOF;
+ conn->outCount = 0;
+ return -1;
default:
- sprintf(conn->errorMessage,
- "pqFlush() -- couldn't send data: errno=%d\n%s\n",
- errno, strerror(errno));
+ 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... */
- return EOF;
+ conn->outCount = 0;
+ return -1;
}
}
else
{
ptr += sent;
len -= sent;
+ remaining -= sent;
}
+
if (len > 0)
{
- /* We didn't send it all, wait till we can send more */
- if (pqWait(FALSE, TRUE, conn))
- return EOF;
+ /*
+ * 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.
+ */
+ if (pqIsnonblocking(conn))
+ {
+ result = 1;
+ 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)
+ {
+ result = -1; /* error message already set up */
+ break;
+ }
+ if (pqWait(TRUE, TRUE, conn))
+ {
+ result = -1;
+ break;
+ }
}
}
- conn->outCount = 0;
+ /* shift the remaining contents of the buffer */
+ if (remaining > 0)
+ memmove(conn->outBuffer, ptr, remaining);
+ conn->outCount = remaining;
+
+ return result;
+}
+
+/*
+ * pqFlush: send any data waiting in the output buffer
+ *
+ * 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.
+ */
+int
+pqFlush(PGconn *conn)
+{
if (conn->Pfdebug)
fflush(conn->Pfdebug);
+ if (conn->outCount > 0)
+ return pqSendSome(conn, conn->outCount);
+
return 0;
}
-/* --------------------------------------------------------------------- */
-/* pqWait: wait until we can read or write the connection socket
+
+/*
+ * pqWait: wait until we can read or write the connection socket
+ *
+ * JAB: If SSL enabled and used and forRead, buffered bytes short-circuit the
+ * call to select().
+ *
+ * We also stop waiting and return if the kernel flags an exception condition
+ * on the socket. The actual error condition will be detected and reported
+ * when the caller tries to read or write the socket.
*/
int
pqWait(int forRead, int forWrite, PGconn *conn)
{
+ return pqWaitTimed(forRead, forWrite, conn, (time_t) -1);
+}
+
+/*
+ * 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.
+ */
+int
+pqWaitTimed(int forRead, int forWrite, PGconn *conn, time_t finish_time)
+{
+ int result;
+
+ result = pqSocketCheck(conn, forRead, forWrite, finish_time);
+
+ if (result < 0)
+ return EOF; /* errorMessage is already set */
+
+ if (result == 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("timeout expired\n"));
+ return EOF;
+ }
+
+ return 0;
+}
+
+/*
+ * pqReadReady: is select() saying the file is ready to read?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqReadReady(PGconn *conn)
+{
+ return pqSocketCheck(conn, 1, 0, (time_t) 0);
+}
+
+/*
+ * pqWriteReady: is select() saying the file is ready to write?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
+ */
+int
+pqWriteReady(PGconn *conn)
+{
+ return pqSocketCheck(conn, 0, 1, (time_t) 0);
+}
+
+/*
+ * Checks a socket, using poll or select, for data to be read, written,
+ * or both. Returns >0 if one or more conditions are met, 0 if it timed
+ * out, -1 if an error occurred.
+ *
+ * If SSL is in use, the SSL buffer is checked prior to checking the socket
+ * for read data directly.
+ */
+static int
+pqSocketCheck(PGconn *conn, int forRead, int forWrite, time_t end_time)
+{
+ int result;
+
+ if (!conn)
+ return -1;
+ if (conn->sock < 0)
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("socket not open\n"));
+ return -1;
+ }
+
+#ifdef USE_SSL
+ /* Check for SSL library buffering read bytes */
+ if (forRead && conn->ssl && SSL_pending(conn->ssl) > 0)
+ {
+ /* short-circuit the select */
+ return 1;
+ }
+#endif
+
+ /* 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[256];
+
+ printfPQExpBuffer(&conn->errorMessage,
+ libpq_gettext("select() failed: %s\n"),
+ SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+ }
+
+ return result;
+}
+
+
+/*
+ * Check a file descriptor for read and/or write data, possibly waiting.
+ * If neither forRead nor forWrite are set, immediately return a timeout
+ * condition (without waiting). Return >0 if condition is met, 0
+ * if a timeout occurred, -1 if an error or interrupt occurred.
+ *
+ * Timeout is infinite if end_time is -1. Timeout is immediate (no blocking)
+ * if end_time is 0 (or indeed, any time before now).
+ */
+static int
+pqSocketPoll(int sock, int forRead, int forWrite, time_t end_time)
+{
+ /* We use poll(2) if available, otherwise select(2) */
+#ifdef HAVE_POLL
+ struct pollfd input_fd;
+ int timeout_ms;
+
+ if (!forRead && !forWrite)
+ return 0;
+
+ input_fd.fd = sock;
+ input_fd.events = POLLERR;
+ input_fd.revents = 0;
+
+ if (forRead)
+ input_fd.events |= POLLIN;
+ if (forWrite)
+ input_fd.events |= POLLOUT;
+
+ /* Compute appropriate timeout interval */
+ if (end_time == ((time_t) -1))
+ timeout_ms = -1;
+ else
+ {
+ time_t now = time(NULL);
+
+ if (end_time > now)
+ timeout_ms = (end_time - now) * 1000;
+ else
+ timeout_ms = 0;
+ }
+
+ return poll(&input_fd, 1, timeout_ms);
+
+#else /* !HAVE_POLL */
+
fd_set input_mask;
fd_set output_mask;
+ fd_set except_mask;
+ struct timeval timeout;
+ struct timeval *ptr_timeout;
- if (conn->sock < 0)
+ if (!forRead && !forWrite)
+ return 0;
+
+ FD_ZERO(&input_mask);
+ FD_ZERO(&output_mask);
+ 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
{
- strcpy(conn->errorMessage, "pqWait() -- connection not open\n");
- return EOF;
+ time_t now = time(NULL);
+
+ if (end_time > now)
+ timeout.tv_sec = end_time - now;
+ else
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ ptr_timeout = &timeout;
}
- /* loop in case select returns EINTR */
- for (;;)
+ return select(sock + 1, &input_mask, &output_mask,
+ &except_mask, ptr_timeout);
+#endif /* HAVE_POLL */
+}
+
+
+/*
+ * A couple of "miscellaneous" multibyte related functions. They used
+ * to be in fe-print.c but that file is doomed.
+ */
+
+/*
+ * returns the byte length of the word beginning s, using the
+ * specified encoding.
+ */
+int
+PQmblen(const unsigned char *s, int encoding)
+{
+ return (pg_encoding_mblen(encoding, s));
+}
+
+/*
+ * returns the display length of the word beginning s, using the
+ * specified encoding.
+ */
+int
+PQdsplen(const unsigned char *s, int encoding)
+{
+ return (pg_encoding_dsplen(encoding, s));
+}
+
+/*
+ * Get encoding id from environment variable PGCLIENTENCODING.
+ */
+int
+PQenv2encoding(void)
+{
+ char *str;
+ int encoding = PG_SQL_ASCII;
+
+ str = getenv("PGCLIENTENCODING");
+ if (str && *str != '\0')
+ encoding = pg_char_to_encoding(str);
+ return (encoding);
+}
+
+
+#ifdef ENABLE_NLS
+
+char *
+libpq_gettext(const char *msgid)
+{
+ static int already_bound = 0;
+
+ if (!already_bound)
{
- FD_ZERO(&input_mask);
- FD_ZERO(&output_mask);
- if (forRead)
- FD_SET(conn->sock, &input_mask);
- if (forWrite)
- FD_SET(conn->sock, &output_mask);
- if (select(conn->sock + 1, &input_mask, &output_mask, (fd_set *) NULL,
- (struct timeval *) NULL) < 0)
- {
- if (errno == EINTR)
- continue;
- sprintf(conn->errorMessage,
- "pqWait() -- select() failed: errno=%d\n%s\n",
- errno, strerror(errno));
- return EOF;
- }
- /* On nonerror return, assume we're done */
- break;
+ already_bound = 1;
+ /* No relocatable lookup here because the binary could be anywhere */
+ bindtextdomain("libpq", getenv("PGLOCALEDIR") ? getenv("PGLOCALEDIR") : LOCALEDIR);
}
- return 0;
+ return dgettext("libpq", msgid);
}
+
+#endif /* ENABLE_NLS */