* for transmitted ints, whereas the backend modules (as of this writing)
* still handle either network or little-endian byte order.
*
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2001, 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.23 1999/02/22 05:26:53 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.43 2001/01/24 19:43:30 momjian Exp $
*
*-------------------------------------------------------------------------
*/
-#include "libpq-fe.h"
-#include "libpq-int.h"
#include "postgres.h"
-#include "pqsignal.h"
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <signal.h>
-#include <time.h>
#ifdef WIN32
#include "win32.h"
#else
-#include <sys/time.h>
-#if !defined(NO_UNISTD_H)
#include <unistd.h>
+#include <sys/time.h>
#endif
-#endif /* WIN32 */
+
+#include <errno.h>
+#include <signal.h>
+#include <time.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
+#include "libpq-fe.h"
+#include "libpq-int.h"
+#include "pqsignal.h"
+
+#ifdef MULTIBYTE
+#include "miscadmin.h"
+#include "mb/pg_wchar.h"
+#endif
+
#define DONOTICE(conn,message) \
((*(conn)->noticeHook) ((conn)->noticeArg, (message)))
with buffering
*/
static int
-pqPutBytes(const char *s, int nbytes, PGconn *conn)
+pqPutBytes(const char *s, size_t nbytes, PGconn *conn)
{
- int avail = conn->outBufSize - conn->outCount;
+ size_t avail = Max(conn->outBufSize - conn->outCount, 0);
+
+ /*
+ * if we are non-blocking and the send queue is too full to buffer
+ * this request then try to flush some and return an error
+ */
+ if (pqIsnonblocking(conn) && nbytes > avail && pqFlush(conn))
+ {
+
+ /*
+ * even if the flush failed we may still have written some data,
+ * recalculate the size of the send-queue relative to the amount
+ * we have to send, we may be able to queue it afterall even
+ * though it's not sent to the database it's ok, any routines that
+ * check the data coming from the database better call pqFlush()
+ * anyway.
+ */
+ if (nbytes > Max(conn->outBufSize - conn->outCount, 0))
+ {
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqPutBytes -- pqFlush couldn't flush enough"
+ " data: space available: %d, space needed %d\n",
+ Max(conn->outBufSize - conn->outCount, 0), nbytes);
+ return EOF;
+ }
+ /* fixup avail for while loop */
+ avail = Max(conn->outBufSize - conn->outCount, 0);
+ }
+ /*
+ * is the amount of data to be sent is larger than the size of the
+ * output buffer then we must flush it to make more room.
+ *
+ * the code above will make sure the loop conditional is never true for
+ * non-blocking connections
+ */
while (nbytes > avail)
{
memcpy(conn->outBuffer + conn->outCount, s, avail);
/* --------------------------------------------------------------------- */
/* 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,
+ 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;
}
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)
return EOF;
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;
}
send a string of exactly len bytes, no null termination needed
*/
int
-pqPutnchar(const char *s, int len, PGconn *conn)
+pqPutnchar(const char *s, size_t len, PGconn *conn)
{
if (pqPutBytes(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;
}
to local byte order
*/
int
-pqGetInt(int *result, int bytes, PGconn *conn)
+pqGetInt(int *result, size_t bytes, PGconn *conn)
{
uint16 tmp2;
uint32 tmp4;
+ char noticeBuf[64];
switch (bytes)
{
*result = (int) ntohl(tmp4);
break;
default:
- sprintf(conn->errorMessage,
- "pqGetInt: int size %d not supported\n", bytes);
- DONOTICE(conn, conn->errorMessage);
+ sprintf(noticeBuf,
+ "pqGetInt: int size %lu not supported\n", (unsigned long)bytes);
+ DONOTICE(conn, noticeBuf);
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;
}
to network byte order.
*/
int
-pqPutInt(int value, int bytes, PGconn *conn)
+pqPutInt(int value, size_t bytes, PGconn *conn)
{
uint16 tmp2;
uint32 tmp4;
+ char noticeBuf[64];
switch (bytes)
{
return EOF;
break;
default:
- sprintf(conn->errorMessage,
- "pqPutInt: int size %d not supported\n", bytes);
- DONOTICE(conn, conn->errorMessage);
+ sprintf(noticeBuf,
+ "pqPutInt: int size %lu not supported\n", (unsigned long)bytes);
+ DONOTICE(conn, noticeBuf);
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?
+ * Returns -1 on failure, 0 if not ready, 1 if ready.
*/
-static int
+int
pqReadReady(PGconn *conn)
{
fd_set input_mask;
struct timeval timeout;
- if (conn->sock < 0)
- return 0;
+ if (!conn || conn->sock < 0)
+ return -1;
FD_ZERO(&input_mask);
FD_SET(conn->sock, &input_mask);
timeout.tv_sec = 0;
timeout.tv_usec = 0;
+retry:
if (select(conn->sock + 1, &input_mask, (fd_set *) NULL, (fd_set *) NULL,
&timeout) < 0)
{
- sprintf(conn->errorMessage,
- "pqReadReady() -- select() failed: errno=%d\n%s\n",
- errno, strerror(errno));
- return 0;
+ if (errno == EINTR)
+ /* Interrupted system call - we'll just try again */
+ goto retry;
+
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqReadReady() -- select() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return FD_ISSET(conn->sock, &input_mask) ? 1 : 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)
+{
+ fd_set input_mask;
+ struct timeval timeout;
+
+ if (!conn || conn->sock < 0)
+ return -1;
+
+ FD_ZERO(&input_mask);
+ FD_SET(conn->sock, &input_mask);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+retry:
+ if (select(conn->sock + 1, (fd_set *) NULL, &input_mask, (fd_set *) NULL,
+ &timeout) < 0)
+ {
+ if (errno == EINTR)
+ /* Interrupted system call - we'll just try again */
+ goto retry;
+
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqWriteReady() -- select() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
+ return -1;
}
- return FD_ISSET(conn->sock, &input_mask);
+ return FD_ISSET(conn->sock, &input_mask) ? 1 : 0;
}
/* --------------------------------------------------------------------- */
int
pqReadData(PGconn *conn)
{
+ int someread = 0;
int nread;
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqReadData() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqReadData() -- connection not open\n");
return -1;
}
* 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);
/* OK, try to read some data */
tryAgain:
- nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
- conn->inBufSize - conn->inEnd, 0);
+#ifdef USE_SSL
+ if (conn->ssl)
+ nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd);
+ else
+#endif
+ nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd, 0);
if (nread < 0)
{
if (errno == EINTR)
/* Some systems return EAGAIN/EWOULDBLOCK for no data */
#ifdef EAGAIN
if (errno == EAGAIN)
- return 0;
+ return someread;
#endif
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
if (errno == EWOULDBLOCK)
- return 0;
+ return someread;
#endif
/* We might get ECONNRESET here if using TCP and backend died */
#ifdef ECONNRESET
if (errno == ECONNRESET)
goto definitelyFailed;
#endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqReadData() -- read() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
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 tryAgain;
+ }
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
* be taken much, since in normal practice we should not be trying to
* read data unless the file selected for reading already.
*/
- if (!pqReadReady(conn))
- return 0; /* definitely no data available */
+ 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);
+#ifdef USE_SSL
+ if (conn->ssl)
+ nread = SSL_read(conn->ssl, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd);
+ else
+#endif
+ nread = recv(conn->sock, conn->inBuffer + conn->inEnd,
+ conn->inBufSize - conn->inEnd, 0);
if (nread < 0)
{
if (errno == EINTR)
if (errno == ECONNRESET)
goto definitelyFailed;
#endif
- sprintf(conn->errorMessage,
- "pqReadData() -- read() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqReadData() -- read() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
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,
+ "pqReadData() -- backend closed the channel unexpectedly.\n"
+ "\tThis probably means the backend terminated abnormally\n"
+ "\tbefore or while processing the request.\n");
conn->status = CONNECTION_BAD; /* No more connection to backend */
#ifdef WIN32
closesocket(conn->sock);
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqFlush() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqFlush() -- connection not open\n");
return EOF;
}
+ /*
+ * don't try to send zero data, allows us to use this function without
+ * too much worry about overhead
+ */
+ if (len == 0)
+ return (0);
+
+ /* 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 = send(conn->sock, ptr, len, 0);
+ int sent;
+
+#ifdef USE_SSL
+ if (conn->ssl)
+ sent = SSL_write(conn->ssl, ptr, len);
+ else
+#endif
+ sent = send(conn->sock, ptr, len, 0);
#ifndef WIN32
pqsignal(SIGPIPE, oldsighandler);
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 or EWOULDBLOCK is trouble. If it's
+ * EPIPE or ECONNRESET, assume we've lost the backend
+ * connection permanently.
*/
switch (errno)
{
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");
- conn->status = CONNECTION_BAD; /* No more connection */
-#ifdef WIN32
- closesocket(conn->sock);
-#else
- close(conn->sock);
-#endif
- conn->sock = -1;
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqFlush() -- backend closed the channel unexpectedly.\n"
+ "\tThis probably means the backend terminated abnormally"
+ " before 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.
+ */
return EOF;
+
default:
- sprintf(conn->errorMessage,
- "pqFlush() -- couldn't send data: errno=%d\n%s\n",
- errno, strerror(errno));
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqFlush() -- couldn't send data: errno=%d\n%s\n",
+ errno, strerror(errno));
/* We don't assume it's a fatal error... */
return EOF;
}
ptr += sent;
len -= sent;
}
+
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
+ */
+#ifdef USE_SSL
+ /* can't do anything for our SSL users yet */
+ if (conn->ssl == NULL)
+ {
+#endif
+ if (pqIsnonblocking(conn))
+ {
+ /* shift the contents of the buffer */
+ memmove(conn->outBuffer, ptr, len);
+ conn->outCount = len;
+ return EOF;
+ }
+#ifdef USE_SSL
+ }
+#endif
+
if (pqWait(FALSE, TRUE, conn))
return EOF;
}
if (conn->sock < 0)
{
- strcpy(conn->errorMessage, "pqWait() -- connection not open\n");
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqWait() -- connection not open\n");
return EOF;
}
- /* loop in case select returns EINTR */
- for (;;)
+ if (forRead || forWrite)
{
+retry:
FD_ZERO(&input_mask);
FD_ZERO(&output_mask);
if (forRead)
(struct timeval *) NULL) < 0)
{
if (errno == EINTR)
- continue;
- sprintf(conn->errorMessage,
- "pqWait() -- select() failed: errno=%d\n%s\n",
- errno, strerror(errno));
+ goto retry;
+ printfPQExpBuffer(&conn->errorMessage,
+ "pqWait() -- select() failed: errno=%d\n%s\n",
+ errno, strerror(errno));
return EOF;
}
- /* On nonerror return, assume we're done */
- break;
}
return 0;
}
+
+
+
+/*
+ * A couple of "miscellaneous" multibyte related functions. They used
+ * to be in fe-print.c but that file is doomed.
+ */
+
+#ifdef MULTIBYTE
+/*
+ * 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));
+}
+
+/*
+ * Get encoding id from environment variable PGCLIENTENCODING.
+ */
+int
+PQenv2encoding(void)
+{
+ char *str;
+ int encoding = SQL_ASCII;
+
+ str = getenv("PGCLIENTENCODING");
+ if (str && *str != '\0')
+ encoding = pg_char_to_encoding(str);
+ return (encoding);
+}
+
+#else
+
+/* Provide a default definition in case someone calls it anyway */
+int
+PQmblen(const unsigned char *s, int encoding)
+{
+ (void) s;
+ (void) encoding;
+ return 1;
+}
+int
+PQenv2encoding(void)
+{
+ return 0;
+}
+
+#endif /* MULTIBYTE */