OSDN Git Service

Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[pg-rex/syncrep.git] / src / interfaces / libpq / fe-misc.c
index 7262792..cf38678 100644 (file)
  * 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)))
@@ -85,10 +89,44 @@ pqGetc(char *result, PGconn *conn)
    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);
@@ -109,12 +147,12 @@ pqPutBytes(const char *s, int nbytes, PGconn *conn)
 /* --------------------------------------------------------------------- */
 /* 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;
@@ -129,18 +167,15 @@ pqGets(char *s, int maxlen, PGconn *conn)
                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;
 }
@@ -163,7 +198,7 @@ pqPuts(const char *s, PGconn *conn)
    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;
@@ -174,7 +209,7 @@ pqGetnchar(char *s, int len, PGconn *conn)
        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;
 }
@@ -184,13 +219,13 @@ pqGetnchar(char *s, int len, PGconn *conn)
    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;
 }
@@ -201,10 +236,11 @@ pqPutnchar(const char *s, int len, PGconn *conn)
    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)
        {
@@ -223,14 +259,14 @@ pqGetInt(int *result, int bytes, PGconn *conn)
                        *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;
 }
@@ -241,10 +277,11 @@ pqGetInt(int *result, int bytes, PGconn *conn)
    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)
        {
@@ -259,43 +296,83 @@ pqPutInt(int value, int bytes, PGconn *conn)
                                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;
 }
 
 /* --------------------------------------------------------------------- */
@@ -311,11 +388,13 @@ pqReadReady(PGconn *conn)
 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;
        }
 
@@ -336,9 +415,10 @@ pqReadData(PGconn *conn)
         * 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);
@@ -352,8 +432,14 @@ pqReadData(PGconn *conn)
 
        /* 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)
@@ -361,28 +447,51 @@ tryAgain:
                /* 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
@@ -392,16 +501,31 @@ tryAgain:
         * 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)
@@ -420,9 +544,9 @@ tryAgain2:
                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)
@@ -436,10 +560,10 @@ tryAgain2:
         * 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);
@@ -462,18 +586,35 @@ pqFlush(PGconn *conn)
 
        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);
@@ -481,10 +622,11 @@ pqFlush(PGconn *conn)
 
                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)
                        {
@@ -496,26 +638,32 @@ pqFlush(PGconn *conn)
                                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;
                        }
@@ -525,9 +673,31 @@ pqFlush(PGconn *conn)
                        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;
                }
@@ -552,13 +722,14 @@ pqWait(int forRead, int forWrite, PGconn *conn)
 
        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)
@@ -569,15 +740,64 @@ pqWait(int forRead, int forWrite, PGconn *conn)
                                   (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 */